拓冰建站拓冰建站
首页 / 资讯中心 / 正文

多AI-Agent协作的量化工作台:盘前盘中盘后自动化闭环架构

前阵子有个读者给我留言问我一个人怎么盯盘前、盘中、盘后三套流程还要兼顾策略优化和风控是不是得组个团队才能扛下来。我当时正好在做QuantBot——一个多AI-Agent协作的量化工作台把从盘前数据准备、信号生成到盘中订单执行、风险监控再到盘后归因复盘和策略迭代的整条链路串成了自动化闭环。这篇文章就把QuantBot的整体架构、关键模块设计、踩过的坑、还有几处对新手最有价值的实操细节一次性讲清楚。这几年做过不少量化工具我的体会是单靠一个脚本或一个“超级Agent”硬撑全流程最终都会死在维护性和鲁棒性上。量化工作台真正难的不是某个模型有多聪明而是盘前、盘中、盘后每个环节都要在严格的时间窗口内各司其职同时还要能应对数据源异常、接口超时、持仓超标这类突发状况。QuantBot采用多AI-Agent协作模式本质上是把“一个人包办所有事”拆成“一支各司其职的团队”每个Agent只干一件事但干得足够深、足够稳。这篇文章适合谁看如果你是做量化开发的工程师或者正在搭建个人/团队量化系统又或者对AI-Agent工程化落地感兴趣那么QuantBot从任务编排、消息通信、状态管理到异常恢复的整套设计思路都会给你一个可参考、可复制的样板。哪怕你只是刚入门也能从中理解“为什么量化系统需要自动化闭环”以及“多Agent之间怎么协作才不会互相打架”。1. 为什么我会把QuantBot做成多Agent协作先说结论不是所有系统都需要多Agent但量化工作台这种“多阶段、强时序、高耦合、需要敏感响应”的系统非常适合用Agent协作来实现。QuantBot的核心逻辑并不难理解难的是让盘前、盘中、盘后不同阶段无缝衔接让信息在正确的时间到达正确的模块。1.1 单Agent方案为什么撑不住全流程一开始我确实尝试过用一个“超级Agent”统一处理所有任务。想法很简单一个大脑接收行情、新闻、持仓、策略信号然后自己做决策、生成订单、管理风控。实际运行后问题暴露得很快。第一个问题是状态混乱。盘中Agent既要盯实时行情又要管持仓和挂单还要每30秒跑一次风控检查。当同时出现异常价格跳动和撤单失败时Agent在上下文里来回切换经常把“当前仓位”和“目标仓位”搞混导致发出错误指令。第二个问题是延迟不可控。盘前阶段要处理几百个标的的财务数据、量价因子和新闻情绪盘中阶段要求毫秒级响应。把两件事塞进同一个上下文窗口数据预处理的时间会严重挤占盘中决策的时间我实测下来单Agent模式在数据量稍大的场景里平均决策延迟会是多Agent模式的3到4倍。第三个问题是故障蔓延。任何一步出错如果没有独立的隔离机制整个Agent会话就会崩溃。比如某天盘前新闻爬虫拿到一个格式异常的HTML解析错误直接污染了当天的全部分析结果。后来我把系统重新拆成盘前、盘中、盘后三大阶段每个阶段再拆为多个独立Agent每个Agent像一个独立的“员工”只处理自己职责内的任务。结构清晰了调试也友好很多。1.2 多Agent协作的核心原则职责单一、消息驱动、状态隔离QuantBot在架构上遵循三个原则。职责单一原则。每个Agent只处理一类任务。例如数据清洗Agent只负责数据标准化它不需要知道策略信号怎么计算风控Agent只负责检查持仓安全不需要关心信号来源。好处很明显修改某一环时不需要担心破坏其他逻辑。消息驱动原则。Agent之间不直接调用彼此的函数而是通过消息队列传递任务和结果。盘前新闻分析Agent完成后把“新闻情绪得分”以标准消息格式发送给信号生成Agent。信号生成Agent不需要关心新闻分析是怎么做的只消费消息队列里的结果即可。这样做使得每个Agent可以独立部署、独立扩展也方便故障重试。状态隔离原则。每个Agent维护独立的上下文状态。这很关键因为盘中Agent的上下文如果混入了盘前的几百条新闻文本token消耗和决策延迟都会变大。QuantBot把不同阶段的Agent使用独立状态空间必要时通过外部存储交互比如我会用Redis保存持仓快照和订单状态让各Agent都能读取但不需要共享上下文。我还为Agent之间的通信做了一层简单的“消息协议”。每一条消息都带消息ID、来源Agent、目标Agent、消息类型、时间戳和负载内容。由于消息可追踪一旦出错就能准确回放是哪一步导致了异常调试效率极高。2. 盘前环节从数据清洗到信号生成的Agent编排盘前是所有步骤的源头。这段时间做不好盘中再怎么优化都无济于事。QuantBot的盘前流程早晨6点30分启动一直运行到开盘前5分钟。任务划分得很细我拆成下面几个Agent按固定顺序可以编排。2.1 数据接入与清洗Agent这个Agent负责从多个数据源拉取行情、财务、资金流和新闻数据。它主要有两个难点。第一是格式统一。不同数据源返回的字段名不一致比如某源返回的“close”另一个源可能返回“收盘价”。QuantBot通过一张字段映射表做标准化统一转换为内部标准格式。映射表用YAML维护数据源格式变化时不需要改动Agent代码只更新映射表。第二是异常处理。数据源经常出现缺失、延迟和重复推送。我实测最烦的是除权除息日复权因子没同步好计算出的收益率曲线会跳出一根大阴线直接污染因子。数据清洗Agent会额外做一次复权因子校验发现某只股票价格单日变动超过阈值且成交额没有同步放大就标记为“疑似除权事件”并自动请求复权因子数据源重新校准。在实现上我用Pandas做清洗数据版本管理使用DVCData Version Control。每次跑完盘前任务会生成一个数据版本号后续如果需要回溯拼接过往因子可以直接使用该版本。def clean_market_data(raw_df: pd.DataFrame, calendar: list[str]) - pd.DataFrame: # 去除重复推送 df raw_df.drop_duplicates(subset[symbol, datetime], keeplast) # 统一列名 df df.rename(columns{收盘价: close, 开盘价: open}) # 按交易日历过滤非交易日数据 df df[df[datetime].dt.strftime(%Y-%m-%d).isin(calendar)] # 关键字段缺失直接剔除其他字段做前向填充 df df.dropna(subset[symbol, datetime, close]) df df.sort_values([symbol, datetime]).ffill() return df这段代码看着简单但实际运行中“前向填充”这个细节特别值得强调。如果某只股票停牌它的收盘价会停留在停牌前的数值盲目ffill会把停牌期间的无效价格当作正常行情。我在ffill之前加了一步当成交量等于0或为NaN时先把价格置为NaN不参与后续因子计算。2.2 主题新闻情绪Agent这个Agent处理盘前最重要的另类数据——新闻。以前我自己看新闻再决定交易方向效率很低A股每天盘前几百条公告和研报摘要根本看不过来。QuantBot的新闻Agent订阅了主要财经媒体和交易所公告流每条新闻进入系统后会经过三个步骤。第一步是文本清洗。去除HTML标签、广告噪声和重复段落。第二步是相关性过滤通过一个轻量级分类器判断新闻是否涉及持仓股票池或自选股池不相关的直接丢弃。第三步是情绪打分使用大模型对新闻标题和正文前500字做多空判断输出-1到1之间的情绪分同时提取涉及的公司名和行业。这里踩过一个坑大模型情绪分析对“业绩预增”和“业绩预增幅度不及预期”这种微妙差异把握不准。为了解决这个问题我给新闻Agent加了一条规则如果新闻来源属于上市公司公告且属于业绩变动类优先使用公告里的量化财务数据做规则判断而非只靠大模型语义。规则判断和大模型判断两者结合后准确率提升明显新闻驱动的信号质量也上来了。{ message_id: news_20240520_083012_001, source: news_sentiment_agent, target: signal_generator_agent, msg_type: sentiment_result, timestamp: 2024-05-20 08:30:12, payload: { symbol: 600519, sentiment_score: -0.32, headline: 公司发布2024年一季度业绩报告, related_industry: 白酒, source_type: announcement, confidence: 0.87 } }这是QuantBot内部的一则真实消息示例。信号生成Agent拿到这类消息后可以直接把新闻情绪作为因子之一参与当日信号评分。2.3 信号生成Agent与多因子融合逻辑信号生成Agent是盘前阶段的汇总节点。它订阅了数据清洗Agent处理后的量价因子、财务因子、技术指标以及新闻情绪Agent输出的情绪因子然后按策略配置的权重生成当日交易信号。信号生成公式不复杂核心是加权打分。我对每个标的维护一个“信号得分”得分是所有因子标准化后的加权和。标准化使用z-score防止量纲差异导致某个因子一票否决。但实际踩坑发现单纯加权打分在震荡行情会出现大量“假信号”。例如一个标的虽然综合得分只有0.2但因为某一项技术指标出现金叉容易触发买入条件。我给信号生成Agent增加了一致性校验只有当“量价因子、情绪因子、趋势因子”三类因子中至少两类同时给出同向信号时综合信号才有效。这个改动显著减少了信号噪声。信号生成Agent输出后所有信号会进入一个候选队列盘中执行模块从队列中按优先级取信号下单。这里我使用Redis的有序集合存储信号score是综合得分成员是信号ID。盘中Agent只需要从队列中pop最高分的信号即可天然按优先级调度。3. 盘中环节多Agent如何避免抢单、漏单与风险越权盘前阶段生成的信号只是“意愿”真正把意愿变成持仓的是盘中环节。盘中也是事故高发区。QuantBot在盘中拆成订单执行Agent、风控Agent、实时监控Agent三个模块分工协作。这里最容易出问题的是各Agent之间的配合和竞争。QuantBot的解法是所有指令都以消息形式传递由唯一的执行协调器仲裁不允许Agent直接访问交易接口。3.1 订单执行Agent把信号安全地变成订单订单执行Agent从Redis中按信号得分顺序取任务。每次取出一个信号后首先检查该信号是否过期。A股盘中信号的有效期一般不超过30分钟过期信号必须丢弃。如果信号没过期订单执行Agent会结合当前实时价格计算出建议的下单价格和数量再提交给协调器。为什么不让执行Agent直接调券商接口因为一旦网络卡顿或接口重试机制不完善很容易导致同一个信号被提交两次产生重复单。QuantBot里所有下单请求会先进入“待确认队列”执行协调器带着全局自增的订单号向后端提交后端返回的唯一委托编号会记录在数据库里。如果提交超时协调器会先查询委托状态确认“已提交”或“未提交”然后再决定是继续还是重试避免重复报单。交易成本控制也需要在执行Agent这里落实。我实测发现按对手价或最新价简单下单滑点吃掉了大量模拟盘利润。后来我给执行Agent加入了价格保护逻辑如果限价单在指定时间内没有成交可按当前市场价追单但追单条件不是无条件必须同时满足“信号仍然有效”和“偏离度不超过0.3%”两个条件。3.2 风控Agent全流程的安全员风控Agent是QuantBot中优先级最高的模块。它没有权限发出交易指令但它有一条“一票否决”通道可以直接向协调器发送“紧急熔断”消息。协调器收到熔断消息后会暂停所有新一轮下单并撤掉部分活跃订单。风控Agent盘中的检查项包括单票持仓上限、行业集中度、组合回撤、单日亏损阈值、撤单频率等。举例来说我设置的组合级回撤阈值是“当日净值回撤超过2%暂停新开仓”单票亏损阈值是“单票亏损超过5%强制平仓”。这里有个容易被忽略的点回撤的计算起点必须是今日开盘时的净值而不是昨日收盘时的净值。如果使用盘中最高净值作为基准风控容易被短期波动触发反而造成频繁熔断。QuantBot的做法是每天早上9点15分行情初始化后记录一次“当日基准净值”此后所有回撤判断都以该基准为参考。这个数值由状态管理模块单独存储不随Agent重启丢失。一个典型的风控Agent判定消息如下。{ message_id: risk_20240520_104512_017, source: risk_control_agent, target: order_coordinator, msg_type: kill_switch, timestamp: 2024-05-20 10:45:12, payload: { trigger: intraday_drawdown_breach, current_nav: 1000000.35, baseline_nav: 1020000.00, drawdown_pct: -1.96, action: pause_new_orders, affected_symbols: [] } }3.3 实时监控Agent与消息总线设计如果说执行Agent负责“做事”风控Agent负责“说不”那监控Agent就是QuantBot的眼睛。实时监控Agent每秒钟采集一次账户资金、持仓、委托回报和行情快照同时也要关注各Agent本身的心跳。Agent心跳监控很关键。由于每个Agent作为独立进程运行某个Agent可能因为网络异常、数据竞争等原因卡死。监控Agent如果发现某个Agent超过预期运行时长仍未完成当前任务就会发布一条告警消息。其他Agent收到告警后可以自动进入降级模式。比如信号生成Agent异常卡死时执行Agent会暂停取信号避免使用不完整的信号下单。QuantBot内部通信基于消息总线。盘中阶段消息量很大我在技术选型时对比过Redis Stream和RabbitMQ。最终还是选了Redis Stream作为主力。原因很简单盘中并发消息量在每秒数千条量级Redis Stream足够支撑而且Redis本身就用来存持仓和信号状态不需要额外维护一套消息中间件。每条消息会保存至少7天方便盘后做审计回放。RabbitMQ当然也能用但如果团队没有专职运维多一个组件就多一个故障点简化基础设施有助于提高稳定性。4. 盘后环节归因、复盘到策略自迭代的闭环很多人白天盯完盘就关机了这是很大的浪费。盘后才是积累复利的地方。QuantBot的盘后模块解决三个问题今天的交易到底赚在哪、亏在哪策略执行是否跟设计一致明天的策略参数是否需要微调。4.1 订单审计与执行质量归因Agent盘后第一步是对当天的订单做全量审计。审计Agent会把当天的委托记录、成交记录、撤单记录以及行情数据对齐然后逐笔计算滑点。我把滑点定义为“成交均价”和“信号发出时盘口最优价”之间的差异。做完滑点统计后还要做执行质量归因。例如某只票连续出现“委托价格偏低导致迟迟无法成交”的情况审计结果就会提示“限价单价格设置过于保守”。根据这些提示我会优化执行Agent的价格保护参数让限价单更贴近盘口。经过一周的数据反馈调整后持仓标的的平均滑点下降了约20%。Audit结果输出一张当天全任务的摘要表字段包括信号ID、标代码、信号方向、计划价格、成交价格、滑点、成交状态、所属Agent等。这份表格既能用于自动复盘也能帮我人工判断当天是否有异常动作。4.2 收益归因Agent把盈亏拆到因子和标的策略赚了还是亏了不能只看净值。收益归因Agent会将当天的组合收益拆解为三部分因子收益、交易成本和随机噪声。因子收益的计算方式是把当天每个持仓标的的收益按标的在关键因子上的暴露值进行加权回归。例如持仓组合在“动量因子”上暴露较高而当天动量因子上涨那么归因结果会显示“组合上涨中较大比例来自动量因子贡献”。如果某天动量因子大涨但组合收益平平说明信号持仓可能在因子暴露上发生了漂移这时需要去查信号生成Agent的权重配置。我用一个简化线性模型做不等式的每日归因[ R_p \sum_{i1}^{n} \beta_i \cdot F_i \alpha \varepsilon ]( R_p )是组合日收益( F_i )是因子日收益( \beta_i )是组合在因子上的暴露( \alpha )是超额收益项( \varepsilon )是回归残差。这个计算在Python里用statsmodels的OLS几分钟就能跑完。上面公式仅用于解释原理QuantBot内部会把计算结果落库存档。新手做归因时最容易犯的错是“把运气当能力”。某天市场整体大涨组合所有票都在涨新手会以为策略有效。收益归因Agent的价值就在于帮你区分今天赚钱到底是因为市场涨了还是因为选股确实选得好。这一点我自己体会很深看到归因结果后很多所谓“策略灵感”都被现实打消了但留下来的改进都是真正有价值的。4.3 策略迭代Agent与人工复核盘后闭环的最后一步是策略迭代Agent。它会汇总当天的信号质量、执行质量、归因结果和风控触发记录生成一份“策略健康度报告”并给出调整建议。例如如果某因子连续5天出现“信号收益均值低于0”策略迭代Agent会建议降低该因子权重。如果某个行业连续3天触发风控限制Agent会建议缩减该行业最大暴露比例。我要特别强调一点QuantBot的策略迭代Agent不会自动修改实盘参数。它只生成建议并写入“策略建议队列”由我在每天收盘后人工复核后再决定是否应用。为什么这么设计因为自动迭代最大的风险是过拟合。如果盘后流水线根据一天的数据自动调整参数策略会进入高频抖动的噪声状态。我设置的策略参数更新频率上限是每周一次除非遇到极端回撤或系统异常。这样既保留了自动化的效率又保留了人工判断的安全垫。盘后复盘做完后所有数据会汇总到每日报告通过内部推送发送到手机端。我每天早上通勤时直接看报告用不了五分钟就能对昨天的盘面、今天需要注意的风险点做到心里有数。5. 落地过程中频繁踩坑的高频问题与排查技巧QuantBot从第一版到稳定运行中间经历了大量修改。这里整理几个高频问题和排查思路对于刚搭建量化Agent系统的朋友最值得参考。5.1 Agent之间消息积压盘中信号处理延迟现象早盘开盘瞬间行情波动大信号生成Agent瞬时产生较多任务订单执行Agent消费不过来消息积压持续几十秒。排查思路首先检查消费者的处理耗时。我通过在消息处理函数开头结尾打时间戳确认单个下单请求平均耗时60毫秒其中网络开销占40毫秒。优化方向从两个维度同时推进一是把目标行情快照、账户持仓、最新委托状态提前缓存到本地减少下单前不必要的接口调用二是提高消费并发度用Python asyncio做异步处理将单个请求耗时压到20毫秒左右。如果并发度提到最高仍然积压就要反过来考虑是不是消费者被某个慢任务阻塞。为避免慢任务阻塞后续任务我把耗时操作如发送公告级消息从主流程摘除转移到一个独立的瞬时一致队列处理。5.2 重复下单和遗漏撤单的根源量化系统最怕的不是亏钱而是出现重复单或漏撤单。由于订单提交后网络超时Agent会重试如果重试逻辑不严谨同一个信号就会被提交两次。排查后发现原来问题出在我给订单执行Agent加的“超时重试”逻辑上。当提交订单接口抛出超时异常时Agent会直接重新执行下单函数而不是先查询原订单是否已经提交成功。修复方案是引入“幂等控制表”下单前先在该表里写入一个本地生成的请求ID重试前先查该请求ID是否已有对应委托号如果委托号不存在再重新提交。这套机制上线后重复单的问题基本消失了。5.3 盘前异常没有及时暴露导致盘中策略用了脏数据现象某天盘前上游数据源只更新了部分股票的数据导致部分股票缺少当日收盘价。由于清洗Agent做了前向填充缺失价格被填充为前一日价格信号Agent无法感知数据异常盘中依旧产生交易信号。解决办法是在数据清洗Agent的输出环节增加“数据新鲜度完整性检查”。系统统计每个标的最新数据时间戳如果某标的最新数据时间戳早于预期时间超过阈值则不允许该标的进入信号生成候选池同时发送告警消息提醒“标的因数据缺失被剔除”。从此以后盘中不会再出现“因脏数据给的信号”这类问题。5.4 行情端剧烈波动引发高频熔断刚开始做风控时我把熔断阈值设得较紧结果遇到一次大盘盘中急速拉升又回落风控Agent连续触发暂停新开仓导致信号执行率骤降。后来我给风控加上了“确认机制”当某风控条件被触发时首先进入预警状态持续观察5秒如果条件持续满足才会真正触发熔断。这可以过滤掉短时毛刺带来的假突破。针对量化系统高频踩坑场景我整理了一个速查表方便大家对照排查。问题疑似根因排查手段常用解法消息积压消费者处理慢在各环节打印耗时异步化、本地缓存、拆分慢任务重复下单网络超时后盲目重试检查幂等控制表引入本地唯一请求ID先查后订脏数据污染信号数据清洗阶段前向填充查看最新数据时间戳增加新鲜度校验剔除过期标的频繁熔断风控阈值过窄查看熔断触发记录5秒连续确认机制Agent重启丢状态上下文状态没有持久化对比重启前后的日志状态存储独立移入Redis等外部存储5.5 Agent协作中的联调技巧与日志追踪多Agent系统联调时最大的障碍是问题归因困难一个错误的信号到底是数据Agent算错了还是信号Agent配置错误还是消息传输中字段被篡改QuantBot靠两层设计来简化这个问题。第一层是追踪ID贯穿全链路。每条信号从消息源头创建时就会带一个全局唯一的信号ID后续所有Agent在处理该信号时都会把信号ID写进自己的日志上下文。排查时只需按信号ID搜索日志整条处理链路的轨迹立刻浮现。第二层是消息录制回放功能。消息总线上的每条消息都会同步存一份副本。当某天的盘前任务结果异常时我可以把当天的消息副本重新灌入测试环境在本地调试并观察每个Agent对同一批消息的输出。这个功能对团队协作尤其重要因为可以随时随地给同事提供一个“必现问题”的测试用例。6. QuantBot后续演进与个人实操建议QuantBot现在的版本已经稳定运行数月盘前、盘中、盘后全流程基本无人值守。但我在实际使用中还是发现了一些可以优化的方向和值得注意的细节。6.1 从“多Agent协作”到“多策略组合”的扩展目前QuantBot的信号生成Agent是统一的多因子融合相当于一个组合策略跑所有资金。但对于多策略资金的实盘分配需要更加智能的策略路由。例如趋势策略和套利策略在风险收益特征上差异巨大共用一套风控参数并不合适。下一步我计划将信号生成Agent进一步拆分为“策略子Agent”组每个策略维护独立的信号流和风控边界由一个“组合管理Agent”统一调度资金分配。组合管理Agent根据各策略当前的风险敞口、历史胜率和相关性动态调节资金分配比例。这个扩展可以沿用现有的消息总线通信模型不改动底层基础设施。6.2 自动化闭环中的“人机回退”机制即使QuantBot的自动化程度已经很高我仍然保留了人工干预通道。每天早上开盘前我会审核一遍“当日候选信号池”如果市场出现了极端环境信号或者策略建议池与我对盘面的判断差异过大可以一键暂停整个自动交易流程。提供几个实操建议设计Agent系统时一定要预留人工回退开关这个开关一定是物理级和逻辑级双重保障不要只依靠程序内部逻辑每个Agent在启动时和每日收盘后都做一次配置校验防止因数据驱动参数更新而引入了非法值消息总线上的保留策略要足够长别为了省存储而牺牲事后审计能力我保留至少7天的消息记录效果很好。6.3 个人对多Agent量化系统的核心体会如果非要用一句话总结QuantBot的设计经验我会说多Agent架构不是为了炫技而是为了控制复杂度。量化工作台的复杂度来自多阶段任务的时间约束、多数据源的不确定性、交易执行对一致性的严苛要求这些需求叠加在一起单Agent会迅速逼近能力边界。我建议每个准备搭建量化Agent系统的朋友先别急着写代码。第一步把盘前、盘中、盘后所有动作列成一个任务清单标出哪些任务必须顺序执行、哪些可以并行、哪些必须人审第二步再根据任务清单去划分Agent边界第三步再谈Agent之间怎么通信、消息怎么定义。从实际经验看任务边界划分比技术选型对系统稳定性的影响更大。QuantBot后续如果有了新的进展我还会继续分享。量化交易这一行做得越久越明白稳定、可解释、可回放地赚钱才是一条可持续的路。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门