
一个强化学习模型在回测里跑得漂漂亮亮收益曲线一路向北结果一上模拟盘就开始连续回撤甚至直接击穿止损线。这种事情我在做“基于强化学习框架的交易策略学习过程”这个方向时见过太多也包括自己亲手踩过的坑。很多人把强化学习应用到交易里第一反应是“找个算法、喂点行情、等模型自己赚钱”但实际操作下来你会发现交易策略根本不是一个单纯的预测问题而是一个序列决策问题强化学习在这里的真正价值是让模型自己去学“该在什么状态下采取什么动作”而不是先去预测涨跌再靠人工规则转换成本。这篇文章我想把整个链路完整拆开讲一遍环境怎么搭、奖励怎么设、算法怎么选、训练怎么判断收敛、回测上线要过哪几道坎。适合已经有一点机器学习基础、想用强化学习做量化交易策略但又不太确定该怎么系统性上手的读者。1. 交易策略为什么值得用强化学习重做一遍1.1 交易的本质是序列决策不是单步预测先把一个概念说清楚传统监督学习的做法是让模型预测未来N期的涨跌方向或者收益率然后人再自己写一套规则去把预测结果转成买卖动作。比如预测明天涨2%就买入预测跌就卖出这种“预测规则”的模式在最简单的情况下是有效的但一旦市场状态复杂一点问题就出来了。你不仅要预测还要决定买多少、什么时候止盈、什么时候止损、头寸之间怎么切换这些决策之间存在时间前后的依赖关系——今天满仓买入明天即使信号变弱了由于手续费和滑点的存在你未必应该立刻清仓反过来信号变强的时候仓位也应该一起调整。这类决策没法用单步预测来覆盖它是一个典型的序列决策问题。强化学习正是为这种问题设计的。它不显式去预测未来的涨跌而是通过和环境交互去学习一个直接输出交易动作的策略策略的好坏由长期收益来决定。你不需要再人工设计“预测转动作”的规则也不需要拍脑袋定止盈止损阈值模型会把它们融进策略参数里。这是我建议交易策略可以考虑用强化学习重新做一遍的核心理由。1.2 强化学习的交易问题是怎么建模的要把交易问题装进强化学习框架第一步是把它形式化成马尔可夫决策过程MDP。一般定义为一个五元组状态、动作、状态转移概率、奖励和折扣因子。状态 S_t在 t 时刻对交易决策有用的全部信息比如过去N根K线的收益率、波动率、持仓盈亏、市场流动性指标等。动作 A_t当前时刻要做的交易操作可以是离散的“买入/卖出/持有”也可以是连续的“目标仓位比例”。状态转移 P市场从 t 到 t1 的变化这部分模型是自己走的强化学习算法通常不显式建模它。奖励 R_t模型进入新状态后获得的反馈信号在交易里最自然的定义就是账户资产的相对变化。折扣因子 γ权衡当前收益和未来收益的系数取值在0到1之间。有一点必须注意交易环境的“状态转移”和你平时在Atari、机器人控制里见到的环境完全不同。游戏环境是确定的、规则写死的而市场状态转移背后是无数交易者共同作用的结果是非平稳的——你今天学到的转移规律下周可能就变了。这个差异会直接影响后面所有环节的设计包括奖励函数和训练评估方案一定要刻在脑子里。1.3 要不要用强化学习先问自己三个问题不是所有交易策略都适合强上强化学习。我的经验是动手之前先问自己三个问题你的交易决策是否真的存在跨时间依赖如果只是简单的“信号触发就买、信号消失就卖”那监督学习加规则可能更稳定。你有没有一个足够接近真实市场的仿真环境这里包括手续费、滑点、最小交易单位、涨跌停约束等。环境不真实训练出来的策略基本就是虚拟世界里的纸上谈兵。你能否承受训练的不确定性强化学习训练过程方差大可能跑十次出五个结果不像监督学习那样稳定收敛需要你有耐心和调试手段。如果三个问题回答下来都是“是”那可以继续往下看。2. 搭建环境状态、动作、奖励三件套2.1 状态空间别什么特征都往里面塞状态空间的设计直接影响训练效率和策略质量。我先说一个常见错误很多人把能拿到的因子全部堆进状态觉得信息越多模型越聪明。实际效果恰恰相反高维状态空间会显著增加探索难度模型要很久才能学会“哪些维度其实根本不用看”。我的建议是把状态分为三类市场特征类过去N根K线的对数收益率、成交量变化、波动率用滚动标准差算、常见的动量因子。持仓状态类当前仓位、当前累计收益、已持仓时间。这类信息非常关键因为模型需要知道“我现在持有什么”才能决定“下一步怎么操作”否则动作会缺乏上下文。风险状态类当前回撤、账户净值相对前期高点的距离。这些信息能帮助模型在高回撤时主动降低仓位。状态标准化是绕不开的环节。做监督学习时你习惯把整个训练集的均值和方差固定下来做标准化但强化学习环境是流式更新的训练集没有“全局统计量”可以直接用。我实际用的方案是先用一段历史数据计算均值和方差然后在训练期间固定住不再随新数据变化。这能避免训练初期状态分布漂移带来的不稳定。还有一个绕不开的坑是防止未来函数。比如你想在状态里加入“今天的收盘价”但如果你的决策点是在当天开盘前收盘价就是未来信息。这种前视偏差一旦进了训练数据回测会异常好看实盘立刻死得很惨。所有状态特征必须严格按照决策时间点可得信息来计算。2.2 动作空间连续仓位和离散命令怎么选动作空间的设定直接表达了你希望策略具备什么能力。我见过三种主流方案纯离散动作输出“买入、卖出、持有”或者更细一点“加仓20%、减仓20%、不动”。优点是实现简单离散动作的训练在不少算法里更稳定缺点是仓位调整粒度受限策略难以做精细风险管理。连续仓位动作输出一个实数表示目标仓位比如0.8表示在资产上配置80%资金。优点是可以精细控制敞口更接近真实交易习惯缺点是连续动作空间搜索难度更大策略容易收敛到“一直满仓”这种平庸解。先离散后连续模型首先判断方向多/空/空仓再输出一个连续仓位大小。这种设计能利用导演网络和行动器网络的分工是我实际项目中最常用的一种效果往往比单一动作空间稳定。不管选哪种动作都必须经过约束处理。比如你设置单次动作的最大仓位变化幅度为10%那模型输出从空仓到满仓也需要至少10步这更接近真实交易的执行限制也能自然平滑换手率。这个约束可以在环境内部处理不需要改动算法。2.3 奖励函数最容易被低估的一环奖励函数是强化学习交易策略设计的灵魂也是翻车率最高的环节。一个最朴素的奖励定义是当前步的账户净值变化但这样直接训练往往会让模型过度追求短期收益换手率高得吓人手续费和滑点最终吞掉大部分利润。我建议从“收益率 风险惩罚 交易成本惩罚”三个维度搭奖励先给一个我常用的版本def compute_reward(prev_equity, equity, cur_position, prev_position, risk_penalty_weight0.1): # 基础收益率 ret (equity - prev_equity) / prev_equity # 交易成本惩罚按仓位绝对变化量估算 turnover abs(cur_position - prev_position) cost_penalty transaction_cost * turnover # 风险惩罚这里用波动项的滚动估计作为代理指标 risk_penalty risk_penalty_weight * rolling_volatility reward ret - cost_penalty - risk_penalty return reward这段代码里的核心思想是每一步的奖励不是单纯的盈亏而是要扣除为获得这个盈亏所付出的代价。交易成本惩罚能抑制过度交易风险惩罚能避免模型在波动率升高时依然硬扛高仓位。实际使用时这三个权重需要反复调没有一个固定值可以抄作业。我自己的经验是先把交易成本惩罚设置成真实成本的1.5到2倍让模型一开始就学着爱惜羽毛。奖励函数还有一个隐蔽的坑跨期平衡。如果你把最优总收益作为优化目标模型会倾向于在训练后期做一些“赌大的”操作因为前期已积累的安全垫让它觉得冒险也无所谓。一个有效的缓解办法是引入卡玛比率或者夏普比率这类风险调整收益指标来做稀疏的结算奖励比如每隔N步给一次额外奖励再配合每日的稠密奖励。这种混合奖励的收敛速度比纯稀疏奖励快也比纯稠密奖励稳定。3. 算法选型不要一上来就堆SAC或PPO3.1 主流算法的适用场景对比强化学习算法种类很多但真正在交易场景中有实战价值的其实是那么几个。先放一张我经常给新同事看的对照表算法动作类型样本效率稳定性交易场景适配度DQN离散低中低不适合连续仓位但适合做方向判断DDPG连续中低中收敛不稳定需要大量调参TD3连续中中中比DDPG稳定但仍依赖调参PPO离散/连续中高高目前交易策略项目里最常用的基线SAC连续高中高适合高维连续状态但要控制熵系数IQL离线离散/连续不适用高高适合只用历史数据训练不能交互的场景这背后是有原因的。交易场景的交互成本极高你不能像游戏里那样跟环境交互几百万步历史数据就那么多每一步还要扣手续费。所以样本效率很重要但稳定性更关键。PPO之所以成为交易领域的事实标准不是它理论多优美而是它用重要性采样和裁剪限制了单次更新步长训练曲线相对可控不容易崩。SAC虽然样本效率更高但最大熵框架天然鼓励探索这在交易里容易表现为策略反复横跳。3.2 根据数据和场景选择具体方案实际项目里我一般按三种场景选型第一种有较长的分钟级或日线级数据超过10年且可以用小步长的模拟环境做在线训练也就是每个回合从历史数据里随机采样一段重新开始。这种情况下我首选PPO因为训练稳定性最好迭代效率也够用。第二种状态和动作维度都比较高需要精细的仓位控制数据量也充足。这种场景我会考虑SAC但必须严格限制熵系数的范围并在训练中期衰减熵权重否则模型会一直保持过高的随机性策略表现很不稳定。第三种只有一段静态的历史数据无法在实时环境中逐步交互比如你要在某个时间点做一份策略回测报告不能反复切换在线退火。这种场景适合离线强化学习比如IQLImplicit Q-Learning。它不会对未观察到的动作做太乐观的估计能较好避免分布外动作带来的幻觉收益。其实很多人的历史K线回测本质上就是离线设定只是很多人没意识到。还有一个必须提醒的点都说“强化学习离线训练不稳定”但交易策略的离线属性比机器人控制场景更严重。市场状态转移不是你能模拟的你必须用历史数据当训练集可历史数据本身只有一条路径没法像游戏那样无限试错。所以在选算法时要优先选“对分布外动作惩罚比较保守”的方案比如PPO、IQL少用DDPG这种容易过度估计Q值的。4. 训练过程怎么判断模型在真正学习4.1 训练协议和评估指标要分开设计训练中最常见的误区是把训练集的累计收益直接当成策略好坏的指标。训练过程中reward曲线上升未必说明策略真的学会了赚钱可能只是学会了在训练数据里过度适配。所以我会把训练协议和评估指标彻底分开。训练协议上我把历史数据切成三份训练集、验证集、回测集。强化学习里没有严格的监督学习式验证集概念但你可以把最近一段时间的样本当作验证集训练过程中每隔几步就用当前策略跑一遍验证集计算一个验证期内的收益曲线。注意这里的“每隔几步”很重要它相当于早停机制防止模型把训练集里的噪声也学进去。评估指标上我一般不只看累计收益或夏普比率这种单一数字而是看收益曲线的形状。一个健康的学习过程应该是这样的训练初期的收益曲线基本平缓或者略亏这是策略还在探索阶段然后曲线会开始缓慢抬升波动也变大最后波动率降低、曲线斜率稳定。如果你看到验证集曲线在训练早期就快速上涨多半是过拟合了因为模型在训练集里背下了太多规律并没有泛化能力。4.2 几个让我印象深刻的训练事故强化学习训练的不稳定性我在交易项目里体会特别深。讲几个真实遇到过的案例和排查过程。第一个是策略收敛到“一直满仓”的平庸解。出现这种问题时训练曲线看起来非常漂亮稳定盈利但拨开一看模型其实什么都没学就是把仓位长期放在最高值吃多头Beta。你要么在奖励里加强对仓位绝对值的惩罚要么在动作空间约束里限制最大仓位促使模型去主动选择仓位的增减时机而不是躺着吃市场平均涨幅。第二个是训练到一半收益曲线突然雪崩。这种通常发生在奖励函数里加入风险惩罚后惩罚权重设置过大模型发现“只要不交易就不会有惩罚”于是策略退化成永远空仓。解法是把风险惩罚改成不对称形式比如只在回撤超过阈值时才加重惩罚而不是每时每刻都在惩罚波动。回撤惩罚和波动率惩罚差别很大前者只在亏损扩大时触发后者则会对正常波动也产生抑制作用。第三个是验证集和训练集的收益曲线背离严重。训练集收益持续上升验证集却横盘甚至下滑。这个问题几乎都是分布偏移导致的。训练集里的市场状态和验证集差异太大尤其是2020年那种V型反转行情之后很多模型在单边下跌期学到的经验在震荡市里完全失灵。应对方法有两个方向一是扩充训练集的风格覆盖范围把不同市场状态的数据都放进去二是采用更保守的算法比如PPO本身就比SAC稳健。4.3 关键超参数怎么设交易策略强化学习的超参数没有标准答案但有经验区间可参考。折扣因子γ我一般设在0.95到0.99之间日线级别取0.98比较合适如果数据是分钟级γ可以适当放到0.995因为未来N步的收益影响时间跨度更长。学习率方面主要有两个地方需要设置actor网络和critic网络的学习率。我建议第一版都从3e-4左右开始然后观察训练曲线调整。如果模型很快陷入局部最优可以降低学习率同时增加探索噪声如果训练曲线剧烈震荡优先考虑降低actor学习率而不是动critic。PPO的clip参数我一般取0.1到0.2太大会导致更新步长过大太小会让学习过程太慢。批次大小batch size我习惯取256到512。批次太小梯度的噪声太大会让训练震荡批次太大会让每次更新太慢样本效率变低。回放缓冲区要考虑和你使用算法的关系PPO是on-policy算法理论上不需要很大的回放缓冲区但如果你用SAC这类off-policy算法缓冲区大小至少要存几十万步的样本否则容易出现老样本和新策略分布错配的问题。5. 回测与上线仿真做得再像也不是真实市场5.1 回测环境里最容易踩的三类隐性陷阱强化学习交易策略最容易产生“回测神话”的地方有三类我挨个说。第一类是前视偏差这个在第二章提到过但这里要强调一个更隐蔽的版本滑点估计。很多回测框架只按收盘价成交可是真实交易中你发出的订单可能要以对手价成交尤其当你仓位很大时你本身就是市场流动性的消耗者。处理办法是在回测时对成交价施加一个额外的滑点惩罚我一般用固定比例加上成交量冲击惩罚公式是exec_price market_price * (1 slippage_rate) # 买入时加价 exec_price market_price * (1 - slippage_rate) # 卖出时减价slippage_rate 根据品种流动性取0.0001到0.001不等。不要小看这个数字日频策略里每年几百次交易千分之一的滑点能吃掉好几个百分点的年化收益。第二类是幸存者偏差。回测标的池用现在还在上市的股票等于把自己局限在“活下来”的样本里凭空放大了收益。正确的做法是使用历史点位上的成分股和退市股一起回测或者在训练时加入退市风险惩罚。很多开源数据源都不含退市股票数据这一步需要自己额外补。第三类是用未来数据做特征标准化。我在2.1节提到过但回测框架里还有个变种在全量数据上算均值和方差再回溯到历史每一期。这等于模型在做决策时看到了未来数据分布的形状回测结果就会虚高。正确做法是把标准化参数固定在某一个时间点后面的数据只用这套参数做变换每滚动一段时间就重新校准一次。5.2 从仿真到实盘分布偏移和重训练频率即使回测做得再干净真实市场的不确定性也会让模型面临分布偏移。2024年初有一段行情不少之前训练好的强化学习交易模型连续回撤原因不是模型算法错了而是市场微观结构变化了比如成交量低迷、价差放大模型在历史数据里学会的“成交量放大之后往往有趋势”这类规律不再成立。面对分布偏移我的经验是两条腿走路一类是训练时的正则化在奖励函数里加入换手率惩罚让模型不要过度依赖某个高换手特征另一类是实盘后的在线监控把策略上线后的实时收益分布和回测收益分布做对比一旦差异超过设定阈值就触发重新训练流程。重训练的触发条件不要只看单日亏损而是看滚动窗口内的收益分布是否偏离回测分布超过两个标准差。另外我强烈建议在正式线上交易前至少做一轮模拟盘跑一个月。模拟盘虽然不能完全模拟真实市场的对手盘深度但能让你验证数据链路、订单执行和风控逻辑是否顺畅。有些人跳过这一步直接上真钱结果发现环境里报价和实盘报价差了几个tick策略执行后滑点远超预期。6. 常见问题速查与实操心得问题现象可能原因优先排查方向训练曲线始终亏损奖励函数中负惩罚过多降低风险惩罚权重简化奖励策略永远满仓或永远空仓动作空间约束太松/太紧平衡奖励缺失检查动作约束范围和奖励结构验证集表现远差于训练集过拟合、分布偏移增加数据覆盖、使用更保守算法回测好但模拟盘差滑点/手续费设置失真、前视偏差重新检查回测成交模型和标准化方式训练中收益曲线突然雪崩探索噪声过大、学习率过高、熵权重失衡降低学习率、增加惩罚的过渡平滑换手率异常高奖励里交易成本惩罚太低提高成本惩罚系数至真实成本的1.5-2倍再补充几个只有实际跑过才会发现的细节。第一个奖励函数里的rolling_volatility要用过去N期收益率的标准差不能用全样本方差。否则模型会在波动突变时反应不过来训练出来的策略在实盘中遇到波动率跳升时容易手足无措。第二个注意动作更新的频率。日线策略一天只做一个决策但分钟级策略一天可能做几百个决策计算量和交易成本压力完全不同。我之前做分钟级策略时把决策频率降到每5分钟一次收益和换手率都有明显改善说明高频决策并不一定等于高频收益。第三个多做几次独立随机种子训练。强化学习的随机性很大同一个环境同一个算法换一个随机种子可能得到完全不同的策略。我习惯用5到8个随机种子跑同一套实验然后看结果分布的中间值和中位数而不是只看最好的一次。只看最好种子很容易让你过分乐观因为那个结果可能只是运气好。第四个如果你打算把策略做成一个长期维护的系统建议把状态、动作、奖励、算法四层完全解耦用配置文件和接口去定义。我自己吃过亏第一版代码把奖励函数直接写死在环境类里后来想调一下风险惩罚权重废了很大劲而且很容易引入bug。重构之后每次实验只需要改配置文件整个迭代速度快了一倍不止。最后说一点真实的心得强化学习交易策略的学习过程本质上是在“理解市场的不完美”和“承受训练的不稳定”之间反复打磨。算法选型、奖励设计、环境搭建都重要但更重要的是让它成为一个可迭代的工程系统——每次训练失败都不是白费而是一个新的探索方向。这套方法我不会说它适合所有人但对那些愿意投入时间理解序列决策本质的量化从业者来说它带来的研究视角和通用框架能力确实值得认真投入。