如何判断一个策略只是过拟合?

1 天前
 matters

“回测收益高”是最容易骗人的指标。一个策略回测年化 50%,最大回撤 10%,Sharpe 2+,看起来非常漂亮。但如果把它拿到真实市场里跑,可能很快就发现完全不是那么回事。想分享一下我目前判断策略是否过拟合的一些方法。

1. 先把训练集和测试集分开

比如我有 2018 ~ 2025 年的历史数据,不能直接拿全部数据调参数,然后说这个策略在 2018 ~ 2025 年表现很好。因为策略参数本身就是根据这些数据调出来的。我一般会先分成:

训练集用来开发策略和调参数。验证集用来检查策略是不是还能工作。测试集最好完全不碰,最后才拿出来做一次真正的 out-of-sample test 。 如果一个策略在训练集表现特别好,但到了验证集和测试集突然失效,我一般就会开始怀疑过拟合。

2. 参数稍微变化,策略就完全失效?

比如我做一个均线策略:

回测结果很好。 然后我把参数稍微改一下:

如果结果都差不多,我会觉得这个策略可能找到了某种比较稳定的市场规律。但如果:

这种情况我就会比较警惕。因为这很可能不是策略本身有效,而是参数刚好“撞上”了历史数据中的某个特殊模式。所以我现在倾向看参数附近的 performance surface 。一个真正有一定 robustness 的策略,通常不会只有一个孤立的最优点,而应该是一片相对稳定的区域。

3. 换一个市场还能不能跑?

比如我做的是一个美股策略。如果策略只在 SPY 上表现很好,我会尝试 SPY 、QQQ 、IWM 、DIA ,甚至换成其他市场。 当然,不是说一个策略必须在所有市场都赚钱。如果策略背后的逻辑是合理的,那么通常应该能够解释, 为什么它在这个市场有效。 如果只是“我在 SPY 上测试了 1000 组参数,最后发现这一组最好”,那我会觉得这个结果的可信度比较低。

4. 用 Monte Carlo 看看是不是太依赖某几笔交易

有些策略回测收益非常高,但仔细看发现,总收益的 70%来自 3 笔交易。这种策略我也会比较担心。我一般会尝试对交易顺序、收益序列做 Monte Carlo simulation 。例如原来的交易结果是:

我可以随机打乱交易顺序,重复跑很多次。然后观察:

如果随机后的结果和原始结果差别非常大,说明策略可能比较依赖特定的交易顺序。这不一定意味着策略过拟合,但至少说明风险可能比回测结果看起来更高。

5. 不要只看收益率

除了看年化收益率、夏普比率、最大回撤,我还会看:

尤其是交易次数。如果一个策略回测了 10 年,最后只有 20 笔交易,然后 CAGR 50%。我会觉得这个结果的统计意义可能不太够,因为样本太少了。反之,如果一个策略 10 年做了几万笔交易,虽然每笔平均收益只有 0.05%,但经过手续费和滑点之后还能赚钱,我反而会觉得这种结果可能更值得研究。

6. 实盘数据和回测数据一定要尽量一致

很多策略回测使用的是日 K ,但真正交易的时候却需要 Tick 级数据和订单簿。这时候很容易出现一个问题:回测里看起来你是在某个价格成交的,但真实市场里根本没有办法以那个价格成交。尤其是

这些策略对数据质量和时间精度都比较敏感。我自己在测试实时策略的时候,会用上实时行情,主要是想把策略回测和实时行情环境尽量连接起来:


import json
import websocket

API_KEY = "YOUR_API_KEY"

def on_message(ws, message):
    data = json.loads(message)
    print(data)

def on_open(ws):
    print("WebSocket connected")

    # 根据 API 文档发送订阅请求
    subscribe_message = {
        "action": "subscribe",
        "symbols": ["AAPL"]
    }

    ws.send(json.dumps(subscribe_message))

ws = websocket.WebSocketApp(
    f"wss://quote.alltick.co/quote-stock-b-ws-api?token={API_KEY}",
    on_open=on_open,
    on_message=on_message
)

ws.run_forever()

实战过程中要确保 [回测数据 → 策略信号 → 实时行情 → 模拟成交 → 实际交易] 这几个环节的数据定义尽量一致。否则可能出现一种情况:回测策略赚钱的原因,是因为你在历史数据里拥有“未来信息”。

7. Walk-forward Test

这是我目前比较喜欢的一种回测方法。 假设我有 2018 ~ 2025 年数据。 可以这样滚动测试:

Train: 2018-2020 Test : 2021

Train: 2019-2021 Test : 2022

Train: 2020-2022 Test : 2023

Train: 2021-2023 Test : 2024

Train: 2022-2024 Test : 2025

每次只使用过去的数据训练和优化参数,然后拿未来的一段数据测试。这样可以模拟真实交易过程中:我只能看到过去的数据,然后决定未来怎么交易。如果一个策略在多个 walk-forward window 里都表现不错,我会认为它的 robustness 比单次回测更高。

8. 我自己现在判断过拟合,大概会看这几个信号

如果一个策略出现下面这些情况,我会提高警惕:

  1. 参数稍微变化,收益就断崖式下降

  2. 训练集很好,测试集明显失效

  3. 换一个相似市场就完全不能用

  4. 收益高度依赖少数几笔交易

  5. 回测交易次数太少

  6. 加入手续费和滑点后直接亏损

  7. 使用更高频的数据后表现明显恶化

  8. 策略逻辑很难解释为什么有效

  9. 参数是经过大量试错后“挑”出来的

  10. 不同时间窗口测试结果差异非常大

尤其是第 9 个,我觉得很容易被忽略。如果我尝试了 500 个策略,最后只挑一个表现最好的出来,然后说:我的策略年化 100%。这个结果本身其实没有太大意义,因为我实际上是在做一种隐性的 multiple testing 。如果测试的策略足够多,总会有一些策略“碰巧”在历史数据里表现特别好。

362 次点击
所在节点    投资
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1230983

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX