在配对交易中,除了检查协整性外,通常还需要检查数据的平稳性、相关性、价差的分布特征等
- 平稳性
- 原因:平稳性是时间序列数据的一个重要特征。如果数据不平稳,可能会出现趋势、季节性等复杂模式,导致统计分析结果出现偏差,影响配对交易策略的有效性和可靠性。例如,非平稳数据可能会使均值、方差等统计量随时间变化,使得基于历史数据计算出的交易信号在未来不再适用。此外,许多统计检验和模型估计方法都要求数据具有平稳性,如常用的 OLS 回归等,如果数据不平稳,可能会产生虚假回归等问题。
- 相关性
- 原因:配对交易通常是基于两只或多只资产价格之间存在某种稳定的相关关系来构建的。通过计算相关系数等指标,可以衡量资产之间价格变动的线性关系程度。如果资产之间的相关性较低或不稳定,那么它们之间的价格变动可能无法形成有效的配对关系,配对交易策略就难以发挥作用。较高的相关性意味着当一只股票价格上涨或下跌时,另一只股票有较大概率也会朝着相同方向变动,从而为通过价差交易获取利润提供了可能。
- 价差的分布特征
- 原因:分析价差的分布特征,如均值、标准差、偏态、峰度等,有助于确定交易的触发条件和止损止盈水平。如果价差呈现出明显的非正态分布,例如具有厚尾特征,那么极端价差出现的概率可能比正态分布假设下更高,需要更谨慎地设置风险控制参数。了解价差的分布范围和波动情况,可以帮助投资者判断何时价差处于异常水平,从而发出买入或卖出信号。
一般来说,在配对交易的数据检查中,协整性、平稳性、相关性是较为核心的检查项目,加上价差分布特征等,至少要检查四项。当然,实际应用中还可能会结合其他因素进行综合分析,如市场流动性、交易成本、资产的基本面信息等,以更全面地评估配对交易策略的可行性和风险。
价差的分布特征 为什么要检查,可以从反面的角度思考下,不检查出现的问题及严重性。
检查价差分布特征在配对交易中至关重要,以下从反面角度阐述不检查价差分布特征可能出现的问题及严重性,并结合例子和绘图进行说明:
问题及严重性
- 无法准确设定交易阈值:如果不了解价差的分布范围和波动情况,就难以确定合理的买卖信号触发阈值。可能会过早或过晚进行交易,导致错过盈利机会或承受不必要的损失。
- 风险控制失效:价差的标准差、偏态和峰度等特征能反映其波动的剧烈程度和极端情况出现的概率。不检查这些特征,就无法准确评估交易风险,可能会使投资者面临超出预期的损失。在极端情况下,可能导致资金大幅缩水甚至策略失败。
- 策略适应性差:市场环境是不断变化的,价差分布也可能随之改变。不关注价差分布特征的变化,投资者就无法及时调整策略以适应新的市场条件,使策略的有效性大打折扣。
案例
假设投资者基于两只股票 A 和 B 进行配对交易,未对价差分布特征进行检查。历史数据显示,大部分时间两者价差在 - 5 元到 5 元之间波动,但偶尔会出现极端情况。
如果投资者仅根据经验设定当价差达到 6 元时进行买入操作,认为价差会回归均值。然而,实际上价差的分布存在厚尾特征,即出现极端值的概率比正态分布假设下要高。在某一时期,由于市场突发重大利空消息,两只股票的价差迅速扩大到 10 元,且持续了较长时间。由于投资者没有考虑到价差分布的这种极端情况,没有及时设置合理的止损,导致损失不断扩大。
数据窗口多长合适?20天?60天?122天?250天?
在配对交易中,数据窗口长度的选择没有绝对标准,20 天、60 天、122 天、250 天等各有特点和适用场景,以下是具体分析:
- 20 天
- 优点:属于短期数据窗口,能快速反映近期市场变化和资产价格的短期波动特征,对短期趋势和短期价差变化较为敏感,适用于短期配对交易策略,能及时捕捉短期的价格异常和交易机会。
- 缺点:数据量相对较少,可能无法充分反映资产价格的长期趋势和周期性特征,容易受到短期噪声和异常值的影响,导致统计结果不稳定,交易信号可能出现较多误判。
- 适用场景:市场波动较大、交易策略侧重于捕捉短期快速波动机会的情况,比如在一些短期投机氛围浓厚的市场环境中,或者针对一些价格波动频繁的小盘股进行配对交易时,20 天的数据窗口可能较为合适。
- 60 天
- 优点:能在一定程度上平衡短期和中期的市场信息,既可以反映近期价格变化趋势,又能涵盖一定的中期波动特征,有助于识别资产价格的中期趋势和价差的中期变化规律,统计结果相对 20 天的窗口更为稳定,能过滤掉一部分短期噪声。
- 缺点:对于一些长期趋势明显、周期性较强的资产,60 天可能无法完全体现其长期特征,在市场趋势发生较大转变时,可能反应不够及时。
- 适用场景:适用于大多数市场环境下的中短期配对交易策略,对于那些价格波动相对适中、具有一定中期趋势的资产,如一些大盘蓝筹股的配对交易,60 天的数据窗口是一个比较常用的选择。
- 122 天
- 优点:接近一个季度的时间长度,能较好地反映资产价格的季度性变化和中期趋势,数据量相对充足,统计结果更具可靠性和稳定性,能够捕捉到资产价格和价差在一个相对较长时期内的平均水平和波动特征,对于分析季节性因素或季度性业绩等对资产价格的影响有一定帮助。
- 缺点:对于短期市场变化的反应相对滞后,可能错过一些短期的交易机会,而且如果市场环境变化较快,122 天的数据可能包含一些过时的信息,影响交易策略的时效性。
- 适用场景:适合于分析具有季度性特征的行业或资产,如一些受季节因素影响较大的农产品、能源等行业的股票配对交易,或者用于一些基于季度财务数据等基本面因素的配对交易策略。
- 250 天
- 优点:约等于一年的交易日数量,属于长期数据窗口,能全面反映资产价格的长期趋势、周期性特征和长期平均水平,能有效过滤掉短期市场波动的干扰,为配对交易策略提供更稳定、更具代表性的统计基础,对于识别长期的均值回归水平和长期价差波动范围非常有帮助。
- 缺点:数据的时效性较差,可能无法及时反映市场最新的变化和趋势,而且长期数据中可能包含较多不同市场环境和经济周期的信息,当市场环境发生结构性变化时,历史数据的参考价值可能会降低。
- 适用场景:适用于长期投资风格的配对交易策略,尤其是对于那些具有稳定经营业绩、长期价格趋势明显的大型蓝筹股或行业龙头股的配对交易,或者用于对市场长期趋势和宏观经济周期有较强依赖的资产配对交易。
给个简单结论
简单来说,数据窗口的选择需依具体情况而定:
- 20 天适合短期投机、捕捉快速波动机会及小盘股配对交易。
- 60 天适用于多数市场环境下的中短期策略及大盘蓝筹股配对交易。
- 122 天适合分析有季度性特征的行业或资产的配对交易。
- 250 天适用于长期投资风格、稳定业绩的大型股或受宏观经济周期影响大的资产配对交易。
python 具体代码
代码说明:
- 数据读取:假设数据存储在 CSV 文件中,第一列是日期,第二列和第三列分别是两只股票的收盘价。
- 平稳性检查:使用 ADF 检验判断每只股票价格序列是否平稳。
- 相关性检查:计算两只股票的相关系数,并绘制相关性热力图。
- 协整性检查:使用
coint
函数进行协整性检验。 - 价差分布特征检查:计算价差序列,并绘制其直方图,同时拟合正态分布曲线。
- 归一化后的曲线图:对两只股票的价格进行归一化处理,并绘制曲线图。
- 确认买卖的阈值:以价差的均值加减一定倍数的标准差作为买卖阈值。
请将'your_stock_data.csv'
替换为实际的 CSV 文件路径。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from statsmodels.tsa.stattools import adfuller, coint
from scipy.stats import norm
# 假设已经有两个 A 股的收盘数据,存储在 CSV 文件中,第一列是日期,第二列和第三列分别是两只股票的收盘价
# 读取数据
data = pd.read_csv('your_stock_data.csv', index_col=0, parse_dates=True)
stock1 = data.iloc[:, 0]
stock2 = data.iloc[:, 1]
# 1. 平稳性检查(使用 ADF 检验)
def adf_test(series):
result = adfuller(series)
print('ADF Statistic: {}'.format(result[0]))
print('p-value: {}'.format(result[1]))
print('Critical Values:')
for key, value in result[4].items():
print('\t{}: {}'.format(key, value))
if result[1] <= 0.05:
print("The series is stationary.")
else:
print("The series is non-stationary.")
print("Stock 1 ADF test:")
adf_test(stock1)
print("\nStock 2 ADF test:")
adf_test(stock2)
# 2. 相关性检查
correlation = stock1.corr(stock2)
print(f"Correlation between stock 1 and stock 2: {correlation}")
# 生成相关性热力图
corr_matrix = pd.DataFrame({'Stock 1': stock1, 'Stock 2': stock2}).corr()
plt.figure(figsize=(8, 6))
sns.heatmap(corr_matrix, annot=True, cmap='coolwarm')
plt.title('Correlation Heatmap')
plt.show()
# 3. 协整性检查
coint_result = coint(stock1, stock2)
print('Cointegration test p-value: {}'.format(coint_result[1]))
if coint_result[1] <= 0.05:
print("The two stocks are cointegrated.")
else:
print("The two stocks are not cointegrated.")
# 4. 价差计算及分布特征检查
spread = stock1 - stock2
spread_mean = spread.mean()
spread_std = spread.std()
print(f"Spread mean: {spread_mean}")
print(f"Spread standard deviation: {spread_std}")
# 绘制价差的直方图并拟合正态分布
plt.figure(figsize=(10, 6))
sns.histplot(spread, kde=False, stat='density', bins=30)
xmin, xmax = plt.xlim()
x = np.linspace(xmin, xmax, 100)
p = norm.pdf(x, spread_mean, spread_std)
plt.plot(x, p, 'k', linewidth=2)
plt.title('Spread Distribution')
plt.xlabel('Spread')
plt.ylabel('Density')
plt.show()
# 5. 归一化后的曲线图
normalized_stock1 = (stock1 - stock1.min()) / (stock1.max() - stock1.min())
normalized_stock2 = (stock2 - stock2.min()) / (stock2.max() - stock2.min())
plt.figure(figsize=(12, 6))
plt.plot(normalized_stock1, label='Stock 1 (Normalized)')
plt.plot(normalized_stock2, label='Stock 2 (Normalized)')
plt.title('Normalized Stock Prices')
plt.xlabel('Date')
plt.ylabel('Normalized Price')
plt.legend()
plt.show()
# 6. 确认买卖的阈值
# 这里简单地以均值加减一定倍数的标准差作为买卖阈值
threshold = 1.5 # 可以根据实际情况调整
upper_threshold = spread_mean + threshold * spread_std
lower_threshold = spread_mean - threshold * spread_std
print(f"Upper threshold for buying/selling: {upper_threshold}")
print(f"Lower threshold for buying/selling: {lower_threshold}")