失真更早发生在数据进入模型之前。复权方式错误,价格序列就不连续。停牌数据未处理,收益率计算就会出现伪信号。退市股票被删除,样本空间就只剩下幸存者。特征使用了未来数据,回测曲线就不再代表可执行结果。
量化研究中,约八成时间消耗在数据清洗与预处理。这个比例并不意味着模型不重要。它说明模型的上限,首先由数据的边界决定。
收益率 = 当前价格 / 前一交易日价格 - 1
这行代码没有问题。问题在于两个价格是否来自同一套时间标准,是否处理了除权除息,是否包含停牌状态,是否在当时已经可获得。算法只能处理输入。不能修复输入中的时间错误。
数据清洗的底层逻辑:先定义可见信息
金融数据不是普通表格。
一张标准的股票日线表,通常包含开盘价、最高价、最低价、收盘价、成交量,即常见的开高低收量数据格式。表面上只有几个字段。实际还涉及交易日、股票状态、公司行为、数据供应商时间戳、币种、市场、交易所规则和字段口径。
如果这些信息没有统一,后续的标准化、因子生成和机器学习训练都会建立在不稳定的基础上。
先处理时间,再处理数值
时间序列的第一约束是顺序。
每个样本必须满足:
- 特征只使用决策时点之前已经存在的数据。
- 标签对应未来区间的收益或风险结果。
- 训练集、验证集和测试集按时间切分。
- 同一资产在同一交易日不能出现重复记录。
- 多源数据合并时,不能使用发布日期晚于决策时点的信息。
传统机器学习中常见的随机切分,在金融时间序列里通常不适用。随机抽样会把未来市场状态混入训练集。模型可能学习到跨期信息,而不是可重复的统计关系。
例如,使用过去二十个交易日的均线作为特征,计算窗口必须截止于当前决策时点。若回测在收盘后生成信号,使用当日收盘价还需要确认订单执行发生在收盘之后。如果信号按照收盘价计算,却假设同一收盘价成交,就会产生时点错误。
这不是实现细节。它直接改变收益率。
空值不是一种情况
空值需要分类。不能统一填充。
常见空值来源至少包括以下几类:
1. 交易不存在
股票停牌、期货合约尚未上市,或者某个市场当天没有交易。此时填充价格通常会制造虚假的成交。
2. 数据缺失
原始供应商没有返回数据,或者接口中断。需要回溯源数据,不能直接用前值替代。
3. 字段不适用
某些指标在上市初期没有足够历史窗口。这个空值具有真实含义,应该保留或延迟生成。
4. 公司行为导致的断点
拆股、分红、配股会造成价格序列跳变。需要进入复权处理流程。
5. 计算窗口不足
二十日波动率在前十九个交易日没有完整样本。直接填充为零,会把未知状态错误地转换为低风险状态。
处理空值之前,需要先建立状态字段。至少区分“无交易”“无数据”“窗口不足”和“字段不适用”。只有分类完成,填充规则才有意义。
异常值需要回到交易规则
价格出现极端变化,不等于数据错误。
可能原因包括:
- 除权除息未处理。
- 合约换月。
- 交易规则变化。
- 价格涨跌停限制。
- 数据单位变化。
- 小数点位置错误。
- 实际发生的极端行情。
简单地按照正态分布剔除三倍标准差之外的样本,在金融数据里并不可靠。收益分布经常具有厚尾。极端收益可能是市场最重要的部分。如果把所有尾部样本删除,模型会得到一个过于平滑的世界。
更稳妥的顺序是:
1. 先检查原始字段和单位。
2. 对照交易日历确认是否真实成交。
3. 检查公司行为与合约事件。
4. 比较多个数据源的相同时间点。
5. 最后才决定是否截尾、缩尾或保留。
异常值处理不能只看数值幅度。还要看它是否符合市场微观结构。
数据清洗的目标不是让数据看起来正常,而是让每个数值都能解释。
规避生存偏差与复权陷阱:构建时点数据库
回测中最隐蔽的错误之一,是只使用今天仍然存在的股票。
这会产生生存偏差。退市、合并、长期停牌或被剔除指数的标的从历史样本中消失。最终留下的股票天然经历了筛选。模型在过去看到的是一个经过未来结果过滤的资产池。
这种回测通常表现稳定。原因不是策略更强,而是数据集已经提前知道哪些对象能够存活到今天。
股票池必须随时间变化
一个正确的股票池,应当回答以下问题:
- 在某个历史日期,哪些股票已经上市?
- 哪些股票当时可以交易?
- 哪些股票已经退市,但在此前仍应保留?
- 某只股票何时进入或离开指数?
- 当时的行业分类和市值数据是什么?
- 当时能否获得该字段?
因此,股票池不能用当前全市场列表直接回填历史。需要使用包含退市股票的时点数据库,也就是按历史日期还原当时可见资产集合。
Point-in-Time数据的核心不是一个字段名称,而是一种时间约束:
数据可用时间 <= 决策时间
财报数据尤其如此。报告期结束时间、公告时间和数据入库时间不是同一个概念。策略不能在公告发布之前使用报告中的利润、现金流或资产负债表数据。
前复权和后复权解决的问题不同
拆股或除息会让价格序列出现非交易性质的跳变。
例如,股票发生拆股后,价格会按照比例调整。若直接使用未复权价格,历史收益率可能把公司行为误判为大幅下跌。前复权和后复权的目的,都是抹平这类价格缺口,但适用场景不同。
| 数据用途 | 更关注的对象 | 常见处理方式 | 主要风险 |
|---|---|---|---|
| 研究历史收益与技术指标 | 保持当前价格口径下的连续性 | 前复权 | 早期历史价格会被调整,不能直接作为历史成交价 |
| 还原历史实际价格 | 保持过去交易日的原始价格关系 | 后复权或未复权配合公司行为 | 用于收益计算时需要同步处理分红与拆股 |
| 订单执行与成交模拟 | 当时真实可交易的价格 | 原始价格、公司行为表、成交规则 | 不能把复权价格当成实际成交价 |
| 长期总收益分析 | 价格收益与现金分红 | 复权收益或总回报序列 | 分红到账时间与再投资假设必须明确 |
技术指标通常需要连续序列。交易执行需要接近当时真实的市场价格。两者不能用同一字段简单替代。
工程上应把价格字段拆开管理,例如:
- 原始开盘价、最高价、最低价、收盘价。
- 前复权价格。
- 后复权价格。
- 分红金额。
- 拆股比例。
- 公司行为生效日期。
- 可交易状态。
- 涨跌停价格。
不要把一个名为“收盘价”的字段交给所有模块。字段名越模糊,误用概率越高。
期货数据还存在连续合约问题
股票的复权问题,在期货中对应合约切换。
不同到期月份的期货合约,成交量、持仓量、价格和基差都可能不同。简单拼接会产生跳变。主连、指数化连续合约和实际可交易合约,不能混为一谈。
如果模型只研究方向,可以使用经过处理的连续价格序列。但如果模型要模拟下单、换月和滑点,就必须保留具体合约。换月规则也需要固定,例如按成交量切换、按持仓量切换,或者使用固定到期日规则。
连续合约只是一种研究表达。不是一个真实订单对象。
机器学习因子挖掘:非线性不是免费收益
当数据完成时间对齐后,才进入因子工程和模型训练。
常见模型包括梯度提升机、随机森林、线性模型和神经网络。XGBoost、LightGBM、CatBoost适合处理非线性特征组合。主成分分析可以压缩高相关变量,提取数据驱动的风险因子。
但模型复杂度不会自动产生有效信息。
先定义预测对象
模型训练之前,需要明确标签。
常见标签包括:
- 未来一个交易日收益率。
- 未来若干交易日累计收益率。
- 横截面排名。
- 超额收益。
- 波动率变化。
- 价格突破或回撤状态。
- 风险事件发生概率。
标签不同,损失函数和评估方式也不同。
预测未来收益率时,均方误差可以作为基础损失函数,但金融收益具有厚尾和异方差,极端样本会显著影响训练结果。预测横截面排名时,相关系数、秩相关和分组收益通常比单纯的均方误差更贴近组合应用。
不能只看模型的分类准确率。即使方向判断正确,预测幅度、换手率和交易成本也可能让策略无法执行。
技术指标不是因子本身
移动平均线、相对强弱指标、波动率、成交量变化都可以作为特征。它们只是对历史价格和成交量的变换。
特征是否有效,需要回答三个问题:
1. 特征是否在决策时点可获得?
2. 特征是否包含重复信息?
3. 特征与标签之间的关系是否在不同时间段稳定?
例如,五日均线和十日均线高度相关。把大量相似窗口同时输入模型,可能只是在增加维度。模型会获得更多分裂路径,却未必获得更多信息。
可以使用相关性分析、方差筛选、主成分分析和特征重要性进行初步压缩。但特征重要性不等于因果关系。树模型偏好取值范围大、切分机会多的变量。神经网络的权重也不能直接解释为经济含义。
滞后特征必须显式命名
每个特征都应该带有明确的时间含义。
例如:
收益率_滞后1日成交量变化_滞后5日波动率_过去20日均线差_截至昨日财报指标_公告后可用
不要使用含义模糊的字段名,例如“最新利润”“当前均线”。在流水线扩展后,模糊命名会让未来函数进入系统,而且很难追踪。
特征生成的核心规则可以写成:
特征(t) = 只使用时间小于等于t的可见数据
如果交易在收盘前执行,则收盘价不能作为当日已知输入。若交易在收盘后执行,则必须为成交时间、成交价格和滑点预留空间。
标准化需要区分横截面和时间序列
常用的标准化方法包括Z分数和最小最大归一化。
Z分数通常写作:
Z = (X - 均值) / 标准差
但均值和标准差的计算范围必须明确。
如果在全样本上计算均值与标准差,未来数据就进入了历史特征。正确做法是使用滚动窗口,或者只用训练集参数转换验证集和测试集。
横截面标准化与时间序列标准化也不能混用。
- 横截面标准化:同一交易日比较不同股票。
- 时间序列标准化:比较同一资产在不同时间的变化。
- 滚动标准化:只使用过去窗口。
- 训练集标准化:模型部署时复用训练阶段参数。
这些方法对应不同的统计假设。结果不能直接横向比较。
特征工程的实战构建:从原始行情到可解释矩阵
金融大数据的难点不在于特征越多越好,而在于每个特征都有生命周期。
从原始数据到模型输入,建议拆成几个独立层级:
1. 原始层
保存供应商返回的数据。只做格式统一,不修改原始值。
2. 标准层
统一字段名、时区、交易日、证券代码和数据类型。
3. 清洗层
处理重复记录、异常单位、停牌状态、公司行为和缺失值。
4. 特征层
生成收益率、波动率、技术指标、滞后变量和交互特征。
5. 标签层
生成未来收益、风险状态或分类目标。
6. 训练层
按时间切分样本,保存训练参数和版本信息。
分层的价值是可回溯。模型表现下降时,可以定位问题发生在源数据、清洗逻辑还是特征计算。
交互特征需要限制维度
交互特征能够表达非线性关系。例如,成交量变化与波动率组合,可能和单独使用任一变量不同。均线距离与趋势强度组合,也可能为树模型提供新的切分条件。
但交互数量会快速增长。若有一百个基础特征,任意两两组合就会产生大量候选变量。模型可能在训练集里找到偶然关系。
控制方法包括:
- 只组合具有明确假设的变量。
- 限制交互阶数。
- 使用滚动验证确认稳定性。
- 记录每次特征筛选过程。
- 不根据测试集结果反复修改特征。
- 对最终特征进行时间外验证。
特征选择本身也是模型训练的一部分。不能先看完整测试区间,再选择在测试区间表现最好的变量。那会把测试集变成训练集。
降维不是消除风险
主成分分析可以将多个相关变量压缩为少数成分。它适合处理高度相关的技术指标、行业暴露和资产收益矩阵。
但主成分的解释依赖样本区间。第一主成分在一个阶段可能代表市场整体波动,在另一个阶段可能更多反映行业集中度。成分方向也可能发生变化。
因此,使用主成分分析时需要保留:
- 训练区间。
- 均值和标准差。
- 特征载荷。
- 成分数量。
- 重训练频率。
- 成分在不同区间的稳定性。
不能把降维结果当作永久不变的风险因子。
对于组合模型,因子中性化也需要谨慎。市值、行业、波动率和流动性暴露可能解释大部分表面收益。如果模型没有控制这些暴露,所谓的机器学习因子可能只是传统风险因子的重新命名。
从模型到实盘:完整链路中的风险拦截
量化交易不是模型输出一个分数,然后直接下单。
完整链路至少包括:
- 行情接入。
- 数据清洗与对齐。
- 因子生成。
- 因子中性化。
- 组合优化。
- 回测引擎。
- 订单拆分。
- 执行算法。
- 交易状态监控。
- 风控拦截。
- 结果回写。
其中任何一层与回测假设不一致,回测结果都会失去参考价值。
回测引擎必须模拟限制条件
最简单的回测通常使用下一根开盘价成交。这已经比使用当前收盘价成交更合理,但仍然不够。
至少需要明确:
- 手续费。
- 印花税或交易税。
- 买卖价差。
- 滑点。
- 最小交易单位。
- 涨跌停限制。
- 停牌状态。
- 成交量约束。
- 订单有效期。
- 资金占用。
- 融资融券或保证金规则。
- 期货合约换月。
- 交易时段。
如果策略的平均持仓周期较短,交易成本对结果的影响可能高于模型参数变化。回测中的毛收益不能直接作为实盘预期。
订单拆分也不是装饰模块。大额订单可能改变成交价格。成交量加权、时间加权和限价执行,对不同市场状态的影响不同。缺少订单层模拟时,回测实际上只验证了信号,不是完整策略。
风控必须位于执行路径上
风控不能只在回测结束后统计。
实盘系统至少应设置以下拦截:
- 单只资产最大仓位。
- 行业或品种集中度上限。
- 单日最大换手。
- 最大回撤触发条件。
- 数据延迟和行情中断检测。
- 价格异常检测。
- 可用资金不足拦截。
- 下单频率限制。
- 重复订单拦截。
- 交易时段检查。
- 合约到期与强平风险检查。
如果行情时间戳停止更新,系统不应继续使用旧行情生成新订单。旧数据在程序层面仍然是有效数字,但在交易层面已经失去时效。
风险控制的基本原则是:数据异常时停止扩大风险,而不是让策略继续运行。
评估指标不能脱离成本
常见指标包括年化收益、最大回撤、夏普比率、胜率、换手率和收益波动率。它们只能描述结果,不能替代原因分析。
需要进一步拆解:
- 收益来自哪些资产。
- 收益集中在哪些时间段。
- 多空两侧是否对称。
- 收益是否依赖少数极端交易。
- 成本占毛收益的比例。
- 换手是否随市场波动放大。
- 模型在不同市场阶段是否发生参数漂移。
- 训练、验证和测试区间是否使用了相同的数据处理规则。
夏普比率较高的净值曲线,不代表模型可以直接部署。滑点、手续费、未来函数和市场冲击成本都可能改变结果。未知项不能用回测曲线补齐。
模型的最终输出不是预测值,而是一组带有成本、约束和失效条件的订单。
如何建立可重复的数据与算法系统
个人量化项目最容易出现的问题,不是缺少算法,而是无法复现。
今天生成的特征,三个月后可能因为数据补录、复权因子变化或交易日历更新而不同。若没有数据版本和代码版本,回测结果无法解释。
每次实验都要保存元数据
至少记录:
- 数据开始和结束日期。
- 数据供应商和接口版本。
- 股票池规则。
- 复权方式。
- 缺失值处理方式。
- 异常值规则。
- 特征列表及窗口。
- 标签定义。
- 训练、验证、测试区间。
- 模型参数。
- 标准化参数。
- 交易成本假设。
- 滑点假设。
- 风控限制。
- 代码提交版本。
- 随机种子。
随机种子不能解决全部复现问题。底层计算库、并行计算和数据排序也可能引入差异。对于生产系统,数据和模型都需要版本化。
用基线模型检测流水线
复杂模型上线前,先运行基线。
可选基线包括:
- 历史均值。
- 横截面排序。
- 线性回归。
- 简单动量。
- 简单均值回归。
- 只使用单一技术指标的规则策略。
如果复杂模型无法稳定超过简单基线,问题可能出在特征、标签、成本或样本切分,而不一定是模型能力不足。
基线的另一个作用是排查数据泄漏。一个极其复杂的模型如果突然获得异常高的预测能力,首先应该检查时点对齐,而不是增加更多层数和参数。
参数优化要留出边界
参数优化不是把回测曲线调到最高。
对于移动平均线、持仓周期、止损阈值、树深度和学习率等参数,可以使用滚动窗口进行训练和验证。每轮只使用当时可见的数据。测试区间只在模型方案确定后使用一次或极少次数。
优化结果还需要观察参数邻域。如果只有一个精确参数点表现优秀,周边参数迅速失效,通常意味着过拟合。更可靠的策略往往表现为一片相对稳定的区域,而不是孤立峰值。
需要关注的不是最高收益点,而是:
- 参数轻微变化后是否仍然有效。
- 不同时间窗口是否保持相同方向。
- 不同股票池是否仍有结果。
- 加入保守成本后是否保留优势。
- 样本外区间是否出现结构性失效。
结语:把模型放回数据链路中
数据与算法实用指南的核心,不是推荐某一个模型。
XGBoost、LightGBM、神经网络和主成分分析都有使用场景。它们不能替代数据清洗,不能修复生存偏差,不能消除未来函数,也不能自动生成真实的成交价格。
稳定的量化研究链路应当满足四个条件:
1. 每个数据字段都有清晰的时间含义。
2. 每个特征都能追溯到可见信息。
3. 每个回测假设都能映射到执行规则。
4. 每个模型结论都经过样本外和成本约束检验。
参数优化建议保持克制。先固定数据口径,再调整模型参数。先验证时点正确,再比较收益指标。先加入交易成本,再讨论策略优势。先测试失效边界,再决定是否扩大资金规模。
金融时间序列不存在永久稳定的分布。模型只能在特定数据生成机制下工作。机制变化,特征关系就可能衰减。回测通过,不等于实盘成立。实盘成立,也不代表未来继续成立。
可复现、可解释、可拦截。
这三个条件,比更复杂的网络结构更接近量化系统的工程底线。
相关阅读: 交易员决策疲劳应对策略:减少日内无效交易的系统化方案 、 全职交易员的社交孤立:享受孤独还是走向心理失衡?.
