
1. 从一笔模拟盘的爆仓说起为什么我要重做这套架构去年年底我在一个模拟盘上跑了一套自认为还不错的期货日内策略。特征工程做了三十多个因子回测夏普比率1.8最大回撤控制在12%以内看起来一切都很美好。结果实盘跑了两周净值曲线直接给我画了一个滑梯——不是策略逻辑出了问题而是整套架构在实盘环境下暴露了三个致命短板特征计算延迟导致信号滞后、风控模块和下单模块各自为政、模型在行情切换时没有任何自适应能力。那次之后我把整套系统推倒重来从特征工程到风控重新设计了一遍架构。这篇文章就是把这次重构的完整思路和实操细节拆开来讲——AI交易期货的完整架构到底该怎么搭特征工程怎么做才不掉坑风控模块怎么设计才能真正兜住底。适合有一定编程基础、想认真做量化交易系统的朋友参考不管你是刚入门还是已经跑过实盘应该都能从里面找到一些能直接抄作业的东西。先说清楚一个前提这篇文章讲的是架构层面的设计思路和工程实现不涉及任何具体的交易建议或收益承诺。期货交易本身风险极高任何系统都不能保证盈利架构做得再好也只是让你少犯低级错误。2. 整体架构设计为什么我选择“三层解耦”方案2.1 从单体到解耦一次痛苦的架构演进最开始我的系统是一个大Python脚本特征计算、模型推理、信号生成、下单执行、风控检查全塞在一个循环里。这种写法在回测阶段跑得挺欢因为回测是逐K线推进的每一步都是确定性的。但一到实盘就完蛋了——实盘的数据流是异步的行情推送、特征计算、模型推理、订单回报各自有不同的时间尺度你没法用一个同步循环把它们串起来。我试过用多线程硬扛结果就是GIL锁把CPU吃满特征计算延迟从回测时的几毫秒飙到实盘的几百毫秒。对于日内策略来说几百毫秒的延迟意味着你的信号价格和实际成交价格可能差出好几个跳。后来我改成了三层解耦架构数据层负责行情接入、数据清洗、特征计算输出标准化的特征向量决策层负责模型推理、信号生成、仓位计算输出目标仓位执行层负责风控检查、订单拆分、下单执行、持仓管理三层之间通过消息队列通信各自独立进程运行。这样做的好处是任何一层出问题不会直接拖垮其他层而且每层可以独立扩容和优化。比如特征计算慢了我只需要加一个计算节点不用动决策和执行层。2.2 为什么选消息队列而不是共享内存有人可能会问三层之间用消息队列通信不是增加了延迟吗共享内存不是更快我实测下来的结论是对于期货日内策略消息队列增加的延迟通常在几十微秒到几毫秒完全可以接受但它带来的解耦收益远大于这点延迟。共享内存的问题在于它要求所有模块对数据结构的理解完全一致一旦特征格式变了所有模块都得跟着改维护成本极高。而且共享内存的调试极其痛苦出了bug你根本不知道是哪个模块写坏了数据。我选的是ZeroMQ作为消息中间件原因是它足够轻量不需要额外部署broker而且支持多种通信模式PUB/SUB、PUSH/PULL、REQ/REP。数据层用PUB模式广播特征向量决策层用SUB模式订阅执行层用PULL模式接收目标仓位。整个通信拓扑非常清晰。注意消息队列的序列化格式建议用MessagePack或者FlatBuffers不要用JSON。JSON的序列化开销在高频场景下是不可忽视的我实测MessagePack比JSON快3到5倍。2.3 架构的容错设计三层解耦之后容错就变得很自然了。数据层如果挂了决策层收不到新特征会自动进入“无信号”状态不会乱下单。决策层如果挂了执行层收不到新目标仓位会维持当前仓位不动。执行层如果挂了风控模块会检测到心跳超时自动触发平仓。这里的关键是每一层都要有独立的心跳检测和降级逻辑。我的做法是每层每隔100毫秒发一次心跳到Redis其他层订阅心跳。如果连续3次收不到某层的心跳就认为该层已经失效触发相应的降级策略。3. 特征工程从三十个因子砍到十二个的思考过程3.1 特征不是越多越好过拟合的教训我最初那套系统有三十多个特征涵盖了价格、成交量、持仓量、技术指标、统计特征等各个维度。回测的时候IC值信息系数都挺好看但实盘一跑就发现很多特征在样本外完全失效。后来我复盘了一下发现问题的根源在于很多特征之间存在高度共线性而且部分特征在特定行情下才会生效行情切换后就变成了噪声。比如我用了RSI、KDJ、CCI三个动量指标它们本质上都在描述同一件事——价格变化的速率三个一起用不仅没有增加信息量反而放大了过拟合风险。砍特征的过程很痛苦因为每个特征在回测里都有贡献你舍不得删。但我的做法是用滚动窗口做特征重要性分析只保留在过去N个窗口中都稳定排在前列的因子。具体来说我用LightGBM的feature importance每500根K线重新计算一次统计每个因子在最近20个窗口中的平均排名和排名标准差。平均排名靠前且标准差小的因子保留其他的砍掉。最终从三十多个砍到了十二个样本外表现反而更稳定了。3.2 十二个核心特征的分类与计算逻辑保留的十二个特征分为四类第一类价格动量特征3个多周期收益率分别计算1分钟、5分钟、15分钟的对数收益率。为什么要多周期因为单一周期的收益率只能捕捉一个时间尺度上的动量多周期可以捕捉动量的持续性。如果1分钟和15分钟收益率同向说明短期和中期趋势一致信号更强。收益率偏度过去20根K线的收益率偏度。偏度为正说明上涨时的波动大于下跌时的波动市场情绪偏多。收益率峰度过去20根K线的收益率峰度。峰度高说明极端行情出现的概率大需要警惕。第二类成交量特征3个量比当前K线成交量与过去20根K线平均成交量的比值。量比大于1.5说明当前成交活跃信号可信度更高。成交量加权收益率用成交量加权的收益率比简单收益率更能反映真实的价格变化。主动买卖比用Tick数据计算的主动买入量和主动卖出量的比值。这个特征需要Level-2行情支持但效果非常好。第三类持仓量特征2个持仓量变化率当前持仓量与过去5根K线平均持仓量的比值。持仓量增加说明资金在流入趋势可能延续。持仓量加权价格用持仓量加权的平均价格反映市场的平均持仓成本。第四类市场微观结构特征4个买卖价差买一价和卖一价的差值反映市场的流动性。订单簿不平衡买一至买五的总量和卖一至卖五的总量的比值。大单净流入过去1分钟内大单超过一定金额的订单的净流入量。波动率过去20根K线的已实现波动率。这十二个特征的计算逻辑我都封装成了独立的函数每个函数接收原始行情数据输出一个标量。这样做的好处是每个特征可以独立测试和优化不会互相干扰。3.3 特征计算的性能优化特征计算是整套系统里最耗CPU的部分。我最初用Pandas做特征计算结果发现Pandas的滚动窗口计算在实盘环境下太慢了——每来一根新K线Pandas都要重新计算整个窗口时间复杂度是O(n)。后来我改成了增量计算的方式。以20根K线的滚动均值来说我不需要每次都重新算20个数的和只需要维护一个滑动窗口的和新K线进来时加上新值、减去最老的值时间复杂度降到O(1)。这个优化让特征计算延迟从几十毫秒降到了几毫秒。具体实现上我用的是NumPy的环形缓冲区ring buffer。每个特征维护一个固定长度的数组用一个指针记录当前写入位置新数据覆盖最老的数据。计算均值、方差、偏度等统计量时直接用NumPy的向量化操作在缓冲区上计算。import numpy as np class RollingWindow: def __init__(self, size): self.size size self.buffer np.zeros(size) self.idx 0 self.count 0 def update(self, value): self.buffer[self.idx] value self.idx (self.idx 1) % self.size self.count min(self.count 1, self.size) def mean(self): return np.mean(self.buffer[:self.count]) def std(self): return np.std(self.buffer[:self.count])这个类虽然简单但在实盘环境下比Pandas的rolling快了一个数量级。实操心得特征计算一定要做增量更新不要每次全量重算。我见过太多人用Pandas的rolling在实盘里跑延迟高得离谱还以为是网络问题。4. 模型推理与信号生成LightGBM为什么比深度学习更适合4.1 模型选型的权衡在期货日内交易这个场景下我最终选择了LightGBM而不是深度学习模型。原因有三第一样本量有限。期货日内策略的样本量通常不大尤其是高频策略一天可能只有几百个样本。深度学习模型需要大量数据才能训练好样本量不够很容易过拟合。LightGBM在中小样本上的表现通常更稳定。第二推理速度要求高。日内策略对推理延迟非常敏感LightGBM的推理速度在毫秒级别而深度学习模型即使经过优化推理延迟也通常在几十毫秒以上。第三可解释性。LightGBM可以输出特征重要性方便我分析哪些特征在起作用。深度学习模型是个黑盒出了问题很难排查。当然这不是说深度学习不能用。如果你的策略是低频的比如日线级别样本量足够大深度学习模型可能会有更好的表现。但在日内高频场景下LightGBM是更务实的选择。4.2 标签设计与样本权重模型训练的关键在于标签设计。我用的是三重障碍法Triple Barrier Method设置一个止盈障碍比如0.5%设置一个止损障碍比如-0.3%设置一个时间障碍比如持有20根K线后强制平仓标签的取值是先触及止盈障碍为1先触及止损障碍为-1先触及时间障碍为0。这种标签设计的好处是它同时考虑了收益和风险而且和实际交易逻辑一致。传统的固定时间窗口收益率标签的问题是它假设你会在固定时间后平仓但实际交易中你可能会提前止盈或止损。样本权重方面我给近期样本更高的权重因为市场结构会变化近期样本更能反映当前的市场状态。具体来说我用指数衰减权重半衰期设为500根K线。4.3 信号生成与仓位映射模型输出的是一个概率值表示在当前特征下价格先触及止盈障碍的概率。我设定一个阈值比如0.6当概率超过阈值时生成做多信号低于0.4时生成做空信号中间区域不操作。仓位大小用凯利公式的分数版本计算f (p * b - q) / b * k其中p是胜率q是败率b是盈亏比k是一个缩放系数我通常设为0.25。用分数凯利而不是全凯利是为了避免过度下注。实际仓位 账户权益 * f / 合约价值这个仓位计算方式比固定手数更合理因为它会根据模型的置信度和当前的盈亏比动态调整仓位。注意凯利公式对参数非常敏感p和b的估计误差会导致仓位计算偏差很大。我建议用滚动窗口估计p和b而且一定要用分数凯利不要用全凯利。5. 风控模块整套系统里最不能省的部分5.1 三层风控体系的设计风控是我这次重构中花时间最多的部分。我设计了三层风控体系第一层事前风控Pre-trade Risk Check在订单发出之前进行检查包括单笔订单最大手数限制单品种最大持仓限制账户总仓位限制单日最大亏损限制订单价格偏离限制防止乌龙指任何一项检查不通过订单直接被拒绝不会发到交易所。第二层事中风控In-trade Risk Monitor在持仓过程中持续监控实时盈亏监控保证金占用率监控持仓集中度监控市场波动率监控如果触发预警线会发出警报如果触发强平线会自动平仓。第三层事后风控Post-trade Risk Analysis在交易结束后进行分析当日交易复盘滑点分析策略表现归因风控规则有效性评估这一层主要是为了持续优化风控参数。5.2 关键风控参数的计算与设置风控参数不能拍脑袋定要有计算依据。我举几个例子单笔订单最大手数根据账户权益和品种波动率计算。max_lots account_equity * risk_per_trade / (atr * contract_multiplier)其中risk_per_trade是单笔交易愿意承担的风险比例我设为0.5%atr是品种的ATR值contract_multiplier是合约乘数。单日最大亏损设为账户权益的2%。这个数字是根据历史最大回撤和恢复时间确定的。如果单日亏损超过2%说明要么策略失效了要么市场出现了极端行情应该停下来检查。保证金占用率上限设为70%。期货是保证金交易保证金占用率过高会导致可用资金不足一旦出现不利行情容易被强平。留30%的缓冲是为了应对突发情况。持仓集中度上限单品种持仓不超过总仓位的30%单板块持仓不超过50%。这是为了分散风险避免某个品种或板块的黑天鹅事件导致整体账户大幅回撤。5.3 风控模块的工程实现风控模块我写成了一个独立的Python进程通过消息队列接收订单和持仓信息通过Redis存储风控状态。关键的风控检查逻辑如下class RiskManager: def __init__(self, config): self.max_lots config[max_lots] self.max_position config[max_position] self.max_daily_loss config[max_daily_loss] self.margin_limit config[margin_limit] self.daily_pnl 0 self.positions {} def check_order(self, order): # 检查单笔手数 if order[lots] self.max_lots: return False, 单笔手数超限 # 检查品种持仓 current_pos self.positions.get(order[symbol], 0) new_pos current_pos order[lots] if order[direction] buy else current_pos - order[lots] if abs(new_pos) self.max_position: return False, 品种持仓超限 # 检查单日亏损 if self.daily_pnl -self.max_daily_loss: return False, 单日亏损超限 return True, 通过这个类虽然简单但涵盖了最核心的风控逻辑。实际使用中还需要加上保证金计算、持仓集中度检查等。实操心得风控模块一定要独立于策略模块不能把风控逻辑写在策略代码里。我见过太多人把风控和策略混在一起结果策略一改风控就失效了。风控应该是最后一道防线独立运行独立部署。6. 实盘部署与运维那些文档里不会写的事6.1 部署架构与硬件选型实盘部署我用的是一台云服务器配置是8核16G放在交易所机房附近延迟更低。操作系统是Ubuntu 20.04Python版本是3.9。为什么选云服务器而不是本地机器因为云服务器的网络稳定性更好而且可以随时扩容。本地机器虽然延迟可能更低但一旦断电或断网损失可能远超省下的那点延迟。硬件方面CPU比GPU重要。LightGBM的推理不需要GPU但特征计算是CPU密集型的。我选的是高频CPU主频3.5GHz以上而不是多核低频CPU。6.2 监控与告警实盘系统必须要有完善的监控和告警。我用的方案是Prometheus Grafana AlertmanagerPrometheus采集各项指标延迟、吞吐量、盈亏、持仓等Grafana做可视化展示Alertmanager负责告警推送邮件、短信、即时通讯工具关键监控指标包括指标正常范围告警阈值特征计算延迟5ms20ms模型推理延迟10ms50ms订单成交延迟100ms500ms消息队列积压10条100条保证金占用率50%70%单日亏损-1%-2%这些阈值是根据我的实盘经验设定的不同策略可能需要调整。6.3 常见故障与排查实盘运行中遇到过不少故障我挑几个典型的说说故障一特征计算延迟突然飙升排查发现是某个特征的计算逻辑里有一个O(n)的操作当窗口内数据量增大时延迟线性增长。解决方法是改成增量计算。故障二订单被交易所拒绝排查发现是订单价格偏离太大触发了交易所的废单规则。解决方法是在下单前加上价格偏离检查超过一定阈值就拒绝下单。故障三消息队列积压排查发现是决策层的模型推理变慢了导致消费速度跟不上生产速度。解决方法是优化模型推理同时增加决策层的消费者数量。故障四风控模块误触发排查发现是风控参数设置过严正常波动也会触发。解决方法是根据历史数据重新校准风控参数。注意实盘系统一定要有完善的日志记录每个关键步骤都要打日志。出了问题第一时间看日志比瞎猜高效得多。7. 回测与实盘的差异为什么回测赚钱实盘亏7.1 回测中的常见陷阱回测和实盘的差异是量化交易里最坑人的地方。我踩过的坑包括未来函数用了未来数据做特征。比如用当根K线的收盘价计算特征然后假设能在当根K线开盘时下单。这是最隐蔽的坑因为回测里看不出来实盘里直接失效。滑点低估回测里假设按信号价格成交但实盘里你的订单可能排在队列后面成交价格差好几个跳。流动性假设回测里假设你的订单能全部成交但实盘里如果订单量太大可能只能部分成交。手续费低估期货的手续费包括交易所手续费和经纪商佣金不同品种差异很大回测里一定要算准。7.2 如何让回测更接近实盘我的做法是用Tick数据回测而不是K线数据。Tick数据能更真实地模拟成交过程。加入滑点模型根据订单量和市场流动性动态计算滑点。加入延迟模型模拟从信号生成到订单成交的延迟。用Walk-forward回测而不是简单的样本内回测。Walk-forward能更好地评估策略的样本外表现。Walk-forward的具体做法是把数据分成多个时间段用前一段训练模型后一段测试然后滚动前进。这样能模拟实盘中模型不断更新的过程。7.3 实盘表现监控与策略迭代实盘上线后我会持续监控策略的表现。关键指标包括滚动夏普比率过去20个交易日的夏普比率滚动最大回撤过去20个交易日的最大回撤胜率和盈亏比实时统计信号衰减模型输出的概率分布是否发生变化如果滚动夏普比率持续低于阈值或者滚动最大回撤超过阈值我会暂停策略重新检查模型和特征。策略迭代的节奏通常是每周做一次小复盘每月做一次大复盘。小复盘看当周的交易明细大复盘看整体的策略表现和风控有效性。8. 一些零散但重要的经验8.1 关于数据质量数据质量是量化交易的生命线。我遇到过好几次因为数据问题导致的亏损行情数据缺失导致特征计算错误数据时间戳错乱导致信号时序混乱数据源切换导致价格不连续解决方法是建立数据质量监控体系对每一批数据进行校验包括完整性检查、时序检查、异常值检查。发现问题数据立即告警不要等到策略出问题才发现。8.2 关于模型更新频率模型更新频率是个需要权衡的问题。更新太频繁模型不稳定更新太慢模型跟不上市场变化。我的做法是每周更新一次模型但每天监控模型表现。如果模型表现突然恶化立即触发紧急更新。更新模型时用最近半年的数据训练用最近一个月的数据验证。8.3 关于资金管理资金管理比策略本身更重要。我见过太多人策略不错但资金管理一塌糊涂最后亏得血本无归。我的资金管理原则是单笔交易风险不超过账户权益的0.5%单日最大亏损不超过账户权益的2%单周最大亏损不超过账户权益的5%单月最大亏损不超过账户权益的10%任何一项触发立即停止交易检查原因。8.4 关于心理因素量化交易虽然是用程序做决策但背后还是人在操作。心理因素对交易结果的影响非常大。我的经验是不要盯盘。程序跑得好好的你盯盘只会手痒想干预。我见过太多人因为盯盘时忍不住手动干预结果把程序化的优势全毁了。另外不要因为短期亏损就改策略。任何策略都有回撤期短期亏损不代表策略失效。我的做法是设定一个观察期比如20个交易日观察期内不做任何策略调整观察期结束后再评估。9. 后续可以扩展的方向这套架构目前跑得还算稳定但还有很多可以优化的地方。我列几个后续想尝试的方向第一引入在线学习。目前的模型是每周离线更新的如果能改成在线学习模型可以实时适应市场变化。不过在线学习的工程复杂度很高需要解决样本权重、灾难性遗忘等问题。第二多品种扩展。目前只跑了一个品种后续想扩展到多品种。多品种的好处是分散风险但挑战是不同品种的特征和模型可能需要分别优化。第三引入强化学习。目前的信号生成是监督学习标签是人工设计的。强化学习可以直接优化交易目标比如夏普比率不需要人工设计标签。但强化学习的样本效率低训练不稳定需要大量实验。第四优化执行算法。目前的执行是简单的限价单后续想尝试TWAP、VWAP等执行算法降低冲击成本。这些方向我还在探索中有进展了再分享。最后说一句AI交易期货不是印钞机架构做得再好也不能保证盈利。市场永远在变化今天有效的策略明天可能就失效了。保持敬畏持续学习控制风险才是长期生存的关键。我在实盘里踩过的坑远不止文章里写的这些有些坑甚至让我一度想放弃。但每次复盘之后总能找到改进的方向。这套架构也不是终点只是一个当前阶段相对稳定的版本后续还会继续迭代。