如果样本中没有退市股票,收益曲线会被幸存者偏差抬高。如果计算时刻 \(t\) 的特征使用了 \(t\) 之后才发布的数据,模型已经获得未来信息。此时再调整学习率、增加树的深度、替换神经网络,都没有意义。
回测不是事实复原。回测是一个由数据、时间戳、清洗规则、特征工程、模型参数和执行假设共同生成的计算过程。任何一个环节泄露未来信息,最终净值都可能失真。
选择数据与算法,不能只看预测准确率。需要先建立数据质量的量化约束,再检查时间边界,最后判断模型是否在训练集之外仍然成立。顺序不能反过来。
一、先检查数据,而不是先挑算法
数据质量决定了策略研究的上限。
算法可以降低部分噪声,也可以拟合复杂的非线性关系。但算法无法自动修复错误时间戳、缺失的退市样本和不一致的复权规则。把脏数据输入更复杂的模型,通常只会得到更复杂的错误。
量化数据至少需要从六个维度评估:
- 完整性:关键字段是否缺失。包括开高低收、成交量、财务指标、行业分类、上市状态等。
- 准确性:数值是否符合业务逻辑。最高价不能低于最低价,成交量不能因单位变化而无提示地放大。
- 一致性:不同数据源对同一证券、同一交易日的定义是否一致。
- 唯一性:同一证券、同一时间、同一数据类型是否重复记录。
- 时效性:数据在策略运行时是否已经可获得。
- 规范性:证券代码、日期、货币单位、复权方式、缺失值标识是否统一。
这六个维度不是文档里的装饰性指标。它们会直接进入数据管道。
例如,准确性和一致性经常被混用。某个数据源记录的价格本身可能没有错误,但如果它使用后复权,而另一个数据源使用前复权,两者拼接后仍然会产生结构性不一致。模型看到的不是正常收益,而是复权口径变化。
唯一性也不是简单的去重。交易数据中的重复记录可能来自:
1. 同一交易日重复导入;
2. 不同数据源拼接时主键设计错误;
3. 分钟数据与日线数据同时写入同一张表;
4. 合约换月后沿用旧合约代码;
5. 修订数据没有覆盖旧版本,而是追加了一条新记录。
如果主键只有证券代码和日期,期货连续合约、不同市场、不同数据版本都可能被错误合并。数据表能够正常读取,不代表数据可以用于回测。
用数据质量指标替代人工浏览
人工打开几张行情表,无法验证数据质量。需要对数据集建立可重复执行的检查。
一个基本的质量报告可以包含以下字段:
| 维度 | 检查对象 | 典型异常 | 对回测的影响 |
|---|---|---|---|
| 完整性 | 行情、财务、分类字段 | 某阶段连续缺失 | 样本数量变化,因子覆盖率失真 |
| 准确性 | 价格、成交量、财务数值 | 负成交量、价格跳变、单位错误 | 收益率和波动率异常 |
| 一致性 | 多源行情、复权数据 | 日期、单位、复权口径不一致 | 拼接后出现虚假跳点 |
| 唯一性 | 证券代码、交易日、字段版本 | 重复记录、重复快照 | 单日权重被重复计算 |
| 时效性 | 发布时间、入库时间 | 使用未来修订值 | 产生前视偏差 |
| 规范性 | 代码、日期、缺失值 | 格式混杂、空值含义不同 | 连接失败或错误填充 |
指标需要保留时间维度。不能只计算全样本平均缺失率。
全样本缺失率为零,不代表每个交易日都可用。一个数据源可能在回测起点附近完整,之后出现连续缺失;也可能只在市场波动阶段缺失。后者对策略的影响更大,因为缺失不再是随机事件。
对于每个字段,至少应保留:
- 按交易日计算的缺失数量;
- 按证券计算的覆盖区间;
- 首次出现时间和最后出现时间;
- 数据修订时间;
- 数据版本;
- 与其他来源的差异记录;
- 异常值处理方式。
这一步的目的不是把数据清洗成一个看起来平滑的表,而是保留数据生成过程。过度清洗同样会破坏回测真实性。
例如,价格跳变可能来自错误,也可能来自分红、拆股、合约切换或真实停牌后的复牌。统一使用中位数填补,会把不同事件压成同一种异常。模型得到的结果无法解释,后续也无法定位。
数据质量不是一个布尔值。它是按字段、按证券、按时间变化的状态变量。
股票与期货不能使用同一套数据假设
股票数据通常围绕证券代码、交易日、公司行为和上市状态组织。期货数据还需要处理合约到期、换月、主力合约、交割月、连续合约构造和基差变化。
连续合约不是天然存在的市场对象。它是由多个实际合约拼接而成的研究序列。拼接规则不同,价格序列就不同。
常见的连续合约构造方法包括:
- 直接拼接;
- 按价差调整;
- 按比例调整;
- 按成交量或持仓量换月;
- 按固定日期换月。
这些方法对应不同的收益解释。直接拼接可能产生非交易性跳点。比例调整会改变历史价格尺度。价差调整会影响长期收益和波动估计。按持仓量换月则依赖持仓数据的时间可用性。
因此,选择期货数据时,需要把合约切换规则写入研究配置,而不是隐藏在数据供应商的接口里。否则策略参数和数据构造方法会被绑定,无法判断收益究竟来自交易逻辑,还是来自合约拼接。
二、幸存者偏差:回测里最容易被忽略的样本错误
幸存者偏差的定义很简单:只保留当前仍然存在、仍然活跃或仍然表现良好的样本,再用这些样本评估过去。
在股票回测中,典型错误是使用当前上市股票列表回溯历史。已经退市、暂停交易、被吸收合并或长期失去流动性的证券被排除。策略只在“幸存者”上运行,历史样本被系统性筛选。
这不是普通的数据缺失。数据缺失可以通过补充记录解决。幸存者偏差改变了样本空间。
如果某些失败样本在过去曾经属于可交易股票,那么回测必须保留它们从上市到退出市场之间的有效记录。退出之后不再产生交易数据,但不能把它们从历史截面中删除。
研究资料显示,未修正的幸存者偏差可能导致策略回测结果虚高超过30%,甚至使年化收益虚增超过20%。这类数字不意味着每个策略都会出现同等幅度的错误,但足以说明:样本清洗不是边缘问题。
关键不是保存退市股票,而是恢复当时的股票池
只把退市股票补回数据库,仍然不够。
策略通常不是对全部证券无条件交易,而是先构造某个时点的股票池,再进行排序、打分和选取。股票池必须使用当时可见的上市状态、行业分类、流动性和价格数据。
例如,某个因子策略在每月最后一个交易日选取流动性最高的股票。如果使用当前行业分类回溯,过去的行业归属可能被后续重分类覆盖。若使用最终修订后的财务数据,过去的财务状态也会被改变。
需要区分三种时间:
1. 事件发生时间:财报对应的会计期间或公司行为发生时间;
2. 披露时间:市场首次可以看到该信息的时间;
3. 入库时间:数据进入研究数据库的时间。
回测特征只能使用第三种时间之前已经公开的内容。更严格地说,应以市场可获得时间为边界,而不是以数据库写入时间为边界。
数据库晚一天入库,不代表市场晚一天知道。数据库提前写入,也不代表历史上提前可用。
幸存者偏差的排查方法
可以从以下路径开始。
1. 检查证券池是否由当前列表生成
如果代码逻辑类似于“获取当前全部股票代码,再拉取历史行情”,默认存在风险。当前列表天然包含未来信息。
股票池需要按日期生成。每个交易日对应一个可交易证券集合,并记录纳入与剔除原因。
2. 检查退市证券的最后有效日期
退市证券不能无限延长到回测结束,也不能直接删掉全部记录。需要保留其历史有效区间,并在退出市场后停止交易。
3. 检查上市日期
新股不能在上市前进入历史横截面。上市首日是否纳入,还要与策略的交易规则一致。
4. 检查公司行为
拆股、分红、配股、合并和更名都会影响价格连续性、证券标识和收益计算。只对价格做复权,不处理证券身份变更,可能造成重复证券或断裂样本。
5. 检查分类数据的历史版本
行业、地域、指数成分和风险分类可能发生变化。使用当前分类回溯,会把未来状态带入过去。
为什么简单增加样本不能修复偏差
幸存者偏差不是样本数量太少,而是样本选择机制错误。
增加更多当前仍然存在的股票,只会扩大幸存者集合。提高模型复杂度也不会改变样本分布。即使交叉验证结果稳定,验证集仍然来自同一组被筛选过的证券,偏差依旧存在。
这也是量化研究中常见的误判:把统计稳定性当成数据真实性。模型可能在错误样本上稳定地输出错误结论。
三、前视偏差:每一个字段都必须有可用时间
前视偏差通常不是一个明显的未来价格字段。更常见的形式是,某个字段在历史数据中存在,但在当时尚未公开。
计算时刻 \(t\) 的特征时,只能使用 \(t-1\) 及之前已经可获得的数据。对于财务数据,还要考虑发布延迟。常用的参考标准约为45天,但实际延迟取决于报告类型、公司披露时间和数据供应商的时间定义。
如果使用财报期末日期代替披露日期,模型就会在报告发布之前看到财务数据。这个错误通常不会让代码报错。它只会让回测更好看。
事件日期、披露日期和交易日期不能混为一谈
以财务指标为例:
- 财务报告期可能结束于季度末;
- 公司可能在数周后发布报告;
- 数据供应商可能在发布后再次清洗和修订;
- 策略可能在下一个交易日开盘执行。
这几个时间点相差数周。若特征工程只保留一个日期字段,就无法判断数据是否泄露。
建议至少保留:
| 时间字段 | 含义 | 在回测中的作用 |
|---|---|---|
| 报告期末 | 指标所对应的会计期间结束时间 | 描述数据内容 |
| 首次披露时间 | 市场首次可能获得数据的时间 | 确定可用边界 |
| 数据入库时间 | 供应商或系统写入时间 | 追踪管道过程 |
| 修订时间 | 数据发生更新的时间 | 处理版本和重述 |
| 交易执行时间 | 策略下单或成交时间 | 对齐信号与收益 |
策略信号的生成时间和成交时间也必须分离。使用收盘价生成信号,再假设同一收盘价成交,通常已经隐含了不可执行的信息。若信号在收盘后才能完成计算,最早成交时间应当是下一交易时段,具体还要结合市场规则和执行模型。
三种常见的时间泄露
使用未来窗口计算当前特征
滚动均值、波动率、最高价和最低价都可能泄露未来。
例如,计算某日波动率时,如果窗口包含该日收盘之后的数据,或者使用居中滚动窗口,特征就不再属于实时信息集。代码逻辑可能只是一个参数设置错误。
使用全样本统计量填充历史数据
标准化、缺失值填充和异常值截断都可能使用未来信息。
若用全样本均值和标准差标准化历史数据,未来阶段的分布已经进入过去。更合理的方式是使用滚动窗口、扩展窗口或训练集统计量,并明确每次更新的时间。
异常值截断也一样。用全历史分位数定义上下界,会把未来分布带入早期样本。市场结构发生变化时,这个误差尤其明显。
使用最终修订数据
财务数据、指数成分、评级和分类信息都可能被修订。研究数据库中保留的最终值,不一定是当时市场看到的初始值。
如果研究目标是模拟历史交易,需要使用点时数据。点时数据记录的是每个时间点可以观察到的状态,而不是今天回头看得到的最终状态。
时间戳不是数据表的附属字段。它决定了一个特征是否具有交易资格。
前视偏差排查的执行顺序
可以按照以下顺序排查:
1. 为每个原始字段定义可用时间;
2. 为每个衍生特征记录所依赖的字段;
3. 将特征可用时间设置为依赖字段可用时间的最大值;
4. 将信号生成时间与订单执行时间分离;
5. 对滚动窗口检查右端点是否越过当前时刻;
6. 对训练、验证和测试数据执行时间切分;
7. 用延迟一个交易日或更长时间的版本重新回测;
8. 对结果差异进行归因。
最后一步很有价值。如果策略在数据延迟后完全失效,原始结果很可能依赖了时间边界上的隐性泄露。
四、模型选择:先判断偏差和方差,再谈复杂度
算法选择不是模型竞赛。
线性模型、树模型、核方法和深度学习模型解决的不是同一个问题。它们对样本量、特征分布、缺失值、非线性关系和噪声水平的要求不同。没有数据诊断,直接比较年化收益,得到的只是一个混合了数据处理差异的结果。
模型诊断的核心问题有两个:
- 当前模型是否存在明显偏差;
- 当前模型是否对训练样本过度拟合。
偏差高,说明模型表达能力不足,或者特征本身没有提供足够信息。方差高,说明模型对样本扰动敏感,可能记住了噪声、时间段特征或证券身份。
用学习曲线观察样本利用效率
学习曲线比较不同训练样本量下的训练误差和验证误差。
如果训练误差和验证误差都较高,且随着样本量增加仍然接近,通常表现为偏差驱动。此时增加模型复杂度可能有帮助,但也可能只是暴露数据本身缺少有效信号。
如果训练误差很低,验证误差明显较高,且两者长期无法收敛,通常表现为方差驱动。增加参数、增加树深或扩展神经网络规模,往往会加重问题。
验证曲线则观察某个超参数变化对训练误差和验证误差的影响。例如树模型的深度、正则化系数、最小叶节点样本数,或者神经网络的权重衰减系数。
可以使用与机器学习框架配合的可视化诊断工具生成学习曲线和验证曲线。工具本身不提供因果结论。曲线只描述模型在指定切分方式下的表现,切分方式错误时,诊断同样错误。
金融时间序列不适合随机打乱交叉验证
普通分类任务经常随机打乱样本,再进行交叉验证。金融时间序列具有时间依赖,这样做可能把未来样本分配给训练集,把过去样本分配给验证集。
问题不只在标签重叠。特征窗口、持有期、行业状态和宏观变量都可能跨越训练与验证边界。
更合理的方式包括:
- 按时间顺序切分训练集、验证集和测试集;
- 使用滚动训练窗口;
- 使用扩展训练窗口;
- 在标签持有期存在重叠时设置间隔;
- 保留一段完全不参与调参的最终测试期。
测试集只能使用一次进行最终评估。若反复查看测试结果并修改参数,测试集就已经成为验证集。此后需要重新划分一个未使用的数据区间。
过拟合不只发生在模型内部
策略研究的过拟合来源包括:
- 选择了表现最好的因子;
- 选择了最优回测区间;
- 反复尝试不同的止损参数;
- 在多个市场、多个频率、多个资产上搜索;
- 删除表现差的证券或年份;
- 根据回测结果决定数据清洗方式;
- 对异常交易日进行事后处理;
- 先看测试结果,再确定模型结构。
即使每次单独测试都没有使用复杂算法,累计搜索空间仍然可能很大。最终被选中的策略,可能只是大量试验中的随机极值。
因此,研究日志需要记录失败实验。只保存最终版本,会掩盖模型选择过程中的多重比较问题。
五、比较不同算法时,比较的必须是同一数据协议
算法比较经常被错误执行。
模型甲使用清洗后的数据,模型乙使用另一套复权规则;模型甲按时间切分,模型乙随机交叉验证;模型甲考虑交易成本,模型乙不考虑。最后比较两个收益率,结论没有统计意义。
算法比较至少需要固定以下条件:
- 相同的证券池;
- 相同的数据版本;
- 相同的时间区间;
- 相同的特征定义;
- 相同的标签定义;
- 相同的训练、验证和测试边界;
- 相同的缺失值处理规则;
- 相同的交易成本和滑点假设;
- 相同的调参预算;
- 相同的评价指标。
评价指标也不能只看准确率。
对于交易模型,需要分别观察:
- 收益分布;
- 波动率;
- 最大回撤;
- 换手率;
- 交易成本敏感性;
- 不同市场阶段的表现;
- 不同证券和行业的贡献;
- 预测值与未来收益的相关性;
- 样本外表现;
- 参数扰动后的稳定性。
如果模型预测的是连续收益,均方误差较低不代表策略收益较高。极端收益往往在均方误差中占据较大权重,但交易组合可能只利用排序信息。此时排序相关性、分组收益和组合换手率更接近策略目标。
如果模型预测涨跌方向,方向准确率也不是充分指标。预测准确率可能接近,但错误方向时的损失大小不同。还需要观察概率校准、分层收益和仓位映射。
低复杂度模型应当作为基准
基准模型不是为了追求最高收益,而是为了判断复杂模型到底增加了什么。
可以设置几个基线:
1. 历史均值或滚动均值;
2. 线性回归或逻辑回归;
3. 简单的均值回归规则;
4. 基于单因子排序的组合;
5. 不带复杂特征工程的树模型。
如果深度模型只比线性基准略好,却需要更多数据、更高延迟和更复杂的部署环境,优势可能不足以覆盖系统成本。
如果复杂模型的优势只出现在某一个短区间,也需要检查是否由局部分布变化驱动。模型在训练集上学习到的关系,不一定能够跨越市场制度变化。
六、AI模型部署:量化不是免费性能
当模型从研究环境进入部署环境,计算资源、内存占用和响应延迟会成为约束。
训练后量化是常见的部署手段。它通常分为:
- 仅权重量化;
- 权重与激活同时量化。
前者主要减少模型参数存储和部分计算负担。后者进一步处理激活值,但对校准数据、算子支持和数值误差更敏感。
常见的量化技术包括先进权重量化方法、GPTQ和SmoothQuant。它们解决的问题不同,不能只按名称排序。
仅权重量化
仅权重量化适合先降低模型存储成本。模型权重从高精度表示转换为较低位宽,推理时再配合相应算子执行。
优点是改造路径相对清晰,对激活分布的干预较少。限制是激活仍可能占据较多计算资源,端到端延迟不一定同步下降。
如果模型部署瓶颈在显存或模型加载速度,权重量化可能有效。如果瓶颈在激活计算、内存访问或算子调度,仅压缩权重可能不足。
权重与激活同时量化
同时量化权重和激活,可以进一步降低计算和内存访问压力,但对异常值更敏感。
深度模型中的激活分布通常不是理想的正态分布。少量大值可能占据较宽的量化范围,导致大多数普通值的有效精度下降。SmoothQuant一类方法的目标,就是在权重与激活之间重新分配量化难度,降低激活异常值的影响。
部署评估不能只比较模型大小。至少需要同时记录:
- 模型存储占用;
- 峰值内存;
- 单次推理延迟;
- 吞吐量;
- 数值误差;
- 预测结果变化;
- 交易信号变化;
- 端到端执行延迟。
对于低延迟交易,单次推理时间不是唯一指标。数据读取、特征计算、进程通信、订单路由和风控检查都可能成为瓶颈。模型压缩后,如果特征工程仍然占据主要时间,系统整体不会获得明显改善。
量化误差需要在交易层验证
模型指标下降很小,不代表交易结果影响很小。
假设量化后均方误差变化有限,但模型输出在排序边界附近发生了改变。原本排名靠前的证券可能被替换,组合持仓和换手率就会变化。对于依赖横截面排序的策略,排名稳定性可能比平均误差更重要。
可以分别检查:
- 量化前后预测值的相关性;
- 量化前后横截面排名的一致性;
- Top-N组合的重合度;
- 信号翻转比例;
- 交易次数变化;
- 组合收益和回撤变化;
- 不同市场阶段的差异。
量化校准数据也必须避免泄露。用于估计激活范围的样本应当来自可用训练数据或独立校准集,不能把测试阶段数据无条件加入校准过程。
七、参数优化:不要把回测结果当成唯一目标函数
参数优化最容易制造虚假确定性。
策略通常有多个参数:窗口长度、阈值、正则化系数、树深、学习率、持有期、换月规则。每增加一个可调参数,搜索空间都会扩大。最终得到的最优点,可能只是样本噪声的局部峰值。
参数曲面比单点最优值更重要。
如果参数从10调整到11,策略结果大幅下降,说明模型对参数敏感。参数从10到20都保持相近表现,说明存在较宽的稳定区域。后者通常更适合部署,因为真实数据和交易执行不会完全复现回测条件。
可以使用以下方式检查参数稳定性:
1. 对关键参数进行网格扰动;
2. 观察相邻参数的收益、回撤和换手率;
3. 在不同时间区间重复评估;
4. 在不同证券池和市场阶段重复评估;
5. 延迟信号或增加交易成本后重新计算;
6. 记录最差区间,而不是只记录平均结果。
不要只优化年化收益。可以把回撤、换手率、交易成本、样本外损失和参数敏感性一起纳入目标函数。目标函数越接近真实部署约束,回测结果越不容易脱离运行环境。
数据、模型和执行需要分别做压力测试
一个策略可能在数据层稳定,在模型层不稳定;也可能模型预测稳定,但交易成本一增加就失效。
因此需要拆分压力测试:
数据层:
- 删除部分低质量字段;
- 增加缺失值;
- 延迟财务数据;
- 更换数据源;
- 改变复权或合约拼接方式;
- 排除异常交易日。
模型层:
- 扰动超参数;
- 降低模型复杂度;
- 改变训练窗口;
- 更换随机种子;
- 重新划分验证区间;
- 使用不同正则化强度。
执行层:
- 增加滑点;
- 提高交易成本;
- 延迟成交;
- 限制成交量占比;
- 限制单品种持仓;
- 处理无法成交和停牌情况。
三层结果需要分开记录。否则当净值下降时,只能知道策略失效,却不知道失效来自数据、模型还是执行假设。
八、一个可执行的审查流程
选择数据与算法时,可以按以下顺序执行。顺序本身是约束,不能把算法搜索提前。
第一步:定义信息集
写清楚每个交易时刻可以使用哪些字段。对财务数据、分类数据、指数成分和公司行为数据分别记录发布日期与修订规则。
没有信息集定义,就没有严格意义上的回测。
第二步:建立点时数据
保留字段的历史版本和可用时间。不能只保留最终修订值。无法获得点时数据时,应当降低结论强度,并在研究记录中说明限制。
第三步:审查样本空间
检查退市证券、上市日期、历史股票池、行业分类和指数成分。当前列表不能直接用于历史回溯。
第四步:验证数据质量
对完整性、准确性、一致性、唯一性、时效性和规范性分别计算指标。异常值需要保留处理日志。
第五步:建立低复杂度基准
先运行均值、线性模型或简单排序策略。确认数据管道和执行逻辑能够产生合理结果,再引入复杂模型。
第六步:使用时间序列验证
按时间划分训练、验证和测试数据。涉及持有期重叠时,增加时间间隔。测试集只用于最终评估。
第七步:诊断偏差和方差
使用学习曲线、验证曲线和参数扰动,判断问题属于表达能力不足,还是过度拟合。不要通过增加模型复杂度直接覆盖诊断结果。
第八步:评估部署成本
记录推理延迟、内存占用、数据读取时间、特征计算时间和交易执行延迟。AI模型量化后,重新评估信号排序和组合交易结果。
第九步:执行样本外压力测试
增加数据延迟、交易成本和滑点,改变时间窗口与参数。记录最差表现和失效条件。
第十步:冻结研究版本
保存数据版本、代码版本、参数、随机种子、训练区间和评估结果。没有版本冻结,后续无法复现,也无法判断性能变化来自模型还是数据更新。
结论:先验证数据边界,再选择算法
量化研究中,数据泄露和样本偏差的破坏力通常高于模型选型错误。
幸存者偏差可能让回测结果虚高超过30%,未修正时甚至会使年化收益虚增超过20%。财务数据常见的约45天发布延迟,也足以改变因子是否具有交易资格。学习曲线和验证曲线可以帮助区分偏差与方差,但无法替代点时数据和正确的时间切分。模型量化可以降低部署成本,但必须在信号排序、组合换手和端到端延迟层面验证。
最终的审查重点只有三个:
- 数据是否完整、准确,并且在当时可获得;
- 模型是否在未参与训练和调参的时间段稳定;
- 策略是否能在真实延迟、成本和成交约束下运行。
算法只是映射函数。数据决定输入,时间戳决定信息边界,执行模型决定结果能否落地。
参数优化可以继续进行。行情预测不需要提前做。
相关阅读: 交易员决策疲劳应对策略:减少日内无效交易的系统化方案 、 全职交易员的社交孤立:享受孤独还是走向心理失衡?.
