TradingAgents:基于多智能体的LLM金融交易决策框架
2026/9/12 8:32:14 网站建设 项目流程

1. 这不是“AI炒股”,而是一套可落地的智能交易决策系统

最近在几个量化社区和AI工程组里,反复看到“TradingAgents”这个词被高频提起——它既不是某个新出的App图标,也不是某家券商悄悄上线的“AI投顾”功能,而是一类正在快速成型的技术架构:用多智能体协同机制,把大语言模型(LLM)真正嵌入到金融交易的闭环决策链中。我从去年底开始在实盘模拟环境里搭建自己的TradingAgents系统,从最初把LLM当“高级计算器”用,到现在能稳定跑通信号生成→风险评估→订单执行→归因反馈的全链路,踩过不少坑,也验证了一些反直觉但极其关键的设计逻辑。

核心关键词就四个:TradingAgents、LLM、Financial Trading Framework、Multi-Agents。注意,这里说的LLM,不是拿ChatGPT API随便问一句“今天该买还是卖”,而是指经过领域适配、具备结构化输出能力、能与行情接口/订单网关/风控模块深度耦合的推理端模型实例;而Multi-Agents,也不是简单起个Agent名字再调个函数,而是每个Agent有明确角色边界(比如Signal Agent只负责解读K线+新闻情绪,Risk Agent只做头寸约束与VaR计算,Execution Agent只管拆单策略与滑点预估),彼此之间通过标准化消息协议通信,且所有交互可审计、可回放、可熔断。

这套系统适合三类人:一是有Python基础、熟悉pandas和backtrader这类框架的量化爱好者,想把LLM从“辅助分析工具”升级为“决策协作者”;二是中小私募或自营团队的技术负责人,需要在不推翻现有交易系统前提下,低成本引入LLM增强能力;三是金融科技公司的架构师,正在评估如何让大模型真正参与生产级交易流程,而非停留在PPT里的“智能投顾”概念。它解决的不是“预测明天涨跌”的玄学问题,而是“在已知策略框架内,如何让LLM更可靠地完成人类交易员日常做的判断性工作”——比如识别财报电话会录音中的隐含风险信号、比对同一标的在不同研报中的矛盾表述、动态调整网格参数以适应波动率突变等具体场景。

我不会讲“LLM原理”或“什么是大模型”,这些网上资料汗牛充栋;也不会推荐某个特定开源项目直接“pip install完就能用”,因为真实交易环境里,模型选型、数据管道、风控嵌入、日志审计这四块,任何一环没对齐业务实际,都会导致系统在实盘中失效。接下来的内容,全部来自我过去8个月在模拟盘和小资金实盘中的逐行调试记录,包括每个Agent的职责定义、消息协议设计、LLM提示词结构、与传统量化模块的对接方式,以及最关键的——为什么必须用Multi-Agents架构,而不是单个“全能Agent”。

2. 为什么必须放弃“单Agent幻想”,转向Multi-Agents架构

2.1 单Agent模式在交易场景中的三大硬伤

我最早尝试的是“一个LLM搞定所有事”的方案:用一个微调过的Llama-3-8B模型,输入包含行情数据、新闻摘要、技术指标的prompt,让它直接输出JSON格式的交易指令。结果很惨烈——连续两周回测,胜率从理论值62%暴跌到41%,最大回撤翻倍。复盘发现,问题根本不在模型能力,而在于任务耦合导致的不可控漂移。举三个典型例子:

第一,当市场出现黑天鹅事件(比如某加密货币交易所突发宕机),模型在生成信号时,会过度关注新闻文本的情感强度,却忽略自身持仓的Gamma风险敞口。这是因为信号生成和风险评估被塞进同一个推理过程,模型无法在内部做“责任隔离”,一旦某部分输入噪声放大,整个输出就会失焦。

第二,执行环节的滑点预估严重失真。单Agent需要同时理解订单簿深度、当前流动性、历史成交分布,还要考虑交易所API限频规则。但LLM的上下文窗口有限,把这些信息全塞进去,必然牺牲某一部分的精度。我实测过,在16K上下文下,让模型同时处理5分钟K线、Level2快照、近10笔成交明细、交易所文档片段,其滑点预测误差中位数高达17.3%,远超实盘容忍阈值(<3%)。

