1. 这节究竟解决什么实际问题?¶
3.5 协整性与相关性 的具体任务是:区分相关与协整,避免把同步波动误作稳定价差关系。 本节将明确输入数据、计算过程、输出及其在后续研究中的用途。
2. 前置知识¶
理解收益率、滚动窗口和基础 DataFrame 操作。 对公式的理解应包括变量定义、单位、时间索引和适用条件。
3. 数据说明¶
使用固定日频价格样本;高低开收和成交量如为教学代理,会在代码中明确说明。 运行前先检查 shape、日期范围、列名与缺失值。数据看起来能画图,不代表它适合回答研究问题。
4. 学习目标与完成标准¶
完成本节后,你应能用自己的话解释 3.5 协整性与相关性 的输入、计算步骤、输出和局限;尤其能说明:区分相关与协整,避免把同步波动误作稳定价差关系。 你还应能独立改动一个参数并解释结果为什么变化,指出至少一个会让结论失真的假设。
5. 核心概念与公式¶
技术指标将历史价格或成交量压缩为统计量。指标本身不包含交易方向、执行时点和风险约束。
滚动指标只使用窗口结束日及以前的数据;交易仓位必须至少滞后一个可交易周期。 看到公式时依次确认:每个符号的单位是什么?它属于哪个时点?分母会不会为零?它是描述性统计量、预测量,还是交易规则?
6. 从零实现¶
原始代码按照“准备输入 → 计算中间量 → 得到结果 → 解释输出”的顺序组织。每个单元应检查变量类型、索引、维度和代表性数值。中间变量用于核对公式实现和定位计算错误。
7. 成熟库实现¶
公共指标函数减少重复代码;首次学习仍应读懂每个滚动、平滑和标准化步骤。 使用库函数前先阅读参数含义、默认值和返回对象;库输出一个数字,并不说明这个数字适合你的交易假设。
8. 两种实现的差异¶
从零实现便于核对公式、调试和识别隐含假设;成熟库适合在计算逻辑已验证后复用。两种实现依赖相同的数据口径与时点假设,使用库函数不会改变输入数据的质量。
9. 预期结果与检查范围¶
你应看到维度正确的 Series/DataFrame、日期单调递增的索引,以及能支持“区分相关与协整,避免把同步波动误作稳定价差关系。”这一任务的表格或图形。金融日收益率均值通常接近 0;若出现极大的连续收益、空表、全 NaN 或索引错位,应先排查数据和对齐逻辑,而不是立即相信结果。
10. 常见错误与诊断¶
窗口不足时误读 NaN、同日用收盘信号按收盘成交、忽略除零、把指标阈值当普适规律。
诊断顺序:先打印类型与 shape,再看 head()/tail(),接着检查日期和 NaN,最后才比较数值。这样能避免在错误的对象上反复改参数。
11. 研究偏差检查¶
测试大量窗口后只保留样本内表现最高的参数会产生数据窥探偏差;任何信号都应和简单基线比较。 结果至少应与一个简单基线比较,并明确哪些部分只是教学演示。
12. 三层练习与答案¶
- 基础练习:验证一个中间变量、补全一段小计算,并用一句话解释输出。
- 研究练习:改变窗口、样本区间、约束或标的,记录哪些结论仍成立。
- 开放项目:把本节方法嵌入模块项目,报告基线、失败结果和限制。
题目在 ../exercises/03_indicators/05_cointegration_correlation_exercises.ipynb;完整推导、可运行参考实现与结果检查在 ../solutions/03_indicators/05_cointegration_correlation_solutions.ipynb。请先独立作答,再打开答案。
13. 延伸阅读¶
延伸材料:Technical Analysis of the Financial Markets(Murphy)。阅读记录应包括研究假设、数据频率、样本范围和结论适用条件。
14. 下一节¶
完成本模块后,进入下一模块的概览页,并把本节的检查清单带到后续研究中。
import numpy as np
import pandas as pd
import statsmodels.api as sm
import statsmodels.tsa.stattools as ts
import matplotlib.pyplot as plt
# --- 模拟两只高度相关的股票,但它们不协整 (分别带有各自的漂移趋势) ---
np.random.seed(42)
returns_base = np.random.normal(0, 1, 500)
price_A = np.cumsum(returns_base + 0.1) # 漂移项较小
price_B = np.cumsum(returns_base + 0.25) # 漂移项更大
plt.figure(figsize=(12, 5))
plt.plot(price_A, label='Stock A (Drift = 0.1)')
plt.plot(price_B, label='Stock B (Drift = 0.25)')
plt.title('High Correlation, No Cointegration (Spurious correlation)')
plt.legend()
plt.show()
# 它们的相关性非常高
pca_corr = np.corrcoef(np.diff(price_A), np.diff(price_B))[0, 1]
print(f'日收益率相关性: {pca_corr:.4f} (极高相关性)')
# 协整检验
_, pval_coint, _ = ts.coint(price_A, price_B)
print(f'协整检验 p-value: {pval_coint:.4f} (大于0.05,拒绝协整)')
日收益率相关性: 1.0000 (极高相关性) 协整检验 p-value: 0.0771 (大于0.05,拒绝协整)
import matplotlib.pyplot as plt
# 1. 设置系统自带的中文字体(这里使用黑体 SimHei)
plt.rcParams['font.sans-serif'] = ['SimHei'] # 如果你想用微软雅黑,可以改成 ['Microsoft YaHei']
# 2. 解决更换字体后,负号(-)显示为方块的问题
plt.rcParams['axes.unicode_minus'] = False
4. 统计套利的基础¶
找到了协整关系,我们就构建了统计套利 (Statistical Arbitrage) 中最经典的配对交易 (Pairs Trading) 的基础:
- 计算差价序列。
- 将差价标准化为Z-Score。
- 当 Z-Score > 2 时,做空差价组合(做空 Y,做多 $\beta$ 份 X)。
- 当 Z-Score < -2 时,做多差价组合(做多 Y,做空 $\beta$ 份 X)。
- 当差价回归均值 (Z-Score=0) 时平仓获利。
下一章将实现配对交易示例,并加入信号滞后、交易成本和动态对冲讨论。
5. 示例:在股票池中筛选协整资产对¶
现实中,量化研究员通常会圈定一个行业的股票池(比如银行股、航空股),然后在这个池子里两两计算 Engle-Granger 协整检验。找出 P-value 小于 0.05 甚至 0.01 的配对。
相关矩阵可以用**热力图(Heatmap)**展示,以便比较资产对之间的相关系数。
import seaborn as sns
import numpy as np
import pandas as pd
import statsmodels.tsa.stattools as ts
import matplotlib.pyplot as plt
# --- 模拟构建一个包含 5 只股票的股票池 ---
np.random.seed(10)
returns_pool = np.random.normal(0, 1, (500, 5))
base_prices = np.cumsum(returns_pool, axis=0) + 100
pool_df = pd.DataFrame(base_prices, columns=['Stock_A', 'Stock_B', 'Stock_C', 'Stock_D', 'Stock_E'])
# 特意制造一对协整关系 (让 Stock_E 成为 Stock_B 的协整影子)
pool_df['Stock_E'] = pool_df['Stock_B'] * 1.5 - 20 + np.random.normal(0, 2, 500)
# 计算协整 P-value 矩阵
def find_cointegrated_pairs(dataframe):
n = dataframe.shape[1]
pvalue_matrix = np.ones((n, n))
keys = dataframe.columns
pairs = []
for i in range(n):
for j in range(i+1, n):
S1 = dataframe[keys[i]]
S2 = dataframe[keys[j]]
result = ts.coint(S1, S2)
pvalue = result[1]
pvalue_matrix[i, j] = pvalue
if pvalue < 0.05:
pairs.append((keys[i], keys[j], pvalue))
return pvalue_matrix, pairs
pvalues, cointegrated_pairs = find_cointegrated_pairs(pool_df)
print("发现的协整配对:")
for pair in cointegrated_pairs:
print(f"{pair[0]} 和 {pair[1]} - P-value: {pair[2]:.4f}")
# 绘制热力图 P-value Heatmap
plt.figure(figsize=(8, 6))
sns.heatmap(pvalues, xticklabels=pool_df.columns, yticklabels=pool_df.columns, cmap='viridis_r',
mask=(pvalues >= 0.99), annot=True)
plt.title('Cointegration P-values Heatmap')
plt.show()
发现的协整配对: Stock_B 和 Stock_E - P-value: 0.0146 Stock_C 和 Stock_D - P-value: 0.0483 Stock_C 和 Stock_E - P-value: 0.0383
练习¶
- 对 A 股银行板块(例如工商银行、建设银行、农业银行)的股票做协整检验,找出配对。
- 画出任意两只高相关股票 5 年内的价格走势图,观察它们的价差是否保持平稳或者最终发散。
- 修改
find_cointegrated_pairs函数,加入参数让用户自定义置信水平(0.05 或 0.01)。
下一节 → ../04_backtesting/06_pairs_trading_strategy.ipynb