回测历史长度不够,你的策略可能正在“过拟合环境”

用户头像sh_****559rtx
2026-06-26 发布

在社区里我们经常看到大家分享回测绩效,有些曲线平滑得让人羡慕。但身为券商投顾,我们拿到客户策略的第一反应往往是:数据用了多长的历史? 这个问题如果不问清楚,后面的所有绩效分析都可能建立在沙子上。

客户的需求是穿越牛熊,不是只赢一年

来我们这里寻求量化支持的客户,诉求很直接:希望策略在外汇市场里能长期稳定,而不是赌对一波行情就结束。他们期望回测结果能真实反映策略在多种环境下的表现。
可现实是,很多客户自己做的回测,数据只拉到两三年。而这两三年很可能是一段趋势顺畅或低波动的窗口,策略表现自然会超常发挥。一旦进入剧烈震荡年份,实盘就急转直下。

投顾的痛:我们曾被“短历史策略”伤过

我们团队在早期服务客户时,也踩过这个坑。帮客户验证一个外汇趋势策略,用API提供的三年分钟线跑出来收益可观。客户信心满满上线后,遇到市场突变,策略大幅回撤。复盘发现,那三年数据里根本没有类似2008年或2015年那样的极端波动。
从那以后,我们形成了一个核心认知:外汇API的数据源差异,最关键的不是有没有数据,而是数据从哪一年开始。历史长度的多寡,直接决定了回测是否覆盖了真实市场结构。

数据差异的几个隐蔽点

对比不同外汇API后,我们发现历史数据的差异经常表现在:

  • 覆盖年份不同,比如有的日线只能回溯到2016年,有的能到2000年;
  • Tick和K线混用,造成价格颗粒度不一致;
  • 重大事件时段的数据存在缺口,或者被平滑处理;
  • 报价中间价的合成规则不同,影响交易成本模拟。

这些隐性差异会直接改变策略绩效的统计分布。尤其对于趋势策略,短历史会把低波动环境“放大”,让策略看起来稳定得不像话。

回测结果的哪些方面会被扭曲?

历史长度变动后,我们观察到几个核心指标会明显改变:

  • 收益曲线形态:从平滑增长变成剧烈波动;
  • 最大回撤:往往成倍扩大;
  • 胜率:可能出现虚高或急剧下降;
  • 交易频率与滑点估计:失真后导致仓位计算错误。

对于中高频策略,过短的历史还容易造成信号只适应某一段微观结构,一旦流动性环境变化,策略直接失效。而这类问题,靠传统代码审查根本发现不了。

实战验证:分段回测让问题现形

我们现在有一套标准动作:对同一策略,用1年、3年、5年三种窗口分别回测,观察绩效稳定性。如果三个窗口下夏普比率或最大回撤差异悬殊,我们就会果断提醒客户,策略可能存在环境依赖。
具体落地时,我们会利用能提供长历史Tick的数据接口(比如AllTick API)拉取行情,再做窗口切片。代码结构如下:

import websocket
import json
import pandas as pd

data = []

def on_message(ws, message):
    msg = json.loads(message)
    data.append({
        "time": msg["ts"],
        "price": msg["price"],
        "volume": msg["volume"]
    })

ws = websocket.WebSocketApp(
    "wss://stream.alltick.co/ws",
    on_message=on_message
)
ws.run_forever()

df = pd.DataFrame(data)

# 按不同时间窗口切片回测
df_1y = df[df["time"] > "2026-06-01"]
df_3y = df[df["time"] > "2024-06-01"]

这样做之后,很多原本被忽略的市场结构问题就会浮出水面。

我们现在怎么看数据源的选择

对外汇API的选择,我们团队已经不再追求“数据有多快”,而是看重“历史有多厚、周期有多全”。只有拉长时间轴,策略才能经历利率决议、黑天鹅、流动性枯竭等完整场景。
如果一个策略只能在特定年份保持优秀,那它大概率只是在“背诵”那段行情,而不是真正理解了市场。这是我们帮客户规避回测陷阱的底线思维。

6da906de54126200a692ac9ee276ae81.jpg

评论