2. 诊断框架搭建:建立诊断流程、数据准备与清洗、回测与实盘对比框架

策略失效了,别急着改参数。先搭个诊断框架。

我见过太多人,策略一亏钱就调阈值、改周期,结果越改越糟。为什么?因为没有系统性的诊断流程。就像医生看病,你得先问诊、再检查、最后开药方。量化策略也一样。

2.1 诊断流程:三步走

我个人习惯把诊断拆成三个阶段。每个阶段都有明确的目标和输出。

  1. 数据层诊断:检查数据是否干净、对齐、无未来信息。
  2. 策略层诊断:验证逻辑是否还在、参数是否过拟合、市场环境是否变了。
  3. 执行层诊断:对比回测与实盘,找滑点、延迟、成交率差异。

这三步必须按顺序来。数据有问题,后面全是白费功夫。我曾经有个策略,回测年化30%,实盘直接亏。查了两周才发现,是数据源换了字段格式,信号全偏了。嗯,从那以后,数据层诊断成了我的第一道防线。

核心原则:先数据,再策略,最后执行。跳过任何一步,诊断结果都不可靠。

量化策略失效诊断流程 数据层诊断 数据完整性·对齐·无未来 策略层诊断 逻辑验证·过拟合·环境变化 执行层诊断 回测vs实盘·滑点·延迟 发现问题后返回上一层修正 💡 诊断不是一次性的。每修复一层,都要重新跑通前面所有层,确保没有引入新问题。 —— 我习惯把这个叫「分层回退验证法」

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 差异分析矩阵

当对比结果出现偏差时,我习惯用这个矩阵快速定位问题。

现象 可能原因 修复方向
收益率走势一致,但实盘收益更低 滑点假设偏乐观、手续费未算全 调整滑点模型、增加交易成本
信号对齐率低 数据时间戳不一致、计算逻辑差异 统一数据源、对齐计算时点
实盘最大回撤远大于回测 流动性不足、市场冲击成本被忽略 加入流动性过滤、模拟大单冲击
成交率低于预期 订单簿深度不足、撤单率过高 优化下单算法、调整订单类型

嗯,这个矩阵不是万能的,但它能帮你快速缩小排查范围。我每次做策略诊断,都会先跑一遍对比框架,然后对着矩阵找问题。效率高很多。

记住:诊断框架不是一次性搭建完就完事了。随着策略迭代、市场变化,框架本身也需要不断优化。我每季度会复盘一次诊断流程,看看有没有遗漏的维度,或者有没有更好的对比方法。

好了,诊断框架就搭到这里。数据清洗、回测对比,这两块是基本功。下一层,我们会深入策略逻辑的诊断——那才是真正考验功力的地方。