通过协整股票实现统计套利(第一部分):恩格尔 - 格兰杰检验与约翰森协整检验·进阶篇
📘

通过协整股票实现统计套利(第一部分):恩格尔 - 格兰杰检验与约翰森协整检验·进阶篇

第 2/2 篇

◍ 用协整替换相关性来搭统计配对

上一轮我们只用黄金兑美元和黄金兑欧元两个报价做均值偏离,把价差拉大当入场、回归当出场,本质上是一条极简统计套利线。这种组合太窄,触发信号也只靠相关性这一种关系,碰到汇率短期同涨同跌但长期各走各的,就会被相关性骗进去。 相关系数量的是两段价格同步同向的程度,局限在两两资产;协整检验盯的是长期等价关系,而且不限制资产数量,能一次性把多个备选组合丢进去测。亚历山大(2001)那句老话很直白:相关性抓短期,协整抓长期均衡,两者高相关不一定协整,协整也不一定高相关。 对外汇和贵金属交易者来说,这套东西在股市的实用度明显高于汇市,但拿来扩配对池仍然有用。MT5 自带 Python 集成,直接调 statsmodels 里的现成函数就能跑,不需要自己推数学。开 MT5 接上 Python 环境,先把主要外汇和金银交叉盘丢进协整筛选,比单纯按相关性排名挑配对更抗假信号。 下一步落点是恩格尔-格兰杰和约翰森两种检验怎么在不同资产类里用、结果怎么读,那是把筛选真正变成可下单信号的关键一步。

用 EG 检验筛出能配对的外汇组合

恩格尔‑格兰杰协整检验回答的是两资产有没有长期均衡关系,对交易者而言就是价差能不能均值回归。它不像相关检验只给方向,而是吐出一个 p 值:p 越小协整越强。业内常把 0.05 当中等信号线、0.01 当强信号线,但阈值得按你的品种和周期自己调。 我们拉了七大外汇直盘各 600 个交易日(约 2.4 年,按一年 250 交易日折算)的日线收盘,跑全配对 EG 检验。结果里绝大多数 p 值高于 0.3,说明直盘间长期均衡很弱;唯独 USDCAD 与 NZDUSD 的 p=0.0052,GBPUSD 与 USDCHF 的 p=0.0238 算勉强入眼。 USDCAD/NZDUSD 的价差用对冲比率 -1.54 构造:价差 = USDCAD + 1.54×NZDUSD。这是线性回归估出的平稳化比例,让两者波动尺度和方向对齐。图上黑虚线是均值、红虚线是 ±2 倍标准差,价差突破±2STD 时倾向触发配对反向单,但外汇高杠杆、点差跳空频繁,这种均值回归策略实盘前必须压仓位试。 别把 p 值当入场令 p=0.0052 只说明样本内协整显著,换时间段可能失效。真要上 MT5 跑,先把代码里 login/server/password 换成你自己的 demo 账号,改 symbols 列表就能测别的资产。

MQL5 / C++
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime, timedelta
class="kw">import MetaTrader5 as mt5
class="kw">import pandas as pd
class="kw">import numpy as np
from statsmodels.tsa.stattools class="kw">import coint
class="kw">import matplotlib.pyplot as plt
# connect to MetaTrader class="num">5 terminal
if not mt5.initialize(login=********, server="MetaQuotes-Demo",password="********"):
    print("initialize() failed, error code =",mt5.last_error())
    quit()
