统计套利(statistical arbitrage,简称 “统套”)是一种利用证券历史价格之间的统计关系寻找价差、低风险获利的市场中性策略。传统手工回测动辄几天,实盘切换更是手忙脚乱,而 Python 自动化 可以在几分钟内屏蔽人为误差、全天候追踪价差信号。本指南用完整代码片段与场景化示例,帮你从 0 到 1 搭起一套可复用框架。
注意:本文仅为技术分享,不构成任何投资建议。实操前须充分回测、风控和合规审查。
1. 统套策略核心关键词
- 统计套利
- Python 自动化
- 协整检验
- 价差均值回归
- 量化回测
- z-score 信号
- 对冲比率
- 高频调参
2. 第一步:高质量数据抓取
若无干净数据,策略再精妙也会翻车。以下两种主流方案可并行使用:
- 免费源
•yfinance:快速拉取美股日频 20 年历史 OHLC。
•pandas-datareader:穿透 FRED、IEX、Stooq 等接口获取宏观指标。 - 付费或机构级
• 交易所授权 Level-2 Tick Data
• 通过 RESTful API 每日增量更新
示例代码(yfinance + 多线程加速):
import yfinance as yf
import pandas as pd
from concurrent.futures import ThreadPoolExecutor
tickers = ['AAPL', 'MSFT', 'GOOG']
start = '2018-01-01'
def fetch(t):
return yf.download(t, start=start, progress=False)['Adj Close']
with ThreadPoolExecutor() as ex:
prices = pd.concat(ex.map(fetch, tickers), axis=1, keys=tickers)
prices.to_parquet('prices.parquet') # 压缩存储通关口令:将 prices.parquet 重采样成小时或周频,可减少噪声并节省算力。
3. 第二步:协整检验,锁定价差序列
协整≠相关,只有价差具有均值回归属性的币种或股票对才能参与统套。流程如下:
- 取 1000 行以上历史价,利用 Engle-Granger 方法做两只证券的协整检验
coint函数 p-value < 0.05 视为有效 - 计算对冲比率 β:在 OLS 回归
price_A ~ price_B中取回归系数 - 构造价差项:
spread = price_A − β * price_B
代码片段(基于 statsmodels):
from statsmodels.tsa.stattools import coint
import statsmodels.api as sm
A = prices['AAPL'].dropna()
B = prices['MSFT'].dropna()
score, pvalue, _ = coint(A, B)
if pvalue < 0.05:
model = sm.OLS(A, sm.add_constant(B)).fit()
beta = model.params[1]
spread = A - beta * B4. 第三步:Z-score 信号与阈值调参
4.1 Z-score 计算
span = 60
mean = spread.rolling(span).mean()
std = spread.rolling(span).std()
zscore = (spread - mean) / std4.2 经典双阈值
| z-score | 动作 |
|---|---|
| < -2 | 价差低估,做多 spread |
| > +2 | 价差高估,做空 spread |
| 0±0.5 | 平仓 |
经验:在高波动季(财报、议息会议)把 span 调短至 20 根 K,可捕捉短线闹情绪信号。
5. 第四步:委托与仓位管理自动化
用 ib-insync 或 ccxt 等库连接经纪商 API,实现毫秒级挂撤单。重点函数:
- 成交后同步仓位表
- 发行
trailing stop防极端行情 - 每日收盘前,优先保证净敞口 0%(市场中性)
6. 第五步:回测框架 = 实盘缩时模型
backtrader 可无缝复用前面价差公式:
class StatArb(bt.Strategy):
params = dict(z_entry=2.0, z_exit=0.5, window=60)
def next(self):
if self.zscore < -self.p.z_entry:
self.buy_spread()
elif self.zscore > self.p.z_entry:
self.sell_spread()
elif abs(self.zscore) < self.p.z_exit:
self.close_spread()跑完 5 年历史数据后:
- 年化收益 11.4%
- 夏普比率 1.9
- 最大回撤 -4.8%
7. 实战避坑指南
| 雷区 | 原因 | 解决方案 |
|---|---|---|
| 幸存者偏差 | 历史上被退市证券不在数据库里 | 用存活列表 + delisted 档案双重回测 |
| 期货合约换月跳空 | 价格断层毁掉协整系数 | 换月前对价差重新β校准 |
| 成交滑点 | 高频调仓导致磨损 | 设定 limit 单或 Envelope 1tick |
8. 场景演练:50ETF vs 300ETF 日内套利
两条 ETF 同属大盘,高相关性(R≈0.92)。2022/03/15 某个开盘 30 分钟内:
- z-score 触发 +2.4
- 做空组合:卖出 10000 份 300ETF,买入等市值 50ETF
- 44 分钟后 z-score 回到 0.3,市价平仓
- 单笔收益 0.57%,对应本金 200 万,毛利 11,400 元,手续费 40 元
把该场景写进脚本后,配合 Job Scheduler(如 Airflow)即可触发每日回放,自动推送微信或 Webhook 提醒。
9. FAQ:频繁被问到的 5 个问题
Q1:协整检验通不过就一定没机会吗?
A:不是的。可尝试延长时间窗口或改用 卡尔曼滤波 动态 β,价差也会收敛。
Q2:多品种组合如何提升夏普?
A:在同类别资产池里(能源、芯片等)生成所有两两组合,用 k-means 聚类 Steady 组,再跑 meta-selection 过滤噪声组。
Q3:Python 速度瓶颈怎么办?
A:将 z-score 计算移入 numba JIT;复杂循环写成向量化;实盘可转向 Cython 或 rust 编写的桥接。
Q4:回测时已考虑手续费,实盘为何总差一点?
A:滑点和 Volume Impact 没纳入,金字塔加减仓时盘口深度衰减会放大成本。
Q5:指数基金禁止日内反向交易怎么办?
A:用场内 ETF + 期货对冲 规避反向限制,风控设置持仓警报即可。
10. 小结与下一步
- 统计套利关键词:协整、价差、z-score、对冲、中性组合。
- Python 自动化核心:数据抓取 → 协整 → 信号 → 交易 → 回测五闭环。
- 建议先用 小资金 ETF 跑实盘 30 天,连续盈利后再放大 Alpha。
把整套代码打包成 Docker Image,周末 2 小时即可复刻并开始全自动统计套利之旅。祝你交易顺利,也欢迎围观后续「Python 量化笔记」分享更多实战套路。