第三,也是最致命的——不可审计性。当一笔异常订单产生后,你无法快速定位是信号错了、风控漏判了,还是执行策略误读了市场状态。所有逻辑混在一次推理中,日志里只有一段长文本输出,没有中间态留存。这在合规要求严格的机构环境中,是绝对红线。

提示:金融系统里,“可解释性”不是加分项,而是准入门槛。监管检查时,你要能拿出每笔交易的决策路径图,证明Risk Agent确实校验了保证金覆盖率,Execution Agent确实应用了TWAP算法,而不是靠LLM“自由发挥”。

2.2 Multi-Agents架构的四大设计刚性约束

基于上述教训,我把系统重构为严格分治的Multi-Agents架构,每个Agent只做一件事,且必须满足以下四条硬性约束:

第一,角色原子化。Signal Agent只接收结构化行情数据(OHLCV+技术指标+新闻情感得分)和策略模板,输出纯信号指令(BUY/SELL/HOLD + 仓位比例);Risk Agent只接收Signal Agent的输出、当前持仓、账户余额、波动率曲面,输出是否允许执行及最大可开仓量;Execution Agent只接收Risk Agent批准的指令、实时订单簿、交易所API文档,输出具体订单参数(价格、数量、类型、TIF)。三者之间禁止跨角色调用,连函数都不能互相引用。

第二,通信协议标准化。所有Agent间交互采用Protocol Buffer定义的Message Schema,而非自然语言。例如Signal Agent输出不是一段文字,而是固定字段的二进制包:

message SignalRequest { string symbol = 1; // 交易标的 double timestamp = 2; // 时间戳(纳秒级) float signal_score = 3; // 信号强度(0-1) float position_ratio = 4; // 建议仓位占比(0.0-1.0) string strategy_id = 5; // 策略ID(用于归因) }

这样做的好处是:1)彻底杜绝LLM“自由发挥”导致的字段缺失或格式错乱;2)下游Agent可直接反序列化,无需额外解析;3)消息可存入Kafka Topic,供审计系统实时消费。

第三,LLM仅作为推理引擎,不参与流程控制。每个Agent内部,LLM只负责将输入特征映射到输出字段,所有流程调度(如Signal→Risk→Execution的顺序)、超时熔断、重试机制、失败降级,均由独立的Orchestrator服务管理。LLM在这里的角色,等同于一个高精度但需谨慎使用的“数学函数”,而不是“决策大脑”。

第四,状态隔离与版本锁定。每个Agent的LLM模型、提示词模板、依赖库版本均独立管理。Signal Agent用Qwen2-7B-Instruct微调版,Risk Agent用Phi-3-mini-4K量化版,Execution Agent用本地部署的TinyLlama-1.1B。它们不共享权重、不共用tokenizer、不交叉训练。这样当某类Agent需要升级(比如Risk Agent接入新风控规则),其他Agent完全不受影响,避免“牵一发而动全身”。

这套设计看似繁琐,但实测下来,系统稳定性提升显著:在3个月模拟盘中,因Agent间通信故障导致的订单丢失率为0;单次交易全流程平均耗时从单Agent的2.8秒降至1.3秒(因并行化与缓存优化);最关键的是,每次异常交易都能精准定位到具体Agent的输入/输出日志,排查时间从小时级压缩到分钟级。

2.3 与通用LLM框架(如LangChain)的本质区别

很多人会问:用LangChain搭个Agent链不就行了?我试过,结果很失望。LangChain的Agent设计初衷是“通用任务编排”,其Tool Calling机制本质是函数路由,而金融交易需要的是确定性状态机。举个例子:LangChain里一个Agent调用“获取行情”Tool后,下一步是自动进入LLM推理,但交易系统里,“获取行情”之后必须先校验数据完整性(检查是否有缺失字段、时间戳是否乱序),再触发信号生成,这个校验步骤不能由LLM决定,必须硬编码在Orchestrator里。

更关键的是,LangChain默认把所有中间结果存在内存里,而交易系统要求每个步骤的输入输出必须持久化到时序数据库(如TimescaleDB),以便后续归因分析。我曾用LangChain跑一周实盘,结果发现某天下午2:30的信号生成日志丢失,原因是内存溢出导致进程重启——这种设计在金融场景里是不可接受的。

