【山东大学项目实训FinAgent】 交易员阶段:设计、实现逻辑与报告输出
FinAgent交易员阶段:设计、实现逻辑与报告输出
摘要:在 FinAgent 的单股分析流水线中,交易员(
trader)负责把上游的研究结论整理成一份可执行的交易方案——包括买入/持有/卖出建议、目标价与理由说明。本文从设计、代码实现到报告与前端展示,说明该阶段如何接入LangGraph主图、如何调用大语言模型(LLM)并提取结构化交易意图,以及在 LLM 不可用时的兜底方法。关键词:FinAgent;LangGraph;交易员;LLM;风控;
一、交易员在流水线中做什么
FinAgent对单只股票的分析,由LangGraph按固定顺序串联多个阶段完成,顺序如下:
analyst→research_debate→trader→risk_review→portfolio_manager→build_report
对照真实的投研流程,各阶段可以这样理解:
| 阶段 | 职责 | 主要产出 |
|---|---|---|
| analyst | 三位分析师分别从行情、新闻、基本面收集信息 | 三份 *_report 报告 |
| research_debate | 研究员辩论,研究经理综合 | investment_plan(投资计划)、debate_verdict(偏多/偏空/中性) |
| trader(本文重点) | 在research结论的基础上,给出具体交易建议 | trader_investment_plan(方案正文)、trade_intent(买/持/卖) |
| risk_review | 风控视角审议交易方案,必要时进行修正 | 风控辩论记录、风险评估裁决 |
| portfolio_manager | 组合层面给出最终评级 | 最终结论与评级 |
| build_report | 汇总全部结果,供页面展示与导出 | report_json、Markdown 报告 |
交易员是第一个把结论落实为“买还是卖、目标价多少”的角色,但需要明确的是,它只生成研究性质的文字建议,不连接券商下单,也不能替代后续的风控审核。
二、设计思路:输入、输出与上下游衔接
2.1 设计原则
开发交易员节点时,我们定了四条基本约定:
- 只读上游,不重复拉数:交易员不再调用AkShare等接口,避免重复耗时,只阅读前面阶段已经写好的文本。
- 一份输出,两种用途:既要有一段完整的中文方案供人阅读,也要提取机器可识别的
trade_intent(交易员结论的结构化缩写,包括buy/hold/sell),供风控与评分卡使用。 - LLM 失败时仍能出结果:API未配置、超时或报错时,流水线不应在此处中断,而应根据研究辩论的结论给出规则化兜底方案。
2.2 输入字段
轮到交易员执行时,从共享状态state中读取以下信息:
| 字段 | 来源 | 用途 |
|---|---|---|
ticker/analysis_date | 用户创建任务时的输入 | 标明分股票、哪一天,用于构造Prompt |
investment_plan | research_debate | 研究经理综合后的投资计划,是交易员的首要参考 |
三份report | analyst | 市场、新闻、基本面报告;拼接后传入,默认最多12000 字符 |
debate_verdict | research_debate | 研究侧的粗结论(偏多/偏空/中性);仅在LLM不可用时,映射为 buy/sell/hold |
四份分析师报告合计篇幅往往很长。若原样全部送入 LLM,既容易超出上下文长度限制,也会增加token成本。因此在用 combined_analyst_reports_text 将四份报告以空行连接,并截断到指定上限:
def combined_analyst_reports_text(state, *, max_chars: int = 12000) -> str:
return "\n\n".join([
state.get("market_report") or "",
state.get("social_report") or "",
state.get("news_report") or "",
state.get("fundamentals_report") or "",
])[:max_chars]
此外还会在 Prompt 中补充 A 股代码、名称及人民币计价等说明,减少模型误用其他市场或错误单位的情况。
小结:交易员的输入=研究经理计划+四份分析师报告(截断后的)+标的上下文;在此基础上由 LLM 撰写交易方案。
2.3 输出字段
| 字段 | 类型 | 说明 |
|---|---|---|
trader_investment_plan | 字符串 | LLM生成的完整方案,最长12000字符;前端折叠区和Markdown报告主要展示此字段 |
trader_output.trade_intent | buy/hold/sell | 从正文中解析出的交易方向,供程序使用 |
trader_output.llm_raw | 字符串或空 | LLM原文的前4000字符,便于调试与评分 |
memory_log | 字符串列表 | 各阶段的短日志,写入任务运行快照 |
2.4 下游如何使用交易员的输出
交易员的结果不会到此为止,而是继续流入后续的阶段:
- 风控Risk Judge(
risk_manager.py):以trader_investment_plan为起点,结合激进/保守/中性三方辩论进行修正;若交易员未产出正文,则回退使用研究辩论阶段的investment_plan。 - 规则风险基线(
risk_state.py):计算risk_score时会参考trade_intent,例如卖出意图会略提高风险分,买入意图会略降低。 - Agent 评分卡(
agent_scorecards.py):给各阶段Agent自动估的展示分;交易员的分来自正文厚度+买/持/卖意图,仅供页面展示,不参与真实决策或阻断流程。
三、实现逻辑:代码里具体怎么走
核心实现在agents/trader/trader.py的trader_node函数。逻辑可概括为两条路径:LLM 正常时走主路径,异常的时候就走规则兜底方案。
3.1 LLM 主路径
System Prompt(系统指令) 规定模型角色与输出格式,要点包括:
- 扮演专业交易员,仅依据给定材料分析,不得编造数据;
- 必须给出具体目标价(人民币 ¥),不得留空或回避;
- 正文须包含:投资建议、目标价、置信度(0-1)、风险评分(0-1)及理由;
- 文末固定格式:
最终交易建议: **买入/持有/卖出**,后续程序靠这一行来提取交易方向。
User Prompt(用户输入) 按顺序提供三块材料:股票代码与日期、研究经理的investment_plan、三份分析师报告的拼接文本。
模型通过get_chat_model(deep=False)调用,与分析师、研究辩论等节点使用同一档快思考模型,在输出质量与分析耗时之间取得平衡。
3.2 交易意图提取
LLM返回的是长文,而风控和评分卡只需要「买/持/卖」三个值之一。因此用正则函数_extract_trade_intent从正文中提取,按优先级依次尝试:
- 匹配约定结尾:
最终交易建议: **买入|持有|卖出** - 兼容英文格式:
FINAL TRANSACTION PROPOSAL: **BUY|HOLD|SELL** - 在全文搜索「买入/BUY」「卖出/SELL」等关键词
- 以上均未命中时,默认
hold(持有)
3.3 规则兜底方案
当LLM调用失败,或项目配置中关闭LLM时,不再调用模型,改为根据研究辩论结论映射交易方向:
debate_verdict(研究侧结论) | 映射为trade_intent |
|---|---|
bullish(偏多) | buy |
bearish(偏空) | sell |
其他 / neutral(中性) | hold |
四、如何接入LangGraph主图
代码分为两层:
trader_node:纯业务逻辑,负责读 state、调 LLM、写结果;_trader_node(在runner.py):图编排包装,负责阶段开关、进度记录与状态持久化。
包装层主要做四件事:
- 读取
FINAGENT_ENABLED_STAGES:未包含trader时标记skipped,并清空trader_output; - 更新
stage_log,前端进度条会显示交易员阶段; - 调用
_persist_running_snapshot,长任务运行中轮询中间结果; - 将
trader_output、trader_investment_plan写回状态,供风控等下游节点进行读取与后续操作。
五、报告输出与前端展示
5.1 写入report_json
在最后的build_report阶段,除基础分析字段外,还会把交易员相关结果写入 report_json:
trader_investment_plan:交易员方案正文trader_output:含trade_intent的结构化对象,是交易员结论的结构化缩写
同一阶段还会调用 process_trading_signal,从 风控/组合经理终稿 final_trade_decision 中解析出 trading_signal(操作建议、目标价、置信度等)。
5.2 Markdown 导出
services/report_generator.py的处理规则:
- 有
trader_plan正文时,增加章节 Trader交易方案(原文),完整地收录LLM的输出; - 仅有
trader_output而无正文时,退化为 Trader交易方案(结构化),只列出trade_intent(trade_intent是交易员结论的结构化缩写,表示买/持/卖,方便下游代码直接用,不用再去解析整篇报告)。
导出长报告中,交易员通常排在牛熊报告之后、风控辩论之前,与流水线执行顺序一致。

5.3 前端交易员Tab
实现位于 pages/SingleStockAnalysisPage.tsx:
hasTraderPayload:判断有没有东西,存在trading_signal.action、trader_investment_plan或trade_intent任一即认为有内容;TraderTab:顶部TradingSignalHero是一排标签,显示操作建议(买入/持有/卖出)、目标价¥xxx、置信度、风险分还有一句理由摘要,下方CollapsibleMarkdownBlock展示方案正文;- 评分卡:是后端算好、塞进报告JSON里的一小块数据,可显示意图BUY/HOLD/SELL,数据来自
agent_scorecards.trader。
单股结果页中,交易员与研究辩论、风控、组合经理 Tab 并列,与主图各阶段一一对应。

六、总结
FinAgent 的交易员阶段,接收研究经理计划与四份分析师报告,由LLM生成中文交易方案,并提取trade_intent,供风控与页面展示使用。在LangGraph中,该节点接口清晰:trader_investment_plan交易方案供人阅读,trader_output供程序使用,再交由风控阶段审议与修正;结果同时进入Markdown报告与前端独立Tab(结果页交易员有自己单独的报告页)。
更多推荐



所有评论(0)