
面向金融的深度学习做了一段时间说实话这个系列的坑比我想象中多得多。上一篇我们聊了基础框架和数据切分很多朋友留言说想看更具体的模型搭建以及真正落到策略上该怎么操作。这篇我直接把第二阶段的完整思路拿出来从数据处理、模型选型、训练细节到策略落地全程用实际案例说话踩过的坑和绕过的弯路都一并写上。1. 数据准备金融时序和CV、NLP数据完全是两回事1.1 金融数据最容易被忽视的三个特性做深度学习的人第一天接触金融数据往往会产生一种错觉这不就是一行一行的时间序列吗和传感器数据差不多。实际情况远没那么简单。金融数据有三个特性直接决定了后面模型能不能work。第一个特性是信噪比极低。一张猫图里有明确的轮廓、颜色、纹理信息模型要学的特征非常丰富。但金融序列里有效信号可能只占整体波动的百分之几其余全是噪声。我见过不少团队一开始拿着LSTM跑出一张漂亮的损失下降曲线以为捡到宝了结果把预测值和真实值拉出来一看几乎就是一条水平线加随机抖动。这不是模型问题是数据本身的信噪比决定了上限。第二个特性是非平稳性。图像数据的分布相对稳定训练集和测试集通常来自同一个“世界”。金融序列则不同不同年份的波动率、相关性、微观结构都在变。严格来说2020年的分钟线分布和2022年根本不是同一个分布。很多人在这一步踩了大坑用随机切分的方式分训练集和测试集导致测试集里“混入”了未来信息回测结果看着特别好实盘一上线就变脸。第三个特性是时序依赖和多重对齐问题。金融数据不像图像那样天然对齐不同数据源的更新时间不一致tick级数据和分钟级数据的时间戳精度不同复权因子、合约换月都会影响特征拼接。如果这一层处理不干净后面所有计算都是空中楼阁。1.2 预处理流程从原始行情到模型输入的完整链路我目前在项目中使用的这套预处理流程是从多次失败里逼出来的。核心思想是宁可特征简单也要保证对齐和语义正确。第一步统一时间基准。所有原始数据先按时间戳排序并统一时区和交易时段。用某交易所的分钟线数据做训练时我会把时间戳全部转成UTC标准后存储避免本地时区差异导致的对齐错位。如果是跨市场数据这一步尤其重要比如美股和欧股时间重叠部分很容易出现“看着是同一时刻实际上差了半小时”的尴尬。第二步复权和换月处理。做日线级别的策略前复权和后复权会直接影响特征计算。对于分钟级数据主力合约连续拼接时换月跳空是必须标注出来的。我在特征工程里单独加了一个“是否换月”的标志位并在换月时刻丢弃跨越换月点的样本宁缺毋滥避免把价差跳空当成可学习的规律。第三步特征构造。金融特征的构造不是越多越好我试过堆了上百个技术指标结果模型过拟合得一塌糊涂。现在使用的核心特征分几类基础价格衍生特征收益率、对数收益、最高最低价差比例、成交量相关特征成交量变化率、量比、以及微结构类特征买卖价差、订单簿不平衡度。所有特征在进入模型前要做缺失值处理金融数据里NaN太常见了有些时刻就是没有成交、没有报价直接填充0会让模型学习到“0代表无信号”这个错误的隐含模式我用的是前后向填充加“是否缺失”的标志位组合。1.3 标签定义预测目标决定了整个模型的天花板标签的定义直接决定了模型能学到什么。我在这个系列里反复强调一个观点预测方向永远比预测绝对价格靠谱。在真实项目中我主要用两种标签第一种是未来N期收益率的方向标签。比如预测未来5分钟涨还是跌如果未来5分钟的累计收益率大于某个极小阈值比如万分之一记为1否则为0。为什么要设阈值因为纯粹的正负分类会让模型把微小的噪声波动也当作学习目标训练不稳定且没有实际交易意义。第二种是未来N期收益率的排序标签。这种标签配合Learning to Rank或者直接做回归主要用于截面选股或多标的排序场景。它的好处是天然去除市场整体涨跌的干扰模型学的是“相对强弱”这在实操中比绝对涨跌预测稳定得多。标签的时长窗口也需要反复验证。分钟级模型我用5分钟、15分钟、30分钟做对比实验最终发现15分钟的预测在信号稳定性和交易频率之间最平衡。时间窗口太短1分钟噪声极大模型学到的基本是微观结构反转手续费和滑点根本覆盖不了太长60分钟及以上信号虽然稳定但样本量成倍减少训练难度上升。2. 模型选型LSTM、CNN还是Transformer2.1 为什么经典LSTM依然是金融时序的“基准性价比之王”金融时序预测中LSTM被唱衰已经不是一两年了但我个人在实际项目中的体验是LSTM在这个场景下依然非常能打关键是看你怎么用。第一LSTM的门控机制天然适应金融数据的时间依赖。市场行为有明显的“惯性”和“突变”LSTM的遗忘门可以学习到什么时候该遗忘旧信息什么时候该更新新信息这与金融时序中“趋势延续”和“均值回归”两种状态切换非常契合。第二LSTM参数量相对可控而金融训练样本非常有限。以分钟线为例一只标的一年大约有数万根K线听着不少但和图像分类动辄上百万张图相比数据量级小得多。用参数量巨大的Transformer很容易过拟合。LSTM参数量适中配好正则化反而能稳稳地学到可泛化的特征。第三推理速度在实盘场景很关键。真实交易中分钟级信号在收盘后必须快速计算出来几秒到十几秒的延迟完全可接受但如果是盘中实时信号模型每秒钟要处理多只标的推理耗时就重要了。LSTM的推理开销比带多头注意力的Transformer小很多在低延迟场景下优势明显。我一般会先跑一个两层LSTM作为基线hidden size在32到64之间序列长度取32到64个时间步。这个基线模型的作用是确定“合理性能天花板”——如果后续更复杂的模型在这份数据上连10%的相对提升都达不到那大概率不是模型容量的问题而是特征或标签压根没含足够信息。2.2 CNN在时序建模中的特殊用法一维卷积不是替代品而是互补品很多人不理解为什么金融时序预测会用到CNN。我最初也以为CNN只能处理图像后来在实践中发现一维卷积对时序数据的局部特征抽取能力非常强而且与LSTM形成了很好的互补。用Conv1d处理时序的核心思想是把时间轴看作图像的“宽度”维度卷积核相当于一个固定长度的滑动窗口自动抽取局部模式。比如一个kernel size为8的卷积核可以捕捉到近8个时间步内的量价组合模式——类似“放量突破”或“缩量回调”这种图形化特征。这类局部图形化特征正是技术分析派总结出的经典模式CNN能自动学到不用手工设计。实际项目中我把CNN和LSTM叠加起来用先用一层Conv1d提取局部特征再把CNN的输出序列送入LSTM捕捉长程依赖。这样做的好处是LSTM不需要从原始噪声序列里一点点抠局部模式可以专注于建模时间步之间的状态转移训练更容易收敛泛化能力也更好。这里有一个实操细节卷积核的数量不宜太多我一般用16到32个kernel size取5或7比较合适太大会把短周期模式直接抹平。残差连接在这个组合里也很重要金融特征尺度差异大加残差能避免深层网络的信息损耗。2.3 Transformer在金融场景中的局限与正确用法Transformer系列模型在NLP和CV领域大杀四方但在金融时序里我的态度是“谨慎使用、明确限定场景”。Transformer最大的优势是并行计算所有时间步之间的依赖关系理论上可以建模任意距离的长期依赖。但金融时序的“长期依赖”本身就很模糊行情的影响往往在几十分甚至几百个时间步内迅速衰减真正需要超长上下文的场景并不多见。反而多头注意力的参数量在样本有限时极易演变成过拟合引擎——前面提到过几万条训练样本对Transformer来说非常勉强。我目前只在两类场景下用Transformer。一是跨标的横截面建模需要同时考虑几十甚至上百个标的之间的相关性这时把每个标的当作一个token用自注意力学习截面关系比LSTM逐标的扫描高效得多。二是混合预测任务的统一建模将一个标的的多周期数据拼接成多分辨率输入利用注意力机制自动加权不同周期的影响。如果非要在单个标的上用Transformer务必做足正则化。我在实践中发现Dropout调到0.3以上、加Weight Decay、用更小的学习率预热能显著缓解过拟合。另外位置编码对金融时序非常重要注意这里不能用NLP里那种标准位置编码套到时间步上时间间隔是不均匀的需要把真实的时间距离比如分钟差融入注意力权重。这一点很多人忽略了直接用原版Transformer处理不均匀间隔的K线数据效果远不如LSTM。2.4 模型集成与融合金融场景里“单模型单配置”是走不远的单独依赖某一个模型结构在金融这种高噪声场景下很容易陷入“训练时好测试时崩”的尴尬。我的经验是一定要做模型集成但集成方式需要结合领域特点来设计。第一种常用的方式是训练多个不同随机种子的同结构模型做预测平均值。金融数据样本少随机种子造成的预测方差非常大多个种子集成后IC稳定不少。第二种方式是异构模型融合比如LSTM、CNN-LSTM、轻量Transformer各训练一份预测结果做加权平均。异构模型抓到的特征模式不同融合后往往能带来额外的稳定性收益。集成还有一个讨巧的用法不是直接融合预测值而是融合“信号”。比如LSTM给了一个方向信号CNN给了另一个在信号层面做投票或者加权能避免极端预测值对组合收益的拉偏。我自己的框架里会把集成方法做成可配置项哪个模型最近表现好就调高权重但会限制权重上限防止过拟合近期行情。3. 训练策略时间序列训练不能套用标准深度学习的“老配方”3.1 时间序列划分随机切分的代价有多大很多做图像、NLP的工程师刚上手金融时序时最自然的做法是用train_test_split随机划分数据然后直接训练。这是金融时序里最致命的错误之一。随机划分意味着训练集和测试集在时间上互相交错。对分钟级数据而言相邻K线之间的相关性极强前一根K线的特征和标签与后一根K线高度重叠。测试集里混入的“未来”信息会让测试指标虚高得离谱——我见过随机划分下测试集AUC达到0.75用时序切分重新划分后直接掉到0.55的真实案例。正确的时序划分有三层约束。第一层是样本间的时间间隔如果训练集最后一段和测试集第一段紧挨着仍然可能造成泄漏必须间隔至少一个计算周期的空窗期。第二层是做滑窗训练而不是一次性训练完再测试。金融市场分布漂移快模型需要定期重训我用的是滚动重训练机制每训练一轮就把最近的数据纳入训练集同时丢掉最老的数据保持训练样本总量恒定。第三层是验证集的选择验证集必须来自训练集之后的时间段且不能反复在同一个验证集上调参调过头否则验证集本身会变成第二测试集。调参阶段我习惯保留三个时间切片训练、验证、最终测试各一个最终测试集只有在全部调参完成后才能碰一次。3.2 滑窗训练与walk-forward验证的完整流程Walk-forward是我在金融时序训练中用得最频繁的验证框架本质上是模拟真实的“历史时点滚动预测”。具体做法是这样的假设有2022年1月到2024年6月的分钟数据设定训练窗口为6个月验证窗口为1个月测试窗口为1个月。第一步用2022年1到6月训练7月做验证8月做测试第二步窗口向后滚动一个月用2022年2到7月训练8月验证9月测试依次类推。后面看着像不起眼的细节其实决定了模型能否持续产出有效信号。在walk-forward中每个测试段的预测结果可以被拼接成完整时间段上的预测序列再统一计算IC、AUC等指标。这样做比单独某一段的指标更有说服力因为它覆盖了多种市场状态趋势行情、震荡行情、高波动和低波动行情。我强烈建议所有做金融时序的团队把walk-forward评估固化为标准流程它能在模型上线前帮你过滤大量“靠运气”的虚假策略。3.3 归一化的正确方式标准化参数必须只从训练集计算金融特征归一化里的一个高频bug是在全量数据上计算均值和标准差再对训练集和测试集分别做变换。听起来很合理但标准差和均值里已经包含了“未来信息”测试集被无形中“泄漏”了。正确的做法是对训练集、验证集、测试集分别计算各自的统计量或者更严格地只用训练集统计量然后把这个统计量应用到验证集和测试集上。我在代码里会单独封装一个Scaler类训练结束后保存训练集的均值和方差在推理阶段直接加载。另一个细节是特征和目标不要混着归一化。收益率的分布是厚尾的直接做z-score后仍有大量极端值。我习惯在特征侧用分位数变换比如把特征投影到高斯分布上让模型输入尽量稳定在标签侧做简单的截断处理把极端收益如超过99.9分位拉回来一点避免单个离群样本主导损失函数。3.4 损失函数的选择交叉熵、MSE还是Rank Loss分类任务用交叉熵回归任务用MSE这是教科书惯例。但金融场景下这两种都存在问题。交叉熵的缺点是忽略预测置信度排序的信息。在排序选股场景里模型不仅要判断涨跌还要区分“涨得多的”和“涨得少的”交叉熵在这一点上效率很低。MSE的问题则是对离群值过度敏感一次极端行情会主导整个batch的梯度更新让模型长期处于“反复纠正极端预测”的抖动状态。我在项目里最常用的是Rank Loss的变体其实就是让模型学到样本间的相对顺序。具体实现可以借助Pairwise Ranking Loss或者直接构造“排名损失分类损失的加权和”。比如在截面选股场景中对每个时间切片内的所有股票模型输出一个预测分目标是最小化预测分与真实未来收益之间的排序损失。这样模型学到的不是绝对的涨跌而是“相对谁更强/更弱”。这种目标下模型预测精度不用特别高只要排序能力稳定策略就能赚钱。如果必须用MSE建议配Huber Loss替代它在误差较小时接近MSE误差较大时改为线性惩罚极大地抑制了离群值的影响。4. 策略落地从模型输出到真实交易的最后一公里4.1 信号映射原始预测值如何变成可交易的仓位模型输出的预测值本身不是交易信号。以分类模型为例输出是0到1之间的概率但概率高不代表应该满仓需要把概率映射成仓位。第一层映射是阈值过滤。简单做多标的时只有当预测概率显著高于某个阈值比如0.55以上才生成开仓信号低于另一个阈值比如0.45以下生成平仓信号。阈值的设定不能拍脑袋而是在验证集上扫描不同阈值对应的收益和回撤选择风险调整后收益最优的阈值。第二层映射是仓位管理。预测概率越高仓位可以适当加大但必须有上限。比如预测概率0.55对应30%仓位0.65对应60%0.75以上才允许80%仓位永远不把仓位打满。为什么因为金融预测本身的不确定性极高再强的模型也有失效期仓位上限是最基本的生存保障。第三层映射是时间衰减。预测是针对未来15分钟做的但15分钟后信号就完全过期了。实际操作中如果在信号生成后的N个时间步内没有成交必须撤单并在下一根K线重新计算信号不能拿着旧信号追价。这一点在盘中策略里极其重要我见过不止一次因为成交延迟、旧信号继续执行导致的回撤。4.2 交易成本模型手续费、滑点和冲击成本一个都不能少很多回测曲线惊为天人一上实盘就亏成狗绝大多数问题出在交易成本被严重低估。金融深度学习的策略迭代很快如果交易成本模型不精确你连“策略到底赚不赚钱”都判断不了。我的交易成本模型分三层计算第一层是显性手续费。不同合约、不同品种费率不同按成交金额的万分之几计算。这部分相对固定直接写进回测引擎的参数里。第二层是滑点。滑点的本质是流动性不足造成的实际成交价偏离信号价。在分钟级别回测里我习惯按每笔成交价增加固定比例的滑点成本比如万分之一到万分之二。对流动性好的品种这个估计已经够用对流动性差的品种需要单独调高。第三层是冲击成本。当单笔下单量占市场当前挂单量的比例超过某个阈值时你的订单本身会推动市场价格变化。冲击成本的计算比较复杂我用的简化方式是冲击成本比例与订单量占比的平方根成正比。这个公式来自行业经验不一定完全精确但比完全忽略要好得多。回测结果出来后我会做一个特别重要的“成本敏感性测试”把滑点成本分别设为0、万分之一、万分之二、万分之五观察策略盈亏的翻转点。如果策略在万分之五滑点下依然盈利实盘才有底气如果滑点从万分之一升到万分之三就从盈利变亏损这策略本质上赚的是流动性风险溢价实际承担的风险远大于预期。4.3 组合层面的风控框架单标的模型的“盲区”必须靠规则弥补单标的深度学习预测模型只解决一个问题该不该做多或者做空某一个标的。它回答不了的问题是如果同时有10个信号该给每个信号分配多少权重如果所有信号同时失效该怎么处理我对每一路模型输出都强制加一层组合层面的规则风控这层不依赖模型输出而是硬编码的约束条件。第一是行业和板块的暴露上限。哪怕模型对某板块内3个标的都给出了强烈看多信号组合在该板块的总仓位也不能超过30%。这限制了板块整体黑天鹅事件对策略的冲击。第二是单标的最大仓位限制。无论模型多自信单标的仓位不超过20%。我踩到过单标的仓位加得太重、遇到极端跳空直接吞掉三个月利润的教训从那以后这个上限再也没改过。第三是整体持仓数量下限。模型预测再准也会在个别信号上错误。持仓分散度本身就是免费的午餐我要求组合在任何时点至少持有5个标的除非市场系统风险到了触发空仓阈值的极端情况。第四是止损规则。模型预测不一定能及时捕捉到突发消息驱动的反转。我在组合层面设置硬止损线比如组合从近期高点回撤5%就强制减半仓回撤8%清仓空档等待信号重新确认。这个规则不依赖模型是纯粹的风险管理纪律。4.4 回测框架的核心设计事件驱动而不是向量化玩过几年回测的人都会遇到同一个分岔路口用开源的向量化回测框架还是自己搭建事件驱动回测框架向量化回测的优势是快一次把所有K线数据都读进内存通过向量运算算出最终净值曲线。但它有个致命缺陷难以精准模拟成交细节、订单簿变化和撮合逻辑。常见的向量化回测引擎假设“按信号价的下一秒价格成交”这个假设在分钟级别尚可接受在tick级别则完全失真。我更倾向自建一个轻量级的事件驱动回测框架核心事件流是读取K线数据、生成信号、计算目标仓位、模拟撮合、更新持仓和资金记录、统计风险和绩效指标。每个环节都是独立模块便于排查逻辑错误。自建框架完整运行一遍可能需要几十秒到几分钟和向量化那几秒没法比但它的可审计性和真实性是向量化无法替代的。回测中还有一些容易被忽视的细节分红除权对净值的影响、合约换月时价格跳变对持仓盈亏的影响、开平仓在K线内部的时间戳对齐。这些细节每个都可能导致回测结果和实盘结果出现系统性偏差。5. 常见问题与排查技巧实录5.1 金融时序深度学习典型问题速查表问题表现根因分析解决方案训练损失下降但测试AUC和随机差不多特征信号太弱、模型容量过大导致过拟合简化特征、增加正则化、先跑LSTM基线回测收益极高但实盘连续亏损交易成本被低估、未来的信息泄漏做成本敏感性测试检查归一化和特征构造中是否混入未来数据预测概率长期集中在0.5附近信号本身太弱或标签噪声过大调整标签窗口长度、改用Rank Loss、增加更有效的特征模型上线三个月后IC突然恶化市场过拟合了模型、分布漂移严重引入walk-forward重训练机制模型定期用最新数据更新批量预测时内存溢出特征太多且一次性加载全量数据改用分块处理特征或用生成器逐步读入数据训练和推理时的预测分布不一致推理时特征缺失值处理方式与训练不一致封装统一的预处理管道训练和推理共用同一套代码5.2 我踩过的三个最深的坑第一个坑是“未来函数藏在特征里”。我曾经在特征工程中加入了一个“当日最高价”设计时想当然认为它是已知信息结果回测里它被用作预测当日的收益相当于模型偷看了结局。这种错误最隐蔽的地方在于它不体现在训练指标上反而会让训练指标更好只有做严格的因果回溯才能发现。从那以后我对每个特征都做一次严格的时间逻辑审查这个特征在t时刻计算时t时刻之后的信息是否已经被用了。第二个坑是“标签与交易时间不对齐”。有一段时间我用分钟数据训练时预测目标用的是下一分钟收益率但在实盘推演时信号计算完再去下单实际成交时间已经落在分钟边界后收益对应的时间窗口和预测目标完全错位。这个问题在回测里可以通过延迟一拍的撮合逻辑暴露出来但如果你用的框架默认按信号形成时点成交就会被悄悄掩盖。后来我在回测引擎里强制加入一个可配置的“信号延迟窗口”默认情况设定为计算完成后至少下一根K线才能成交彻底消除这类前视偏差。第三个坑是“特征分布漂移带来的静默失效”。有一个模型上线后前三个月表现非常稳定第四个月开始IC骤降但特征和模型都没改最终排查发现是某交易所调整了最小报价单位导致所有价差类特征的整体分布发生了变化。这种市场微观结构的变化不会直接报错模型却在悄悄恶化。现在我对所有特征都做了分布监控当某个特征在近一周的分布与训练期分布差异超过3个标准差时自动触发告警人工检查是否需要重训或调整特征。5.3 影子账户与灰度上线让模型先“赚假钱”再“赚真钱”即使回测、walk-forward、成本敏感性测试全做完了我依然不建议直接把策略切到真金白银。我的习惯是搭建一个影子交易通道让模型在实盘行情下输出信号但这些信号不真实下单只记录虚拟持仓和虚拟盈亏持续运行一到两周。影子交易的核心价值是检验“实盘数据管道”和“回测数据管道”是否完全一致。很多团队模型在回测里很好一接实盘数据流就崩溃原因往往是实盘的数据格式、字段命名、缺失值处理和回测时不完全一致。影子阶段能把这些不一致全部暴露出来。影子交易跑通后我会用最小资金量灰度上线比如账户总资金的5%到10%。这个阶段持续2到4周重点观察真实交易中的滑点、成交速度和冲击成本是否与回测假设一致。确认衰减程度可接受后再逐步放大仓位直到达到目标资金规模。最后说点实际的金融深度学习的核心矛盾始终是“无限复杂的模型”和“极度有限的信息”之间的矛盾。模型再先进如果不能在你这份数据、你这个场景里稳定产生有效信号都是零。我的建议是先跑通最简基线用严格的时间序列划分去验证信号本身是否存在再逐步叠加复杂的模型结构。不要一上来就搬一个巨大的Transformer先让一个小LSTM告诉你这份数据到底有多少“料”。另外回测永远只能证明“过去有效”不能证明“未来有效”。我们能做的是用严格的方法避免自欺欺人的验证流程用成本敏感测试评估策略的真实生存空间再用分布监控和滚动重训练去延长策略的有效生命周期。这行没有一劳永逸的圣杯只有一遍一遍地跑数据、审逻辑、排bug、做灰度把每一个环节都控制到极致才能在充满噪声的市场里把那一点点微弱的优势积累起来。