问题描述
解决方案
这是一个非常经典且普遍的量化交易问题。你遇到的情况通常被称为**“过拟合”(Overfitting)或“幸存者偏差”**。在量化领域,回测表现极其优秀但实盘亏损的现象非常常见。
以下是导致这种现象的核心原因分析,以及在 QMT 平台中避免这些问题的具体建议。
核心原因分析:为什么回测好但实盘差?
1. 过度拟合 (Overfitting)
这是最常见的原因。当你测试了大量策略或参数组合,并从中选出“效果最好”的一个时,你实际上是在筛选运气,而不是筛选逻辑。
- 现象:策略逻辑专门为了迎合过去某段特定的历史行情而设计。
- 本质:策略记住了历史数据的“噪声”,而不是市场的“规律”。
- 比喻:就像你为了通过一场已知的考试,背下了所有答案,但并没有学会解题方法。一旦题目(市场)稍微变化,你就无法应对。
2. 未来函数 (Look-ahead Bias)
在编写策略代码时,无意中使用了当时那个时间点无法获取的数据。
- 常见错误:
- 在日线回测中,使用当天的收盘价(Close)去决定当天的开盘买入。
- 使用了财务数据,但未考虑财报的公告日期(Announce Date),而是使用了报告期(Report Date)。例如,在1月1日使用了直到4月才发布的年报数据进行交易。
- QMT 特性:在 QMT 中,如果使用
ContextInfo.get_financial_data时未正确设置report_type或时间参数,容易引入此类偏差。
3. 交易成本与滑点低估
回测环境通常过于理想化,忽略了实际交易中的摩擦成本。
- 滑点:回测通常假设你能以当前 K 线的收盘价或开盘价完美成交。但在实盘中,买入价可能比你看到的更高,卖出价更低,尤其是在资金量大或流动性差的标的上。
- 手续费:忽略了印花税、佣金或最低收费标准。
- 冲击成本:你的买卖行为本身会推高或拉低股价,回测无法体现这一点。
4. 市场风格切换 (Regime Change)
- 现象:你的策略可能在过去两年的牛市或震荡市中表现很好,但实盘时市场进入了单边下跌或极度低波动环境。
- 本质:策略缺乏普适性,只适应特定市场风格。
解决方案与建议:如何在 QMT 中避免?
1. 样本外测试 (Out-of-Sample Testing)
不要用所有历史数据来优化参数。
- 做法:将数据分为“训练集”和“测试集”。例如,用 2018-2021 年的数据来研发策略和调整参数,然后用 2022-2023 年的数据进行验证。
- 原则:如果在测试集(从未见过的数据)上表现大幅下降,说明策略过拟合,必须放弃。
2. 参数敏感性分析 (Parameter Sensitivity)
检查策略对参数变化的敏感程度。
- 做法:如果你的策略在
MA(20)时表现极好,但在MA(19)或MA(21)时表现极差,说明该策略是“孤岛”策略,不可靠。 - 目标:优秀的策略应该在参数小幅波动时,绩效保持相对稳定(“参数高原”)。
3. 在 QMT 中设置真实的费率与滑点
在 QMT 的策略编辑器或代码中,必须设置严格的费率和滑点。
- 代码设置示例:
def init(ContextInfo): # 设置滑点:例如设置为 0.02 元,或者按比例设置 # 1:固定值,2:比例 ContextInfo.set_slippage(1, 0.02) # 设置佣金:例如万分之三,最低5元 # [买入印花税, 卖出印花税, 开仓佣金, 平仓佣金, 平今佣金, 最低佣金] # 注意:股票卖出有印花税(目前0.05%),买入无 ContextInfo.set_commission(0, [0, 0.0005, 0.0003, 0.0003, 0, 5])
4. 严格检查未来函数
- 财务数据:在 QMT 中获取财务数据时,务必注意
report_type参数。- 错误做法:默认或不注意时间点。
- 正确做法:确保使用
announce_time(公告日)作为基准,或者在回测逻辑中加入时间滞后。
- 信号触发:
- 如果是日线策略,尽量使用
ContextInfo.do_order()在次日开盘成交,或者在代码逻辑中确保使用的是上一根 K 线的数据来计算信号,并在当前 K 线(或次日)执行。 - 使用
ContextInfo.get_market_data_ex获取历史数据时,注意end_time不要包含未来数据。
- 如果是日线策略,尽量使用
5. 模拟盘验证 (Paper Trading)
在实盘投入资金前,先在 QMT 的模拟交易模式下运行一段时间(例如 1-3 个月)。
- 目的:观察实盘信号是否与回测逻辑一致,检查是否有未预料到的撤单、废单或数据延迟问题。
6. 逻辑简化 (Occam's Razor)
- 原则:越复杂的策略(规则越多、参数越多),过拟合的概率越大。
- 建议:尽量减少过滤条件。一个依靠 10 个条件才盈利的策略,远不如一个依靠 2 个核心逻辑盈利的策略稳健。
Q&A
Q: 如何判断我的策略是否使用了未来函数?
A: 最简单的方法是查看成交明细。如果你的策略在日线回测中,能够以当天的最低价买入,或者在最高价卖出,或者在涨停板打开前买入,这通常意味着使用了未来函数。另外,检查代码中是否使用了类似 close (当根K线收盘价) 来决定当根K线的交易。
Q: QMT回测时,撮合机制是怎样的?
A: QMT 回测通常基于 K 线撮合。如果是日线回测,默认是在信号触发的次日开盘撮合(除非使用了 quickTrade 参数)。如果设置为收盘价成交,而你的信号依赖于收盘价,这就构成了逻辑闭环的未来函数。建议设置为“次日开盘价”撮合,这更符合实际。
Q: 为什么我的策略在小市值股票上回测很好,实盘买不进去?
A: 这是流动性陷阱。回测假设你可以无限量成交。对于小市值或低流动性股票,实盘中你的买单可能会推高价格(冲击成本),或者根本没有足够的卖单(流动性枯竭)。建议在策略中加入流动性过滤,例如剔除日成交额小于 2000 万的股票。
Q: 什么是“参数高原”?
A: 参数高原是指在参数的一定范围内,策略的绩效表现都比较好且稳定。例如,均线策略在 18日、19日、20日、21日、22日均线都能盈利,这就形成了一个高原。如果只有 20日盈利,其他都亏损,那就是“参数尖峰”,这种策略实盘极易失效。