2. 诊断框架搭建
策略失效了,别急着改参数。先搭个诊断框架。
我见过太多人,策略一亏钱就调阈值、改周期,结果越改越糟。为什么?因为没有系统性的诊断流程。就像医生看病,你得先问诊、再检查、最后开药方。量化策略也一样。
2.1 诊断流程:三步走
我个人习惯把诊断拆成三个阶段。每个阶段都有明确的目标和输出。
- 数据层诊断:检查数据是否干净、对齐、无未来信息。
- 策略层诊断:验证逻辑是否还在、参数是否过拟合、市场环境是否变了。
- 执行层诊断:对比回测与实盘,找滑点、延迟、成交率差异。
这三步必须按顺序来。数据有问题,后面全是白费功夫。我曾经有个策略,回测年化30%,实盘直接亏。查了两周才发现,是数据源换了字段格式,信号全偏了。嗯,从那以后,数据层诊断成了我的第一道防线。
核心原则:先数据,再策略,最后执行。跳过任何一步,诊断结果都不可靠。
2.2 数据准备与清洗:地基要打牢
数据是策略的命根子。数据脏了,再牛的模型也白搭。你想想看,如果K线里有跳空、复权没处理好、或者时间戳对不上,策略信号能准吗?
2.2.1 数据源选择
我个人建议至少准备两个独立数据源。一个做主数据,一个做校验。我在项目中遇到过,某家数据商的期货数据在交割日前后经常出错,幸好有另一家做对比,才没踩坑。
| 数据维度 | 常见问题 | 清洗方法 |
|---|---|---|
| OHLCV | 缺失值、异常跳空、价格倒挂 | 前向填充、阈值过滤、逻辑校验 |
| 复权因子 | 除权除息未处理、复权方式不一致 | 统一使用后复权、校验复权因子序列 |
| 时间戳 | 时区错误、非交易时段数据 | 统一转UTC、剔除集合竞价外数据 |
| 成交量 | 单位不一致、异常放量/缩量 | 标准化单位、Z-score异常检测 |
2.2.2 清洗流水线
我写了个标准的数据清洗流程,每次跑策略前都会过一遍。说白了,就是给数据做个「体检」。
def data_cleaning_pipeline(df):
# 1. 去重
df = df.drop_duplicates(subset=['datetime', 'symbol'])
# 2. 时间对齐
df = df.set_index('datetime').asfreq('1min').reset_index()
# 3. 缺失值处理
df['close'] = df['close'].fillna(method='ffill')
df['volume'] = df['volume'].fillna(0)
# 4. 异常值过滤(3倍标准差)
for col in ['open', 'high', 'low', 'close']:
mean = df[col].mean()
std = df[col].std()
df = df[(df[col] > mean - 3*std) & (df[col] < mean + 3*std)]
# 5. 逻辑校验:high >= low, close 在 [low, high] 内
df = df[(df['high'] >= df['low']) &
(df['close'] >= df['low']) &
(df['close'] <= df['high'])]
return df
小技巧:清洗完数据后,一定要做一次「回放验证」。把清洗前后的K线图叠在一起看,确保没有引入人为偏差。我习惯用matplotlib画个对比图,一眼就能看出问题。
2.3 回测与实盘对比框架
回测跑得再好,实盘一上就露馅。这是量化交易里最头疼的事。为什么会这样?因为回测是理想世界,实盘是真实战场。
2.3.1 对比维度
我一般从四个维度做对比。每个维度都有具体的量化指标。
- 收益率曲线:回测 vs 实盘的净值曲线是否走势一致?
- 交易信号:回测信号和实盘信号的时间戳是否对齐?
- 滑点与成本:实盘滑点是否超出回测假设?
- 成交率:实盘成交率是否低于回测的假设?
2.3.2 对比工具
我写了个简单的对比函数,每次实盘跑完都会自动生成报告。
def backtest_vs_live_comparison(bt_df, live_df):
# 计算每日收益率
bt_returns = bt_df['equity'].pct_change()
live_returns = live_df['equity'].pct_change()
# 相关性分析
correlation = bt_returns.corr(live_returns)
# 最大回撤对比
bt_max_dd = max_drawdown(bt_df['equity'])
live_max_dd = max_drawdown(live_df['equity'])
# 信号对齐率
signal_match = (bt_df['signal'] == live_df['signal']).mean()
return {
'correlation': correlation,
'bt_max_dd': bt_max_dd,
'live_max_dd': live_max_dd,
'signal_match_rate': signal_match
}
避坑指南:我曾经发现回测和实盘的信号对齐率只有60%,查了半天才发现是回测用了收盘价,而实盘信号是盘中触发的。时间戳精度不一致,导致信号完全对不上。后来我统一用分钟级数据做回测,问题才解决。
2.3.3 差异分析矩阵
当对比结果出现偏差时,我习惯用这个矩阵快速定位问题。
| 现象 | 可能原因 | 修复方向 |
|---|---|---|
| 收益率走势一致,但实盘收益更低 | 滑点假设偏乐观、手续费未算全 | 调整滑点模型、增加交易成本 |
| 信号对齐率低 | 数据时间戳不一致、计算逻辑差异 | 统一数据源、对齐计算时点 |
| 实盘最大回撤远大于回测 | 流动性不足、市场冲击成本被忽略 | 加入流动性过滤、模拟大单冲击 |
| 成交率低于预期 | 订单簿深度不足、撤单率过高 | 优化下单算法、调整订单类型 |
嗯,这个矩阵不是万能的,但它能帮你快速缩小排查范围。我每次做策略诊断,都会先跑一遍对比框架,然后对着矩阵找问题。效率高很多。
记住:诊断框架不是一次性搭建完就完事了。随着策略迭代、市场变化,框架本身也需要不断优化。我每季度会复盘一次诊断流程,看看有没有遗漏的维度,或者有没有更好的对比方法。
好了,诊断框架就搭到这里。数据清洗、回测对比,这两块是基本功。下一层,我们会深入策略逻辑的诊断——那才是真正考验功力的地方。