Pair trade 信号挖掘算法技术(双重标准差)
核心算法: Adaptive Bollinger Z-Score + 多周期协整验证
目录
- 系统概览
- 信号生成全链路
- 第一层:相关性分析
- 第二层:协整检验 (Engle-Granger 两步法)
- 第三层:Z-Score 计算
- 多周期验证门控
- Adaptive Bollinger Z-Score 策略引擎
- 入场与退场决策逻辑
- 风控体系
- 关键参数速查表
- 回测与参数优化
- 架构总结
1. 系统概览
本系统是一个加密货币配对交易平台,专注于 Hyperliquid 交易所上的 HYPE/PURR 交易对。核心思路是利用统计套利——当两个高度相关的资产价格偏离均衡关系时入场,等待价格回归时退场获利。
核心技术栈
| 组件 | 技术选型 | 用途 |
|---|---|---|
| 协整检验 | statsmodels.OLS + adfuller |
Engle-Granger 两步法 |
| 策略引擎 | 自研 Adaptive Bollinger | EMA + 滚动标准差突破 |
| 数据存储 | TimescaleDB | 时序数据高效存储 |
| 实时数据 | WebSocket | 5m/1h/4h K线订阅 |
| 交易执行 | Hyperliquid Python SDK | 市价单执行 |
信号流水线概览
WebSocket K线数据 (5m/1h/4h)
│
▼
┌─────────────────────┐
│ 相关性分析 (Pearson) │ ← Layer 1: 收益率相关
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ 协整检验 (EG两步法) │ ← Layer 2: OLS回归 + ADF检验
│ · 全量OLS (Old) │
│ · 双窗口OLS (New) │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ Z-Score 计算 │ ← Layer 3: 基于OLS价差的标准化
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ 多周期门控验证 │ ← Gate 1: 协整通过数 ≥ 2/6
│ │ ← Gate 2: 健康监控 (4h/60d)
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ Adaptive Bollinger │ ← 自适应突破检测
│ 策略引擎 │
└────────┬────────────┘
│
▼
┌─────────────────────┐
│ 风控 9 项检查 │ ← 入场前最终审查
└────────┬────────────┘
│
▼
交易执行
2. 信号生成全链路
数据采集层
系统通过 WebSocket 订阅 Hyperliquid 交易所的 K线数据,支持三个时间框架:
| 时间框架 | 历史窗口 | K线条数 | 用途 |
|---|---|---|---|
| 5m | 7 天 | ~2,016 | 最快检测,高灵敏度 |
| 1h | 30 天 | ~720 | 中期确认 |
| 4h | 60 天 | ~360 | 长期稳定性,主信号源 |
并行处理架构
WebSocket → K线缓冲队列 (10,000) → 批量写入线程 → DB
│
▼
分析任务队列 (30,000)
│
▼
30 个分析工作线程 (并行)
├─ 相关性分析
├─ 协整检验 (Old + New)
├─ Z-Score 计算
├─ 健康监控
└─ 信号强度分类
性能指标:
- 信号延迟: < 5 秒 (K线到分析完成)
- 告警延迟: < 10 秒 (分析到飞书通知)
- DB 写入: > 40K 行/秒 (COPY 方法)
3. 第一层:相关性分析
源码:
src/utils/analysis/analysis_core.py:83-140
为什么使用收益率相关而非价格相关?
| 对比项 | 价格相关 | 收益率相关 |
|---|---|---|
| 趋势影响 | 受整体市场涨跌影响 | 去趋势化,消除市场β |
| 平稳性 | 价格通常非平稳 | 收益率通常平稳 |
| 实战意义 | 绝对价格关联 | "BTC涨1%时,PURR涨X%" |
算法实现
# 1. 对齐时间索引
aligned = pd.DataFrame({'base': base_prices, 'alt': alt_prices}).dropna()
# 2. 计算收益率序列
base_returns = aligned['base'].pct_change().dropna()
alt_returns = aligned['alt'].pct_change().dropna()
# 3. Pearson 相关系数
correlation = base_returns.corr(alt_returns, method='pearson')
最低数据要求: 20 个数据点 (MIN_POINTS_FOR_CORRELATION)
4. 第二层:协整检验
源码:
src/utils/analysis/analysis_core.py:149-407
协整检验是配对交易的理论基石。两个非平稳价格序列如果存在协整关系,它们的线性组合(价差)是平稳的——这意味着价差会均值回归。
4.1 Engle-Granger 两步法
第一步: OLS 回归
$$\log(P_{alt}) = \alpha + \beta \cdot \log(P_{base}) + \varepsilon$$
第二步: 对残差 $\varepsilon$ 做 ADF (Augmented Dickey-Fuller) 检验
- p-value < 0.05 → 残差平稳 → 协整成立
- p-value ≥ 0.05 → 残差非平稳 → 协整不成立
4.2 智能模型选择
系统根据截距项 $\alpha$ 的统计特性自动选择最优模型:
def _select_cointegration_model(alpha, alpha_pvalue):
if alpha_pvalue < 0.05 and |alpha| > 5.0:
# 跨资产类配对 (如 NEAR/BTC): α巨大,去掉截距
return "no_intercept_forced", False
elif alpha_pvalue < 0.05 and |alpha| < 2.0:
# 同类资产配对 (如 UNI/SUSHI): α显著但小,保留截距
return "standard_EG", True
else:
# α不显著或中等范围: 默认无截距
return "no_intercept", False
设计原理: 不同资产对的价格关系存在结构性差异。跨资产类(如 meme 币 vs 主流币)的截距可能很大但无统计意义,保留会扭曲价差计算。
4.3 两种协整检验方法
Method A: 全量 OLS (验证性)
# 使用全部历史数据
model = sm.OLS(log_alt_all, sm.add_constant(log_base_all)).fit()
spread = log_alt - (α + β × log_base) # 或不含α
adf_pvalue = adfuller(spread).pvalue
- 优点: 参数估计最稳定
- 缺点: 存在 look-ahead bias
- 用途: 事后验证,作为协整计数的一票
Method B: 双窗口 OLS (实时交易)
# OLS 回归窗口: 100 期 (BETA_WINDOW)
# 使用前 99 个点回归,避免 look-ahead bias
ols_data = recent_data[-100:-1] # 前 99 个点
model = sm.OLS(log_alt_ols, sm.add_constant(log_base_ols)).fit()
# Z-Score 窗口: 30 期 (ZSCORE_WINDOW)
# 用短窗口计算价差,提高均值回归检测灵敏度
spread_zscore = log_alt[-30:] - (α + β × log_base[-30:])
# ADF 检验用完整 100 期
spread_full = log_alt[-100:] - (α + β × log_base[-100:])
adf_pvalue = adfuller(spread_full).pvalue
双窗口设计哲学:
← ──── BETA_WINDOW (100期) ──── →
[━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━]
^ ^
OLS 回归 (前99点) 当前点(不参与回归)
← ZSCORE_WINDOW (30期) →
[━━━━━━━━━━━━━━━━━━━━━━]
^ ^
Z-Score 计算窗口 最新值
- 长窗口 (100期): 保证回归参数 β 的稳定性
- 短窗口 (30期): 提高均值回归检测的灵敏度
- 去除最后一点: 避免 look-ahead bias
5. 第三层:Z-Score 计算
源码:
src/utils/analysis/analysis_core.py:410-480
公式
$$Z_{score} = \frac{S_{current} - \bar{S}{window}}{\sigma{S_{window}}}$$
其中:
- $S_{current}$ = 当前价差 (OLS 残差)
- $\bar{S}_{window}$ = 窗口内价差均值 (前 N-1 期)
- $\sigma_{S_{window}}$ = 窗口内价差标准差 (前 N-1 期)
实现细节
spread = cointegration_result['spread'] # 来自双窗口OLS
# 使用前 N-1 期计算统计量(避免样本偏差)
spread_mean = spread.iloc[:-1].mean()
spread_std = spread.iloc[:-1].std()
# 当前 Z-Score
zscore = (spread.iloc[-1] - spread_mean) / spread_std
Z-Score 的经济学含义
| Z-Score | 含义 | 交易信号 |
|---|---|---|
| Z < -2.5 | 目标币相对基准币严重低估 | 做多目标币 |
| -2.5 < Z < -2.0 | 中等低估 | 中等信号 |
| -2.0 < Z < 2.0 | 正常波动范围 | 无信号 |
| 2.0 < Z < 2.5 | 中等高估 | 中等信号 |
| Z > 2.5 | 严重高估 | 做空目标币 |
信号强度分级
SIGNAL_STRENGTH_THRESHOLDS = {'strong': 2.5, 'medium': 2.0}
def determine_signal_strength(zscore):
abs_z = abs(zscore)
if abs_z > 2.5: return 'strong' # 强信号
elif abs_z > 2.0: return 'medium' # 中等信号
else: return 'weak' # 弱信号
信号强度直接影响仓位大小:
| 信号强度 | 缩放系数 | 示例仓位 ($100 基础) |
|---|---|---|
| strong | 1.5x | $150 |
| medium | 1.0x | $100 |
| weak | 0.7x | $70 |
6. 多周期验证门控
源码:
src/utils/analysis/analysis_core.py:700-858
这是信号从"原始计算"到"可交易信号"的关键验证环节。
Gate 1: 协整通过数检查
3 个时间周期 × 2 种方法 (Old + New) = 6 个协整检验结果
5m/7d: Old ✓ New ✗ → 1 通过
1h/30d: Old ✓ New ✓ → 2 通过
4h/60d: Old ✗ New ✓ → 1 通过
────────────────────────────
总计: 4/6 通过 ≥ 2 (阈值) → ✅ Gate 1 通过
阈值: COINTEGRATION_THRESHOLD = 2 (至少 2 个检验通过)
设计考虑: 阈值设为 2 而非 6,是因为不同市场状态下协整关系会暂时性弱化,过于严格会错过有效信号。
Gate 2: 健康监控
仅在 4h/60d 周期执行,使用双窗口健康监控器:
长期窗口 (200 期 ≈ 33 天): → 长期结构稳定性
短期窗口 (100 期 ≈ 17 天): → 近期状态判断
判定: 短期窗口状态 == HEALTHY → ✅ Gate 2 通过
健康评分组成:
| 指标 | 权重 | 含义 |
|---|---|---|
| ADF 平稳性 | 40% | 价差是否均值回归 |
| 半衰期 | 30% | 回归速度 (5-30 期为健康) |
| 稳定性 | 30% | 参数随时间的波动程度 |
状态分级: HEALTHY → DEGRADING → UNHEALTHY
完整验证流程图
对每个周期 (5m/7d, 1h/30d, 4h/60d):
├─ 计算 correlation
├─ Old OLS 协整检验 → passed/failed
├─ New 双窗口 OLS 协整检验 → passed/failed
├─ 健康监控 (仅 4h/60d)
└─ 计算 Z-Score
Gate 1: cointegration_count ≥ 2 ?
├─ NO → ❌ 信号丢弃
└─ YES → 继续
Gate 2: 4h/60d 短期健康 == HEALTHY ?
├─ NO → ❌ 信号丢弃
└─ YES → ✅ 信号通过
方向: direction = 'long' if zscore_4h < 0 else 'short'
强度: strength = determine_signal_strength(zscore_4h)
7. Adaptive Bollinger Z-Score 策略引擎
源码:
src/trading/strategy.py
这是系统的核心交易策略,对通过多周期验证的 Z-Score 进行二次处理,生成精确的入场/退场时机。
7.1 核心思想
传统方法: 当 |zscore_4h| > 阈值时交易
本策略: 跟踪 Z-Score 相对于自身移动平均的偏离度
传统: 绝对阈值 → 在震荡市中频繁假信号
本策略: 自适应阈值 → 根据近期波动率动态调整
7.2 算法步骤
# Step 1: EMA 追踪 Z-Score 的"中枢"
ema_zscore = EMA(zscore_4h, span=72) # 72 × 5min = 6小时
# Step 2: 计算 Z-Score 的近期波动率
rolling_std = std(zscore_4h[-72:], ddof=1)
rolling_std = max(rolling_std, 0.01) # 最小标准差保护
# Step 3: 计算自适应 Z (核心指标)
adaptive_z = (zscore_4h - ema_zscore) / rolling_std
# Step 4: 突破检测
breakout = |adaptive_z| > threshold AND 上一tick未超阈值
7.3 数学推导
Adaptive Z 的含义: 衡量当前 Z-Score 偏离近期"正常水平"的程度,单位是近期波动的标准差。
$$adaptive_z = \frac{z_{4h}^{(t)} - EMA(z_{4h}, 72)}{\sigma_{rolling}(z_{4h}, 72)}$$
| 场景 | 传统方法 | Adaptive 方法 |
|---|---|---|
| 平静市 (std=0.5) | z=2.5 触发 | adaptive_z = (2.5 - 2.0) / 0.5 = 1.0 不触发 |
| 波动市 (std=2.0) | z=2.5 触发 | adaptive_z = (2.5 - 0) / 2.0 = 1.25 不触发 |
| 真正异常 (std=0.5) | z=3.5 触发 | adaptive_z = (3.5 - 2.0) / 0.5 = 3.0 触发 |
优势: 自动适应市场状态变化,在波动加剧时抑制信号,在真正异常时快速响应。
7.4 状态机
┌──────────┐ breakout ┌──────────┐ reversion ┌──────────┐
│ 空仓 │ ──────────────→ │ 持仓 │ ──────────────→ │ 平仓 │
│ (IDLE) │ |az| > 2.5 │ (OPEN) │ 回归到 18% │ (CLOSE) │
│ │ |z4h| > 0.5 │ │ │ │
│ │ 冷却期已过 │ │ 或触发止损 │ │
└──────────┘ └──────────┘ └───┬──────┘
▲ │
│ 冷却 30 分钟 │
└────────────────────────────────────────────────────────────┘
7.5 EMA 增量更新
# 衰减因子
alpha = 2.0 / (span + 1) # span=72 → alpha ≈ 0.0274
# 增量更新 (每个 5m tick)
if ema is None:
ema = z4h # 首次: 直接赋值
else:
ema = alpha * z4h + (1 - alpha) * ema # 等价于 ewm(span=72, adjust=False)
缓冲区: deque(maxlen=144) — 存储最近 12 小时的 z4h 值
8. 入场与退场决策逻辑
8.1 入场条件 (4 重检查)
源码:
src/trading/strategy.py:396-462
def _check_entry(symbol, z4h, adaptive_z, timestamp):
# ① 无持仓
if position is not None:
return None
# ② 突破检测: 首次穿越阈值
current_above = |adaptive_z| > threshold # threshold = 2.5
breakout = current_above and not prev_above_threshold
if not breakout:
return None
# ③ Z-Score 绝对值过滤
if abs(z4h) < min_zscore_abs: # min_zscore_abs = 0.5
return None
# ④ 冷却期检查
if last_trade_time and elapsed < cooldown_minutes * 60: # 30 min
return None
# 方向判断
direction = 'long' if adaptive_z < -threshold else 'short'
return EntrySignal(direction, z4h, adaptive_z)
8.2 退场条件 (均值回归退出)
源码:
src/trading/strategy.py:338-394
def _check_exit(symbol, z4h, adaptive_z):
baseline = position.entry_adaptive_z # 入场时的 adaptive_z
reversion_target = baseline * reversion_factor # 0.18
# Long 仓位: adaptive_z 从负值回归到接近 0
if direction == 'long' and adaptive_z >= reversion_target:
return ExitSignal('reversion')
# Short 仓位: adaptive_z 从正值回归到接近 0
if direction == 'short' and adaptive_z <= reversion_target:
return ExitSignal('reversion')
退场示例:
入场: adaptive_z = -3.0 (做多)
退出目标: -3.0 × 0.18 = -0.54
含义: adaptive_z 从 -3.0 回归到 -0.54 即退出
即回归了 (3.0 - 0.54) / 3.0 = 82% 的路程
8.3 风控退场 (Secondary)
| 退场条件 | 参数 | 触发逻辑 |
|---|---|---|
| 止损 | 5% | PnL% < -5% |
| 移动止损 | 激活 7%, 回调 3% | 盈利达 7% 后,从峰值回撤 3% |
| 最大持仓时间 | 72 小时 | 超时强制平仓 |
| 最大回撤 | 10% | 账户从峰值回撤 >10% |
9. 风控体系
源码:
src/trading/risk_manager.py,src/trading/safety.py
9.1 九项开仓前检查
┌────────────────────────────────────────────────────┐
│ Pre-Trade Check (9项) │
├─────┬──────────────────────────────────────────────┤
│ 1 │ 交易功能已启用 (TRADING_ENABLED=true) │
│ 2 │ Kill Switch 未激活 │
│ 3 │ 熔断器未触发 │
│ 4 │ 无重复仓位 (同一交易对不重复开仓) │
│ 5 │ 未达最大持仓对数上限 (max=3) │
│ 6 │ 总敞口未超限 (max=$3,000) │
│ 7 │ 当日亏损未超限 (max=$500) │
│ 8 │ 最大回撤在限制内 (max=10%) │
│ 9 │ 可用余额充足 (保证金 × 安全系数) │
└─────┴──────────────────────────────────────────────┘
任意一项失败 → 拒绝开仓并记录原因
9.2 三层安全机制
Layer 1: Kill Switch (紧急停止)
┌─────────────────────────────┐
│ touch /tmp/trading_kill_switch │ → 立即停止所有交易
│ rm /tmp/trading_kill_switch │ → 恢复交易
└─────────────────────────────┘
Layer 2: Rate Limiter (速率限制)
┌─────────────────────────────┐
│ 滑动窗口 60 秒 │
│ 最大 30 笔下单/分钟 │ → 防止信号风暴
└─────────────────────────────┘
Layer 3: Circuit Breaker (熔断器)
┌─────────────────────────────┐
│ 连续 5 次下单失败 │ → 熔断 300 秒
│ 冷却期自动恢复 │ → 防止级联故障
└─────────────────────────────┘
9.3 仓位大小计算
# 1. 基础仓位
base_usd = 100 # 默认 $100
# 2. 信号强度缩放
scale = {'strong': 1.5, 'medium': 1.0, 'weak': 0.7}
position_usd = base_usd * scale[strength]
# 3. 上限检查
position_usd = min(position_usd, max_position_usd) # max=500
# 4. 可用余额动态限制
max_affordable = available_balance * leverage / legs * 0.9
position_usd = min(position_usd, max_affordable)
# 5. 转换为币种数量
alt_size = position_usd / alt_price
10. 关键参数速查表
分析参数
| 参数 | 值 | 说明 | 定义位置 |
|---|---|---|---|
BETA_WINDOW |
100 | OLS 回归窗口 | src/config.py:68 |
ZSCORE_WINDOW |
30 | Z-Score 计算窗口 | src/config.py:69 |
COINTEGRATION_THRESHOLD |
2 | 协整通过门槛 (共 6 票) | src/config.py:70 |
SIGNAL_STRENGTH_THRESHOLDS |
strong: 2.5, medium: 2.0 | 信号强度阈值 | src/config.py:71 |
HEALTH_MONITOR_LONG_WINDOW |
200 | 长期健康监控窗口 | src/config.py:80 |
HEALTH_MONITOR_SHORT_WINDOW |
100 | 短期健康监控窗口 | src/config.py:81 |
ALPHA_CROSS_ASSET_THRESHOLD |
5.0 | α 跨资产判定阈值 | src/config.py:74 |
ALPHA_SAME_ASSET_THRESHOLD |
2.0 | α 同资产判定阈值 | src/config.py:75 |
策略参数
| 参数 | 值 | 说明 | 定义位置 |
|---|---|---|---|
strategy_ema_span |
72 | EMA 跨度 (6 小时) | src/trading/config.py:65 |
strategy_adaptive_threshold |
2.5 | Adaptive Z 突破阈值 | src/trading/config.py:66 |
strategy_min_zscore_abs |
0.5 | Z-Score 绝对值过滤 | src/trading/config.py:67 |
strategy_reversion_factor |
0.18 | 均值回归退出因子 (82%回归) | src/trading/config.py:68 |
strategy_cooldown_minutes |
30 | 交易冷却期 (分钟) | src/trading/config.py:69 |
风控参数
| 参数 | 值 | 说明 |
|---|---|---|
stop_loss_pct |
5% | 止损百分比 |
trailing_stop_activation_pct |
7% | 移动止损激活线 |
trailing_stop_callback_pct |
3% | 移动止损回调线 |
max_hold_hours |
72h | 最大持仓时间 |
max_open_pairs |
3 | 最大同时持仓对数 |
max_exposure_usd |
$3,000 | 最大总敞口 |
max_daily_loss_usd |
$500 | 每日最大亏损 |
max_drawdown_pct |
10% | 最大回撤 |
leverage |
3x | 杠杆倍数 |
11. 回测与参数优化
可用回测脚本
| 脚本 | 策略 | 说明 |
|---|---|---|
backtest_adaptive_zscore.py |
Adaptive Bollinger | 当前主力策略 |
backtest_double_zscore.py |
双 Z-Score | 替代方案 |
backtest_kalman.py |
卡尔曼滤波 | 实验性 |
backtest_cusum.py |
CUSUM 控制图 | 实验性 |
optimize_adaptive_zscore.py |
网格搜索 | 参数优化 |
参数敏感性分析范围
| 参数 | 测试范围 | 当前值 |
|---|---|---|
| EMA span | 36, 48, 72, 96, 144 | 72 |
| Threshold | 1.5, 2.0, 2.5, 3.0 | 2.5 |
| Min Z-Score | 0.1, 0.3, 0.5 | 0.5 |
| Reversion Factor | 0.10, 0.15, 0.18, 0.25 | 0.18 |
回测评估指标
- 胜率 (Win Rate)
- 盈亏比 (Profit Factor)
- 最大回撤 (Max Drawdown)
- 夏普比率 (Sharpe Ratio)
- 索提诺比率 (Sortino Ratio)
- 平均持仓时间
- 最大连续亏损次数
12. 架构总结
信号挖掘的分层设计
┌─────────────────────┐
│ 数据采集层 │
│ WebSocket + DB │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ 统计分析层 │
│ 相关性 + 协整 + Z │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ 信号验证层 │
│ 多周期门控 + 健康 │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ 策略决策层 │
│ Adaptive Bollinger │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ 风控执行层 │
│ 9项检查 + 3层安全 │
└─────────┬───────────┘
│
┌─────────▼───────────┐
│ 交易执行层 │
│ Hyperliquid SDK │
└─────────────────────┘
关键设计决策
| 决策 | 选择 | 原因 |
|---|---|---|
| 相关性计算 | 收益率 Pearson | 去趋势、平稳性好 |
| 协整方法 | Engle-Granger | 适合两变量配对交易 |
| 价差定义 | OLS 对数回归残差 | 比简单比率更科学 |
| 窗口策略 | 双窗口 (100/30) | 平衡稳定性与灵敏度 |
| 策略引擎 | Adaptive Bollinger | 自适应市场波动率变化 |
| 退出方式 | 比例回归 (18%) | 保护利润,避免贪婪 |
| 模型选择 | α 自适应 | 适配不同资产类别 |
信号从生成到执行的时间线
T+0s: WebSocket 收到新 5m K线
T+1~3s: 分析工作线程完成多周期计算
T+3~5s: 多周期门控验证 (Gate 1 + Gate 2)
T+5s: Adaptive Bollinger 策略处理
T+5~6s: 风控 9 项检查
T+6~8s: Hyperliquid 市价单提交
T+8~10s: 飞书告警通知推送
端到端延迟: < 10 秒