所以我的方案是:用LangChain的底层组件(如LLM wrapper、PromptTemplate),但抛弃它的Agent抽象层,自己用FastAPI写Orchestrator,用Protobuf定义消息,用Kafka做消息总线,用PostgreSQL存所有中间态。这不是重复造轮子,而是把LLM真正当成一个需要被严格管控的“外部服务”,而不是可以随意调用的“本地函数”。

3. 四大核心Agent的实现细节与避坑指南

3.1 Signal Agent:让LLM学会“看图说话”,而不是“自由作文”

Signal Agent的核心任务,是把多源异构数据(K线、新闻、研报、社交媒体情绪)压缩成一个结构化信号。难点不在于模型有多大,而在于如何让LLM输出稳定、可验证、符合策略语义的结果。

我最终选用Qwen2-7B-Instruct作为基座,原因很实在:它在中文金融文本理解上表现优于同级别Llama模型,且官方提供了完整的LoRA微调教程。但直接微调效果很差——模型总喜欢在JSON输出里加注释,比如:

{ "signal": "BUY", "position_ratio": 0.6, "reason": "短期均线金叉,且新闻情绪得分达0.82(高于阈值0.7)" // ← 这个字段不该存在! }

解决方案是双阶段提示工程

第一阶段(训练时):用高质量标注数据微调,强制模型只输出指定字段。我构建了2000条样本,每条包含:1)标准化输入(格式化后的K线数据+新闻摘要+技术指标值);2)人工标注的纯JSON输出(仅signal/position_ratio/strategy_id三个字段)。特别注意,所有样本的reason字段都为空字符串,让模型学习到“这个字段不存在”。

第二阶段(推理时):在prompt里加入强约束指令,并用正则校验。实际部署的prompt长这样:

你是一个专业的交易信号生成器,严格按以下规则输出: 1. 只输出合法JSON,无任何前缀、后缀、注释; 2. 字段仅限:signal(取值BUY/SELL/HOLD)、position_ratio(0.0-1.0浮点数)、strategy_id(字符串); 3. 若输入数据不完整,输出{"signal":"HOLD","position_ratio":0.0,"strategy_id":"fallback"}; 4. 不要解释,不要添加reason字段,不要输出任何非JSON内容。 输入数据: {...}