# Forex majors - check your Market Watch names
symbols = [&class="macro">#x27;EURUSD&class="macro">#x27;, &class="macro">#x27;GBPUSD&class="macro">#x27;, &class="macro">#x27;USDJPY&class="macro">#x27;, &class="macro">#x27;AUDUSD&class="macro">#x27;, &class="macro">#x27;USDCAD&class="macro">#x27;, &class="macro">#x27;NZDUSD&class="macro">#x27;, &class="macro">#x27;USDCHF&class="macro">#x27;]
# define the timeframe and the number of days
timeframe = mt5.TIMEFRAME_D1  # Daily
n_days = class="num">600
utc_to = class="type">class="kw">datetime.now()
utc_from = utc_to - timedelta(days=n_days)
# download historical data for each symbol
data = {}
for symbol in symbols:
    # Make sure the symbol is available in Market Watch
    mt5.symbol_select(symbol, True)
    
    # Get historical rates
    rates = mt5.copy_rates_range(symbol, timeframe, utc_from, utc_to)
    
    if rates is None or len(rates) == class="num">0:
        print(f"No data for {symbol}.")
        class="kw">continue
    df = pd.DataFrame(rates)
    df[&class="macro">#x27;time&class="macro">#x27;] = pd.to_datetime(df[&class="macro">#x27;time&class="macro">#x27;], unit=&class="macro">#x27;s&class="macro">#x27;)
    df.set_index(&class="macro">#x27;time&class="macro">#x27;, inplace=True)
    data[symbol] = df[&class="macro">#x27;close&class="macro">#x27;]
# Store cointegration test results
results = []
# Test all unique pairwise combinations for cointegration
pairs = [(a, b) for i, a in enumerate(data.columns) for j, b in enumerate(data.columns) if i < j]
print("Cointegration test results(Engle-Granger):")
for a, b in pairs:
    score, pvalue, _ = coint(data[a], data[b])

「成对检验的 p 值怎么读」

上面这段是 Python 里把两组样本 a、b 的检验结果收进列表,并打印 p-value 到小数点后四位。注意 pvalue:.4f 的写法,保证输出对齐,方便你横向扫多组对比。 在 MT5 里做策略显著性验证时,p 值小于 0.05 通常意味着两组收益分布差异显著,但外汇与贵金属属高风险品种,样本外失效概率不低,别把一次回测的 p 值当圣旨。 你可以把这段逻辑移植成 MQL5 的 OnTester 输出,或用 Python 侧跑完再导入 MT5 做 Walk Forward,重点看 p 值在不同时间段是否稳定。

◍ 多资产协整怎么测:约翰森检验的秩与临界值

约翰森协整检验解决的是和恩格尔-格兰杰一样的根本问题:一组资产是否存在长期共同均衡。区别在于它能一次性塞进两个以上的品种,并判断这 N 个资产是否共享同一组均值回归价差,而不是只能两两配对。 检验的核心产出是“协整秩”——即变量线性组合中有几个是平稳的。它会为秩 0、1、2…N-1 分别算一个迹统计量,再配上对应临界值。临界值由变量数、样本长度、置信水平(90%/95%/99%)三者决定,比如 johansen_result.cvt[i,0] 是 90% 水平,cvt[i,2] 是 99%。 我们用 D1 周期、95% 置信度扫了 7 个主要外汇:EURUSD、GBPUSD、USDJPY、AUDUSD、USDCAD、NZDUSD、USDCHF。秩 0 迹统计量 105.25 低于临界值 125.62,一路到秩 6 的 0.58 对 3.84,全都不显著。外汇主盘之间大概率没有共享长期均衡,这点符合直觉。 切到 H1 的 NVDA 与 GOOGL(观测 5769 根),秩 0 迹统计量 18.71 超过 15.49,显著;秩 1 的 0.29 不显著。说明两者存在一个协整关系。但注意:把顺序调成 GOOGL→NVDA 再跑恩格尔-格兰杰,p 值从大于 0.05 变成 0.0403,约翰森却纹丝不动(18.71 / 0.29)。EG 对顺序敏感,约翰森理论上不敏感,小样本或临界边缘才可能抖一下。 实操上,跑 EG 必须正反顺序都测一遍,并把价差图画出来看是否围绕均值上下、两倍标准差内回归。若换顺序结果打架,先加样本量——这也是我们把 D1 换 H1 的原因。外汇与贵金属杠杆高、跳空频繁,协整结论只代表历史样本内的统计倾向,实盘前请在 MT5 用下方代码自测。

MQL5 / C++
# Johansen Test
from class="type">class="kw">datetime class="kw">import class="type">class="kw">datetime, timedelta
class="kw">import MetaTrader5 as mt5
class="kw">import pandas as pd
class="kw">import numpy as np
from statsmodels.tsa.stattools class="kw">import coint
from statsmodels.tsa.vector_ar.vecm class="kw">import coint_johansen
class="kw">import matplotlib.pyplot as plt
# Create a matrix for the test
log_prices = data.apply(np.log)
johansen_result = coint_johansen(log_prices, det_order=class="num">0, k_ar_diff=class="num">1)
# Trace statistics and critical values
print("
Johansen Test Results(Trace Statistic):")
for i, stat in enumerate(johansen_result.lr1):
    cv = johansen_result.cvt[i, class="num">1]  # class="num">5% critical value
    print(f"Rank {i}: Trace Stat = {stat:.2f} | class="num">5% CV = {cv:.2f} | {&class="macro">#x27;Significant&class="macro">#x27; if stat > cv else &class="macro">#x27;Not significant&class="macro">#x27;}")

ADF与KPSS打架时怎么判价差

价差作为时间序列,要确认均值回归属性,先得看它平不平稳——均值、方差、协方差不随时间漂才算数。MQL5圈子做配对常拿Python的statsmodels跑ADF和KPSS,这两个检验一个原假设是“非平稳”,一个是“平稳”,合起来能拆出四种结论。 实测一组价差:ADF统计量-3.0946、p值0.0270,临界值1%/-3.4315、5%/-2.8620、10%/-2.5670,按阈值拒绝原假设判平稳;KPSS统计量2.2702、p值0.0100、滞后44,临界值1%/0.739、5%/0.463、10%/0.347,反而拒绝平稳。两套结果矛盾,按statsmodels文档的分类,KPSS非平稳+ADF平稳属于“差分平稳”:序列可能漂移,但一阶差分后就严平稳。 外汇与贵金属价差受流动性与事件冲击,本身就是高风险品种,这种差分平稳很常见。好在协整配对交易不靠预测模型,价差不用强制转严平稳;但知道它差分才稳,能帮你判断止损带要不要随漂移放宽。 下面这段是跑检验的Python骨架,MQL5端可用Python桥接调用,逐行看逻辑: from statsmodels.tsa.stattools import adfuller —— 引入ADF检验函数 (...) —— 省略数据准备与价差计算 adf_result = adfuller(spread) —— 对价差序列执行ADF,返回统计量/p值等 from statsmodels.tsa.stattools import kpss —— 引入KPSS检验函数 (...) —— 省略前置 def run_kpss(series, regression='c'): —— 封装KPSS,regression='c'测水平平稳 statistic, p_value, lags, crit_values = kpss(series, regression=regression, nlags='auto') —— 自动滞后阶跑检验并解包结果 (...) —— 省略打印 # Run KPSS test on the residuals —— 注释:对残差跑KPSS run_kpss(spread, regression='c') # 'c' = test for level stationarity (use 'ct' for trend) —— 对价差测水平平稳,换'ct'可测趋势平稳

MQL5 / C++
from statsmodels.tsa.stattools class="kw">import adfuller(...) 
adf_result = adfuller(spread)
from statsmodels.tsa.stattools class="kw">import kpss(...) 
 def run_kpss(series, regression=&class="macro">#x27;c&class="macro">#x27;):
    statistic, p_value, lags, crit_values = kpss(series, regression=regression, nlags=&class="macro">#x27;auto&class="macro">#x27;)
(...) 
# Run KPSS test on the residuals
run_kpss(spread, regression=&class="macro">#x27;c&class="macro">#x27;)  # &class="macro">#x27;c&class="macro">#x27; = test for level stationarity(use &class="macro">#x27;ct&class="macro">#x27; for trend)

「散户最值钱的是市场认知」

前两篇把恩格尔-格兰杰检验、约翰森协整检验以及 ADF、KPSS 平稳性检验的入门用法铺开了,下一篇才会真正落到外汇对和一篮子股票的回测与优化。对普通散户来说,这些数学和统计模块在 MQL5 库和开源工具里都能直接调,难点从来不在写公式。 真正稀缺的是你对所交易资产的理解——哪些货币对受同一宏观因子驱动、哪只股票和同业存在结构性价差。高水平从业者早把重活做完了,AI 助手也能帮你正确接线,但选哪组标的、信不信价差会回归,只能靠你自己的盘感与常识。 资源有限就别碰拼执行速度的高频类思路,外汇和贵金属杠杆高、滑点凶,速度劣势会被放大。用认知去挖数据里的相对价值,才是散户能长期试错的小路。 附带三个 ipynb 文件(coint.ipynb 约 132.59 KB、coint_googl_nvda.ipynb 约 132.12 KB、helper_quotes_to_db.ipynb 约 17.34 KB)可离线跑协整与平稳性检验,先下载报价存进 sqlite 再跑,比重复拉数据省事得多。

常见问题

相关系数只说明同涨同跌,协整能确认价差长期回归。用协整挑组合,价差不会越走越远,套利平仓更有依据。
p 值小于 0.05 只说明残差平稳、可能存在协整。还要看样本区间和价差波动幅度,避免过拟合历史。
小布可对接品种页做协整与 EG 检验诊断,直接标出潜在配对和 p 值,你把时间花在决策上就行。
秩为 0 表示无协整关系;迹统计量大于临界值才可拒绝原假设,确认存在对应数量的协整向量。
优先延长样本重测,单边检验易误判。两者打架时降低仓位或暂不做该配对,等信号一致再动手。