问题描述
解决方案
这是一个非常经典且令许多量化交易者头疼的问题。在量化领域,这种现象通常被称为“回测过拟合”或“幸存者偏差”,但也可能源于代码逻辑中的“未来函数”或对市场环境的设定不切实际。
以下是导致策略回测表现优异但实盘亏损的几个核心原因,以及针对 PTrade 平台的具体分析:
1. 未来函数 (Look-ahead Bias)
这是最常见也是最致命的错误。它指的是策略在决策时使用了当时实际上无法获取的数据(即使用了“未来的数据”)。
- 现象:例如在日线级别的回测中,你在计算当天的买入信号时,使用了当天的收盘价(Close),但实际上当天的收盘价在收盘前是未知的。如果你基于收盘价在当天开盘或盘中买入,就是使用了未来函数。
- PTrade 特性:
- 在
handle_data中获取日线数据时,如果回测频率是日线,data或get_history获取的是当天的收盘数据。如果在逻辑上认为这是“盘中”数据并进行交易,就会产生偏差。 - 检查方法:检查代码中是否在信号计算时使用了当天的
high、low、close,却在当天的open或盘中时间成交。
- 在
2. 过度拟合 (Overfitting)
策略参数被调整得完美适配历史数据,捕捉了历史上的“噪音”而非“规律”。
- 现象:策略有非常多的参数(如 MA5, MA10, RSI14, KDJ9...),并且你通过暴力枚举找到了在过去两年表现最好的一组参数。
- 后果:市场风格一旦切换,历史噪音不再重复,策略就会失效。
- 检查方法:样本外测试(Out-of-Sample Testing)。将数据分为训练集(如2018-2021)和测试集(2022-2023),如果两者表现差异巨大,则存在过拟合。
3. 交易成本与滑点设置不足 (Transaction Costs & Slippage)
回测通常假设你可以以理想价格成交,而忽略了实际交易中的摩擦成本。
- 滑点 (Slippage):实盘中,当你下单买入时,成交价往往比你看到的最新价要高(卖出时则更低),尤其是在资金量大或行情波动快时。
- 手续费:印花税、佣金、过户费。高频策略对成本极其敏感。
- PTrade 设置:
- 在
initialize中是否使用了set_commission设置真实的佣金费率? - 是否使用了
set_slippage或set_fixed_slippage来模拟滑点?如果回测中滑点设为0,实盘必然亏损。
- 在
4. 流动性陷阱与市场冲击 (Liquidity & Market Impact)
回测引擎通常假设“只要有成交量,我就能成交”,而忽略了你的订单对市场的影响。
- 现象:回测中你全仓买入一只成交量很小的股票,系统显示按收盘价全部成交。但在实盘中,你的大额买单会直接把价格拉高,导致你的平均持仓成本远高于回测价格。
- 涨跌停限制:PTrade 回测虽然会处理涨跌停,但有时策略逻辑可能在涨停板打开的瞬间买入,回测可能判定成交,实盘中却根本排不上队。
5. 生存偏差 (Survivorship Bias)
回测时只选择了当前还在上市的股票,忽略了历史上已经退市或暴雷的股票。
- 现象:你在2023年选出当前的“好股票”去回测2015年的数据。这天然剔除了那些在2015-2023年间退市的烂股。
- 后果:回测曲线会虚高,因为你实际上是在用“幸存者”进行测试。
- PTrade 建议:使用
set_universe动态调整股票池,或者使用指数成分股(如get_index_stocks)随时间动态变化的历史数据,而不是固定一个今天的股票列表。
6. 偷价与撮合机制差异
回测引擎的撮合逻辑是简化的,而实盘是复杂的。
- 偷价:在分钟级回测中,如果策略在 09:31 产生信号,回测可能允许你以 09:31 的
open价格成交。但在实盘中,当你收到 09:31 的数据并计算完信号下单时,价格可能已经变动,你只能以更差的价格成交。 - PTrade 机制:PTrade 的日线回测通常在 15:00 运行
handle_data,此时全天交易已结束。如果在日线回测中下单,通常是在下一个交易日开盘成交(除非使用了特定的盘后撮合逻辑)。如果你的理解是“当天成交”,就会产生巨大偏差。
7. 动态复权与除权除息
数据处理不当导致的价格失真。
- 现象:未处理分红派息导致股价“跳水”,触发止损信号;或者前复权数据掩盖了实际的价格波动区间。
- PTrade 建议:在使用
get_history或get_price时,注意fq(复权)参数的设置(通常使用 'pre' 前复权进行技术分析,但需注意实际账户资产是按不复权计算的)。
建议排查步骤
- 检查未来函数:确保策略逻辑中,产生信号的数据时间点严格早于下单时间点。
- 增加成本压力测试:在回测中将滑点(Slippage)设置为 0.2% 或更高,将手续费设为万分之三或更高,看策略是否依然盈利。
- 检查成交量限制:使用
set_volume_ratio限制单笔成交量不超过该分钟总成交量的 10% 或更低。 - 样本外测试:保留最近半年的数据不参与策略优化,仅用于验证。
- 对齐回测与实盘逻辑:在 PTrade 中,尽量使用分钟级回测(
frequency='minute')来模拟真实的盘中交易,因为日线回测的撮合逻辑与实盘差异较大。
Q&A 常见问题解答
Q: 什么是未来函数?如何避免?
A: 未来函数是指在策略回测时使用了当时不可预见的数据(如当天的收盘价、最高价等)。避免方法是严格遵守时间序列,只使用当前时间点之前的数据(如昨天收盘价、当前tick的最新价),并确保下单动作发生在信号产生之后。
Q: PTrade 中如何设置滑点以模拟真实环境?
A: 可以在 initialize 函数中使用 set_slippage 或 set_fixed_slippage。例如 set_slippage(PriceRelatedSlippage(0.002)) 表示设置 0.2% 的滑点,这能让回测结果更接近实盘。
Q: 为什么我的策略在小市值股票上回测特别好?
A: 小市值股票波动大且流动性差。回测往往忽略了流动性冲击,假设你能以当前价格买入任意数量。实盘中,大资金买入小盘股会推高价格(冲击成本),导致实际收益大幅缩水。建议在回测中剔除流动性过差的标的。
Q: 回测时应该使用前复权还是后复权数据?
A: 计算技术指标(如均线、MACD)通常使用前复权(fq='pre'),因为这能保持价格走势的连续性,消除分红配股造成的缺口。计算账户收益和持仓市值时,系统会自动处理实际价格。