四、回测核心概念:过拟合、样本内外、前视偏差、幸存者偏差、滑点、手续费等关键术语
量化回测的本质是用历史数据模拟策略表现,但历史数据本身并不“干净”——它包含噪声、幸存者、时间错位和交易摩擦。如果对这些核心概念缺乏系统认知,回测结果往往只是“精心装饰的随机数”。本章将逐一拆解回测中最关键的六个术语:过拟合、样本内外、前视偏差、幸存者偏差、滑点与手续费,并给出可操作的识别方法与规避策略。
4.1 过拟合:回测中最隐蔽的陷阱
核心原理
过拟合是指策略在历史数据上表现优异,但在新数据(未来数据)上表现显著退化。其本质是模型把历史噪声当作规律进行拟合。在量化回测中,过拟合通常表现为:参数微调后绩效剧烈波动、策略在样本内夏普比率极高但样本外迅速衰减、或者策略包含过多参数(如同时优化均线周期、止损比例、仓位系数等)。
判断方法
- 参数敏感性测试:对核心参数做±10%、±20%的扰动,观察绩效变化是否平滑。若参数从5改为6绩效暴跌,说明存在过拟合。
- 样本外验证:将数据按时间分为训练集(70%)和测试集(30%),只在训练集上优化参数,测试集仅用于最终验证。
- 交叉验证:采用滚动窗口方式,例如每2年训练、1年测试,滚动推进,观察各窗口绩效一致性。
案例拆解
某学员开发了一个双均线策略,原始参数为MA5/MA20。他在2018-2022年数据上反复优化,最终将参数调整为MA7/MA23,同时加入ATR止损(倍数2.5)和RSI过滤(阈值30/70)。回测年化收益从15%提升至28%,最大回撤从18%降至9%。然而在2023年实盘测试中,该策略年化收益仅4%,最大回撤达22%。复盘发现:MA7/MA23恰好拟合了2019-2020年特定波动节奏,而RSI过滤在趋势行情中频繁误杀信号。
总结复盘
过拟合的根源在于“用历史数据回答未来问题”。规避方法包括:限制参数数量(建议不超过3个)、采用正则化手段(如对参数施加惩罚)、以及最重要的——保持策略逻辑的经济学直觉。如果一个参数组合无法用市场微观结构或行为金融学解释,那么它大概率是过拟合产物。
4.2 样本内外划分:科学验证的基石
核心原理
样本内(In-sample)指用于模型训练和参数优化的数据区间;样本外(Out-of-sample)指用于验证模型泛化能力的数据区间。两者的核心区别在于:样本内数据被模型“看见”过,样本外数据则完全陌生。正确的划分方式能有效检验策略是否真正捕捉到了市场规律,而非仅仅记住了历史。
操作流程
- 数据分割:按时间顺序切分,严禁随机打乱。例如2015-2020年为样本内,2021-2023年为样本外。
- 样本内优化:在样本内进行参数扫描、因子筛选、模型训练。
- 样本外验证:将优化后的参数直接应用于样本外数据,记录绩效指标。
- 滚动再验证:将样本外数据并入训练集,重新优化,再向后滚动验证,重复多次。
| 划分方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 简单时间切分 | 简单直观 | 仅一次验证,偶然性大 | 初步验证 |
| 滚动窗口 | 多次验证,更稳健 | 计算量大 | 策略上线前最终验证 |
| K折交叉验证(时间序列版) | 充分利用数据 | 需处理时间依赖 | 因子研究阶段 |
4.3 前视偏差:未来数据泄漏
核心原理
前视偏差(Look-ahead Bias)指策略在计算信号时,使用了当时无法获取的未来信息。常见来源包括:使用未来财务数据(如未发布的季报)、使用当日收盘价计算次日开盘信号(但信号在开盘前已生成)、或使用复权因子时未考虑未来除权事件。前视偏差会让回测结果虚高,且难以通过参数调整修复。
判断与排查
- 时间戳检查:确保信号生成时间戳严格早于交易执行时间戳。
- 数据对齐检查:财务数据应使用公告日期而非报告期截止日期。
- 复权处理:使用后复权数据时,需确认复权因子是否基于历史已知信息。
# 错误示例:使用当日收盘价计算次日开盘信号
signal = (close > ma20) # 假设在t日收盘后计算
execute_price = open[t+1] # 次日开盘执行
# 问题:如果t日收盘价尚未确定,信号无法在t+1开盘前生成
# 正确做法:使用t-1日及之前的数据计算信号
signal = (close[t-1] > ma20[t-1])
execute_price = open[t] # 当日开盘执行
案例拆解
某CTA策略使用“当日最高价突破20日高点”作为开仓信号,并在当日收盘价成交。回测显示年化收益35%。但实盘中,信号在盘中触发时,收盘价尚未确定,实际成交价往往比回测价高0.5%-1%。经过排查,发现回测代码中使用了当日收盘价作为成交价,而信号计算也使用了当日最高价——这相当于用未来数据确认信号。修正后,策略年化收益降至18%,更接近实盘表现。
4.4 幸存者偏差:被隐藏的失败者
核心原理
幸存者偏差指回测数据集中仅包含当前仍在交易的标的,而剔除了已退市、被并购或破产的标的。这会导致回测收益被系统性高估,因为退市股票通常表现较差。例如,在美股回测中,若仅使用当前标普500成分股,会忽略2000年互联网泡沫中大量退市的科技股。
影响量化
| 市场 | 年均退市率 | 对年化收益的影响(估计) |
|---|---|---|
| A股 | 约1.5% | 高估0.5%-1.5% |
| 美股 | 约4% | 高估1%-3% |
| 港股 | 约2% | 高估0.8%-2% |
规避方法
- 使用全量数据:包含已退市标的,可通过交易所历史名录或专业数据商获取。
- 动态调整:在回测中模拟“标的进入/退出”机制,例如按市值排名动态调整股票池。
- 敏感性分析:对比“仅幸存者”与“全量数据”的回测差异,评估偏差程度。
4.5 滑点与手续费:从理论到现实的桥梁
核心原理
滑点是指理论成交价与实际成交价之间的差异,主要由市场流动性不足、订单规模过大、或市场冲击造成。手续费则包括佣金、印花税、过户费等。两者共同构成“交易成本”,是回测中最容易被低估的部分。一个在回测中忽略交易成本的策略,可能在实盘中完全无法盈利。
建模方法
- 固定滑点:按每笔交易固定金额或固定比例(如0.1%)计算。
- 流动性滑点:基于订单量占日均成交量的比例估算,例如订单量超过日均成交量1%时,滑点按平方根模型增加。
- 手续费分层:区分佣金(按成交额比例)、印花税(卖出时收取)、以及冲击成本(大单特有)。
# 交易成本建模示例
def calculate_trade_cost(price, shares, side, avg_daily_volume):
notional = price * shares
commission = max(notional * 0.0003, 5) # 万三佣金,最低5元
stamp_tax = notional * 0.001 if side == 'sell' else 0 # 印花税千一
# 流动性滑点:订单量占比越高,滑点越大
participation = shares / avg_daily_volume
slippage = price * (0.0005 + 0.002 * participation**2)
return commission + stamp_tax + slippage * shares
案例拆解
某高频策略在回测中设定单边手续费万二、无滑点,年化收益22%。实盘运行一个月后,实际年化收益仅9%。分析发现:该策略日均交易20次,每次买卖价差平均0.15%,远超回测假设。此外,由于订单规模占当日成交量5%,冲击成本显著。调整回测参数为“手续费万三+滑点0.1%”后,回测年化收益降至11%,与实盘接近。
总结复盘
交易成本建模应遵循“保守原则”——宁可高估成本,不可低估。建议在回测中至少设置两档成本:基准档(手续费万三+滑点0.05%)和压力档(手续费万五+滑点0.2%)。如果策略在压力档下仍能盈利,则实盘存活概率较高。