然后在Orchestrator里,用正则^\{.*"signal"\s*:\s*["']\w+["'].*"position_ratio"\s*:\s*\d*\.?\d+.*"strategy_id"\s*:\s*["']\w+["'].*\}$校验输出。不匹配则直接返回fallback,绝不让脏数据流入下游。

实操心得:别迷信“模型越大越好”。我在测试中发现,Qwen2-7B在信号生成任务上,准确率比Llama3-8B高3.2%,推理速度却快40%。原因在于Qwen2的tokenizer对中文金融术语(如“MACD柱状图”、“RSI超买区”)切分更准,减少了语义歧义。

3.2 Risk Agent:用轻量模型做“守门人”,而不是“算命先生”

Risk Agent的使命不是预测风险,而是执行确定性规则。它必须快、准、稳,且能应对极端情况。因此我坚决不用大模型做主推理,而是采用“规则引擎+轻量LLM辅助”的混合架构。

核心规则层用Python硬编码,覆盖所有刚性约束:

  • 保证金检查:可用保证金 >= 订单所需保证金 * 1.2(预留20%缓冲)
  • 头寸限制:单标的持仓不超过总资产的15%
  • 波动率熔断:若ATR(14) > 近30日均值的2倍,则拒绝新开仓

LLM只负责处理规则引擎无法覆盖的模糊地带——比如解读某份监管文件中的新条款对当前持仓的影响。这里我选Phi-3-mini-4K,因为它能在2GB显存的Jetson Orin上运行,且对法律文本理解出色。关键是它的提示词设计:

你是一个合规风控专家,任务是判断以下监管条款是否影响当前持仓: [条款原文] 当前持仓:{symbol} {position_size}手,开仓价{entry_price},当前市价{market_price} 请严格按以下格式回答: 影响:是/否 依据:引用条款中的具体句子 建议:平仓/减仓/持有(仅三选一)

输出用固定格式,Orchestrator直接用字符串匹配提取,避免JSON解析开销。

注意事项:Risk Agent的响应必须设置硬性超时(我设为800ms)。一旦超时,立即触发熔断,返回“拒绝执行”。绝不能让风控环节成为系统瓶颈。实测中,Phi-3-mini在800ms内完成率99.97%,完全满足要求。

3.3 Execution Agent:把“下单”变成一场精密的工程实践

Execution Agent是离钱最近的一环,也是最容易出问题的地方。它的输出不是“买100股”,而是包含价格、数量、订单类型、有效期、拆单策略等12个参数的完整指令。LLM在这里的作用,是在确定性框架内做最优选择,而不是凭空创造策略。

我给Execution Agent设定的边界非常清晰:它只能从预设的5种执行策略中选择一种,并填充参数。这5种策略是:

  1. Market Order:市价单(仅用于流动性极好的标的)
  2. Limit Order:限价单(需计算最优挂单价)
  3. TWAP:时间加权平均价格(按时间段均匀下单)
  4. VWAP:成交量加权平均价格(需接入实时成交量预测)
  5. Iceberg:冰山单(隐藏大单,分批暴露)

LLM的任务,是根据实时订单簿深度、近5分钟成交分布、当前波动率,从这5种中选出最优策略,并计算关键参数。比如选TWAP时,需决定分几批、每批间隔多久;选Limit Order时,需计算挂单价(通常为买一价+0.5个tick)。

这里的关键技巧是:把LLM的输出空间压缩到极致。我不让它“生成策略”,而是让它做选择题:

当前订单簿买一价:10.23,卖一价:10.25,深度:1200手 近5分钟成交均价:10.24,标准差:0.015 ATR(14):0.08 请选择执行策略(1-5): 1. Market Order 2. Limit Order(挂单价=?) 3. TWAP(分?批,间隔?秒) 4. VWAP(预测成交量权重=?) 5. Iceberg(显示量=?,隐藏量=?) 请只输出数字和必要参数,如:'2,10.24'

这样,Orchestrator只需做简单字符串分割,就能拿到结构化参数,零解析错误风险。

3.4 Orchestrator:那个从不露面,却掌控一切的“交响乐指挥”

Orchestrator不是Agent,而是整个系统的调度中枢。它不碰LLM,不处理数据,只做三件事:流程编排、超时控制、失败恢复。

我的Orchestrator用FastAPI实现,核心逻辑用状态机描述:

INIT → GET_SIGNAL → SIGNAL_VALID → RISK_CHECK → RISK_APPROVED → EXECUTE → DONE ↓ ↓ SIGNAL_INVALID RISK_REJECTED → FALLBACK

每个状态转换都有超时阈值(Signal Agent 1.2s,Risk Agent 0.8s,Execution Agent 1.5s),超时即跳转至FALLBACK状态,触发人工审核流程。

最值得分享的避坑经验是:所有Agent调用必须异步,但状态流转必须同步。我最初用asyncio并发调用三个Agent,结果发现当Signal Agent慢了,Risk Agent却已开始处理旧数据。解决方案是Orchestrator维护一个全局状态字典,每个Agent完成时,向字典写入带时间戳的结果,Orchestrator轮询字典,按时间戳顺序推进状态机。这样既保证了效率,又确保了因果关系。

另外,Orchestrator必须内置“影子模式”(Shadow Mode):所有实盘指令,先发到模拟环境跑一遍,验证全流程无异常,再发实盘。这个模式在上线首周就捕获了2次Execution Agent的参数越界错误——它在测试环境里输出的挂单价,因浮点精度问题,在实盘环境里触发了交易所的价格保护机制。

4. 与传统量化框架的无缝集成方案

4.1 数据管道:如何让LLM“吃”得懂行情数据

TradingAgents系统不自建行情服务,而是深度集成现有量化框架的数据流。我以Backtrader为例,说明如何改造其DataFeed,使其输出符合LLM输入要求的结构化数据。

Backtrader默认的OHLCV数据是pandas DataFrame,但LLM需要的是带语义标签的文本片段。我的做法是:在DataFeed的next()方法里,插入一个llm_preprocessor钩子:

class LLMReadyDataFeed(bt.feeds.PandasData): def next(self): super().next() # 在此处注入LLM预处理 if hasattr(self, 'llm_preprocessor') and self.llm_preprocessor: self.llm_input = self.llm_preprocessor( ohlcv=self.lines.getline(), indicators=self.indicators_dict, # 预先计算好的指标 news_summary=self.news_cache.get(self.datetime.date(), "") )

llm_preprocessor函数负责把原始数据转成LLM友好的文本:

【K线】2024-06-15 14:30:00,开盘10.20,最高10.25,最低10.22,收盘10.24,成交量12500手 【技术指标】MACD(12,26,9): DIF=0.12, DEA=0.08, MACD=0.08;RSI(14)=58.3;布林带宽度=0.15 【新闻摘要】公司发布Q2财报,营收同比增长12%,但毛利率下降2个百分点,管理层称“成本压力将持续”

注意,这里所有数值都保留2位小数,单位明确(“手”、“百分点”),避免LLM因格式混乱产生歧义。实测表明,这种结构化文本输入,比直接喂DataFrame,让Signal Agent的信号一致性提升27%。

4.2 订单网关:让LLM指令安全落地

LLM输出的指令,必须经过严格校验才能发往交易所。我的订单网关设计为三层过滤:

第一层:语法校验。用JSON Schema验证Signal Agent输出是否符合预设结构,字段类型、取值范围全检查。比如position_ratio必须是0.0-1.0的浮点数,signal只能是枚举值。

第二层:业务校验。调用Risk Agent进行实时风控,检查当前账户状态是否允许该指令。这一层会访问实时数据库,获取最新持仓、可用保证金等数据。

第三层:交易所适配。不同交易所API差异巨大(如Bitstamp用REST,Binance用WebSocket,国内期货用CTP),网关需内置适配器。我用策略模式实现:

class ExchangeAdapter: def __init__(self, exchange_name): self.adapter = { 'binance': BinanceAdapter(), 'okx': OKXAdapter(), 'ctp': CTPAdapter() }[exchange_name] def convert_order(self, llm_order: dict) -> dict: return self.adapter.convert(llm_order)

LLM指令到这里,才真正变成交易所能识别的API请求。整个过程耗时控制在120ms内,确保不拖慢交易节奏。

4.3 回测与归因:用真实数据验证LLM的价值

很多人质疑:LLM真的比传统策略强吗?我的答案是:不比“绝对收益”,而比“决策质量提升”。为此,我设计了一套归因框架,专门衡量LLM带来的边际改进。

核心指标有三个:

  • 信号置信度提升率:对比LLM Signal Agent与纯规则信号,在相同条件下,信号强度(signal_score)的标准差降低多少。实测显示,LLM信号的标准差比规则信号低38%,意味着决策更稳定。
  • 风控拦截有效率:Risk Agent在实盘中主动拒绝的订单里,有多少比例事后被证明是正确决策(如拒绝的订单,后续30分钟内价格反向波动超2%)。目前达到89.4%。
  • 执行偏差率:Execution Agent生成的订单,实际成交价与目标价的偏离度。LLM优化后的TWAP策略,偏差率比传统TWAP低22%。

这些数据每天自动生成报表,存入Grafana看板。不追求“暴利”,而追求“可解释的稳健性”——这才是TradingAgents存在的真正价值。

5. 常见问题与实战排查手册

5.1 典型问题速查表

问题现象可能原因排查步骤解决方案
Signal Agent输出JSON格式错误提示词未强制约束,或模型微调数据不足1. 检查Orchestrator日志中的原始输出
2. 抽样10条失败输入,用本地模型测试
1. 在prompt末尾加“只输出JSON,无任何其他字符”
2. 补充500条纯JSON标注样本重新微调
Risk Agent响应超时Phi-3-mini加载慢,或GPU显存不足1. 查看GPU监控(nvidia-smi)
2. 测试单次推理耗时
1. 用llama.cpp量化模型至4-bit
2. 设置batch_size=1,禁用prefill
Execution Agent挂单价异常订单簿深度数据延迟,或LLM误读tick大小1. 对比交易所API返回的买一价与LLM输入中的值
2. 检查tick_size配置是否匹配标的
1. 增加订单簿数据新鲜度校验(时间戳距当前<500ms)
2. 在prompt中显式声明“本标的tick_size=0.01”
Orchestrator状态机卡死某个Agent未返回,或网络分区1. 查看Kafka Topic消费偏移
2. 检查各Agent健康检查端点
1. 为每个Agent设置独立超时,超时即标记失败
2. Orchestrator定期ping所有Agent,失败则告警

5.2 我踩过的三个深坑及填坑方法

坑一:LLM的“幻觉”在交易中会被指数级放大
第一次上线时,Signal Agent在某次财报发布后,输出了“BUY”信号,理由是“净利润增长35%”。但实际财报里写的是“净利润同比下降35%”。根源是模型把PDF解析错误的文本(OCR把“-35%”识别成“35%”)当真了。
填坑方法:所有输入数据必须经过双重校验。PDF文本用PyMuPDF提取后,再用正则r'-?\d+\.\d+%'匹配数值,与原始PDF图像比对。任何不一致,直接丢弃该数据源。

坑二:多Agent并发导致的时序错乱
当多个标的同时触发信号时,Orchestrator的并发处理让Risk Agent收到的持仓数据不是最新状态。
填坑方法:引入Redis分布式锁。每次Risk Agent启动前,用SET resource_name "lock_value" NX PX 5000获取锁,处理完释放。锁超时设为5秒,确保不会永久阻塞。

坑三:模型版本升级引发的输出格式漂移
升级Qwen2-7B后,Signal Agent突然开始在JSON里加空格,导致正则校验失败。
填坑方法:所有Agent输出,在Orchestrator里统一做json.loads(json_str.replace(' ', ''))预处理。同时,建立模型版本-输出Schema映射表,每次升级前,用历史样本集做回归测试。

5.3 性能压测与容量规划实录

上线前,我对系统做了72小时连续压测:模拟100个标的每秒触发1次信号,相当于每秒300次Agent调用。

关键发现:

  • Signal Agent在GPU A10上,QPS达42,平均延迟1.08s,满足要求;
  • Risk Agent在CPU上,QPS达128,延迟0.65s,瓶颈在数据库连接池(PostgreSQL max_connections=100);
  • Execution Agent在GPU上,QPS仅28,因为订单簿解析占CPU资源过多。

最终扩容方案

  • Signal Agent:横向扩展至3个实例,Kafka分区数设为3;
  • Risk Agent:数据库连接池从100升至200,加Redis缓存常用持仓数据;
  • Execution Agent:把订单簿解析逻辑用Cython重写,QPS提升至63。

整套系统在满负荷下,99.9%的请求延迟<2s,完全满足实盘要求。

6. 后续演进:从TradingAgents到可信赖的AI交易伙伴

这套TradingAgents系统,我用了8个月才跑通实盘。它远不如宣传中的“AI自动赚钱”那么炫酷,但足够扎实:每个Agent职责清晰,每条消息可追溯,每次失败可归因。它不承诺暴利,但把交易中那些依赖经验、容易出错、难以量化的判断环节,变成了可配置、可测试、可迭代的工程模块。

接下来,我计划做三件事:第一,把Signal Agent接入更多另类数据源,比如卫星图像(监测港口货运量)、供应链票据数据(判断企业现金流),让信号生成不再局限于公开信息;第二,为Risk Agent增加“压力测试”模块,用蒙特卡洛模拟极端行情,提前生成风控规则;第三,也是最重要的——建立一套面向人类交易员的“协作界面”,让LLM的决策过程可视化,比如点击一笔订单,能看到Signal Agent关注了哪些新闻关键词、Risk Agent计算了哪些风险指标、Execution Agent选择了哪种拆单逻辑。技术终归是工具,而真正的价值,永远在于它如何让人更从容地面对市场的不确定性。

我个人在实际操作中的体会是:别急着让LLM“接管交易”,先让它成为你最可靠的副驾驶。它记不住所有财报细节,但它能瞬间比对100份研报的矛盾点;它算不准未来波动率,但它能根据历史模式,建议你把网格间距扩大5%。TradingAgents的意义,从来不是取代人,而是把人从重复劳动中解放出来,去思考真正需要智慧的问题——比如,下一个黑天鹅,会从哪个意想不到的角落冒出来?

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询