1.1 上篇 · 偷"信息"——数据与技术层作弊
本篇聚焦"偷信息"——数据与技术层作弊。一个前提贯穿全文:任何一个漂亮的回测曲线,在被独立证据证明为真之前,都应默认它是假的。 所有数字均为特定历史区间的客观回测记录,仅供研究参考,不构成任何投资建议。
上篇 · 偷"信息"——数据与技术层作弊
作弊的最底层来源,潜伏在数据的时序里,也潜伏在统计自欺的选择偏差里。它的本质只有一句话:决策时使用了当时尚不存在的信息。
一、未来函数:从"偷价格"到"偷一切"
未来函数是量化作弊中最广为人知、却也最被低估其变体的一类。它的本质只有一个:决策时使用了当时尚不存在的信息。但这个"信息"远不止收盘价。
- (一)同 bar 信号与成交(SAME_BAR):一个日频策略在 09:30 用"当天收盘价"算涨幅、按"当天开盘价"成交。代码没显式请求未来日期,却同时用到了同一根 K 线两端、在决策时点都不可得的信息。平台的 avoid_future_data 能拦跨日期调用,却拦不住同 bar——因为它不涉及未来日期,只涉及同一 bar 内不可得的数据。
- (二)直接读取未来日期(FUTURE_RET):比同 bar 更粗暴的是 get_price(..., end_date=今天+N天)。任何"用未来区间收益反推当前选择"的构造都是它的变体,把"读未来价格"换成"用未来收益造标签再训练模型",隐蔽性高一个量级。
- (三)财务数据的"报告期—披露期"错位(FUTURE_FUNDAMENTAL):用 statDate='2023Q1' 取数,等于让策略在 1 月 1 日就知道 3 月 31 日才结束、可能 4 月底才披露的数据。用"公告日"而非"报告期"作为截面时钟,是财务因子回测不可妥协的底线。
- (四)股票名称里的"未来 ST":平台的名称字段返回的是当前名称而非历史当日名称。2023 年才被 ST 的股票,在 2019 年的截面里已显示为 ST,策略"恰好"提前把它过滤掉了。正确做法是用 get_extras('is_st', end_date=T-1) 取按日历史。它危险在于:代码看着像风控,实为偷看未来才发生的风险事件。
二、前视偏差:那些"回测当时不存在"的东西
前视偏差与未来函数共享同一个逻辑内核——使用了决策时点不可得的信息——但它的来源不在数据调用,而在回测环境本身的设定。
- (一)幸存者偏差:股票池是写死的静态列表、或用 get_all_securities() 不传历史日期时,回测宇宙系统性缺失了那些曾存在、却因退市或长期停牌而消失的标的。价值筛选策略(低 PB、低负债、高股息)恰恰最容易选中退市股——剔除它们,等于删除了"最可能造成永久资本损失"的样本。
- (二)指数成分股的时点错配:2026 年回测却选用 2020—2025 涨幅最大的黄金 ETF、纳指 ETF 作轮动池,本质是用未来才知道的赢家构造过去的投资宇宙。十年前并不存在"它们将继续领涨"的先验知识。
- (三)回测起点的刻意选择:起点恰在牛市底部、又在顶部结束,"年化 40%、回撤 8%"可能只是区间选择的产物。它不涉及任何一行错误代码,却与未来函数同等致命。
三、过拟合:从"参数挑峰"到"条件空间膨胀"
- (一)参数挑峰:核心参数在回测里被系统性扫描,最终值恰好落在一条凸型敏感性曲线的峰顶。25 日动量在 20—30 天里"恰好"最优、两侧迅速衰减——任何被固定的数字,都很难排除"它在历史上跑得最好"这一选择机制。
- (二)条件空间膨胀:从"低 PE + 深度偏离"两个条件,膨胀到六种形态、每种 5—10 个硬条件、外加板型与加速过滤,构成数百个可调旋钮。在这个空间里,找到一个"恰好有效"的组合几乎是统计必然。一个被硬编码为永远为真的伪条件(如 volume_pulse=False),不筛选任何东西,只让列表显得更丰富——这是"为了复杂而复杂"的明确信号。
- (三)PBO 的缺席:回测过拟合概率(PBO)直接度量"样本内最优配置在样本外跌破中位数的概率"。一个从未算过 PBO 的策略,无法回答"我的优势里有多少是选择偏差"。
- (四)辛普森悖论:单变量分析里"有效"的买入优化方向,在多因子联合环境里可能完全逆转。局部有效的条件在全局可能负向交互,回测里的"联合有效"或许只是特定参数下的偶然。
