> ## Content Index
> Fetch the complete content index at: https://www.hubwiz.com/blog/llms.txt
> Use this file to discover other available public pages before exploring further.

# 用 LangGraph 构建 AI 交易代理
- URL: https://www.hubwiz.com/blog/build-a-ai-trading-agent-with-langgraph/
- Published: 2025-11-12T00:51:19.000Z
- Updated: 2025-11-12T00:51:19.000Z
- Description: 我想分享一个我特别兴奋的项目：一个自动化的金融交易代理。它是一个有状态的、自我指导的代理，能够推理目标、执行一系列金融分析步骤，并决定何时完成工作。
- Author: admin
- Tags: APPLICATION, #Import 2026-10-14 14:41

我最近深入研究了人工智能代理的世界，我想分享一个我特别兴奋的项目：一个自动化的金融交易代理。这不仅仅是一个简单的脚本；它是一个有状态的、自我指导的代理，能够推理目标、执行一系列金融分析步骤，并决定何时完成工作。

这个神奇之处在于 [**LangGraph**](https://www.langchain.com/langgraph?ref=hubwiz.com)，这是一个用于构建强大、循环代理工作流的库。让我带你了解一下我是如何构建它的。

你可以在这个 [GitHub 仓库](https://github.com/rejith77/SMA-Trading-Agent?ref=hubwiz.com) 中找到完整的代码。

## 1、整体思路：一切皆是状态机

在这个代理的核心，是一个状态机。它在两个关键状态之间循环：**思考**和**行动**。它思考下一步该做什么，使用工具执行动作，然后根据结果决定是否再次思考或完成。这个循环会持续下去，直到代理的目标达成。

这是此代码创建的工作流程的可视化表示：

![](https://www.hubwiz.com/blog/content/images/2025/11/image-44.png)

本项目的状态图流程

系统分为两个主要文件：`main.py`（大脑）和 `tools.py`（力量）。

## 2、main.py \~ 代理的大脑

这个文件定义了图、状态和决策逻辑。

### 2.1 状态：代理的记忆

首先，我定义了一个 `TypedDict` 来结构化代理的记忆。这个 **AgentState** 是在节点之间传递的单一真相来源。

```
class AgentState(TypedDict):  
 goal: str  
 past_steps: List[str]  
 action_command: str  
 result: str

```

**goal**: 总体任务（例如，“获取 TSLA，计算 SMA，然后回测”）。  
**past\_steps**: 所有先前操作和其结果的列表。这对于上下文至关重要。  
**action\_command**: 要执行的下一个命令（例如，`FETCH “TSLA”`）。  
**result**: 最新工具执行的输出。

### 2.2 节点：思考和行动

图表有两个节点：

**1\. executor\_node:** 这是“思考者”。它使用 LLM（GPT-4o-mini）基于目标和过去步骤来决定下一个命令。我提示 LLM 只输出原始命令，这使得解析变得容易。

```
def executor_node(state: AgentState) -> dict:  
 # … (setup and prompt)  
 prompt = f"""  
   You are a financial trading agent.  
   Your goal: {state['goal']}  
   Past Steps:  
   {past_steps}  
   Available Commands:  
   1. FETCH "TICKER"  
   2. INDICATOR "TICKER" "INDICATOR_NAME"  
   # … etc …  
   """  
 response = llm.invoke(prompt)  
 return {"action_command": response.content.strip()}

```

**2\. tool\_node:** 这是“执行者”。它从状态中获取 *action\_command* 并通过 *execute\_command* 函数运行它。然后将动作及其结果添加到 *past\_steps* 中，这样执行器就知道发生了什么。

这里的一个关键设计选择是处理 `BACKTEST` 命令。由于回测通常是分析的最后一步，工具节点会自动将下一步设置为 `FINISH`，迫使图表在下一次循环中结束。

```
if command.startswith("BACKTEST"):  
 return {  
 "past_steps": state["past_steps"] + [f"Action: {command}\nResult: {result}"],  
 "action_command": f'FINISH "{result}"', # Force a finish  
 "result": result  
 }

```

### 2.3 条件边：决策点

循环的真正智能在于 *should\_continue* 函数。在工具节点运行后，这个函数检查状态以确定下一步应该发生什么。

```
def should_continue(state: AgentState) -> str:  
 if state["action_command"].startswith("FINISH"):  
 return "end"  
 return "continue"

```

如果最后一个命令是 `FINISH`，则图表结束。否则，它会路由回 *executor\_node* 以选择下一个动作。

### 2.4 组装图

最后，我们用 LangGraph 非常声明式地将所有内容连接在一起。

```
workflow = StateGraph(AgentState)  
workflow.add_node("executor", executor_node)  
workflow.add_node("tools", tool_node)  
workflow.set_entry_point("executor")  
workflow.add_edge("executor", "tools")  
workflow.add_conditional_edges("tools", should_continue, {"continue": "executor", "end": END})  
app = workflow.compile()

```

## 3、tools.py \~ 代理的力量

这个文件包含与外部世界交互的实际函数。我使用了 [yfinance](https://pypi.org/project/yfinance/?ref=hubwiz.com) 来获取实时市场数据。

**fetch\_data(ticker)**: 获取最新的股票价格数据。  
**calculate\_indicator(ticker, indicator):** 计算技术指标如 SMA 或 RSI。  
**backtest\_strategy(ticker, strategy):** 这是最复杂的函数。它运行一个简单的回测（目前是 SMA 交叉策略），计算累计回报，并…这是我最喜欢的部分\~生成一个可视化图表 :)

在回测期间调用 *plot\_chart* 函数。它创建一个双面板图表：  
**顶部面板:** 价格序列、SMA 和交易进入（绿色向上三角形表示盈利，红色表示亏损）和退出（向下三角形）的视觉标记。  
**底部面板:** 资产曲线，将策略的表现与简单的“买入并持有”方法进行比较。

这些图表会自动保存到 *charts/* 目录中，为代理的最终答案提供清晰的可视化输出。

## 4、运行代理

当你执行 `main.py` 时，它会流式传输事件，让你实时观看代理的思维过程。

```
if __name__ == "__main__":  
 GOAL = "Fetch TSLA, calculate SMA, then backtest SMA strategy"  
 inputs = {"goal": GOAL, "past_steps": [], "result": ""}  
 for event in app.stream(inputs):  
 for k, v in event.items():  
 if k != "__end__":  
 print(f"\n - - Event: {k} - -\n{v}")

```

你会看到类似这样的输出：

```
--- EXECUTOR ---  

--- Event: executor ---  
{'action_command': 'FETCH "TSLA"'}  

--- TOOLS ---  
/home/rejith/Sites/Agent/tools.py:8: FutureWarning: YF.download() has changed argument auto_adjust default to True  
  df = yf.download(ticker, period="1y", interval="1d")  
[*********************100%***********************]  1 of 1 completed  

--- Event: tools ---  
{'past_steps': ['Action: FETCH "TSLA"\nResult: Price            Close        High         Low        Open     Volume\nTicker            TSLA        TSLA        TSLA        TSLA       TSLA\nDate                                                                 \n2025-10-20  447.429993  449.799988  440.609985  443.869995   63719000\n2025-10-21  442.600006  449.299988  442.049988  445.760010   54412200\n2025-10-22  438.970001  445.540009  429.000000  443.450012   84023500\n2025-10-23  448.980011  449.399994  413.899994  420.000000  126709800\n2025-10-24  433.720001  451.679993  430.170013  446.829987   94408400'], 'result': 'Price            Close        High         Low        Open     Volume\nTicker            TSLA        TSLA        TSLA        TSLA       TSLA\nDate                                                                 \n2025-10-20  447.429993  449.799988  440.609985  443.869995   63719000\n2025-10-21  442.600006  449.299988  442.049988  445.760010   54412200\n2025-10-22  438.970001  445.540009  429.000000  443.450012   84023500\n2025-10-23  448.980011  449.399994  413.899994  420.000000  126709800\n2025-10-24  433.720001  451.679993  430.170013  446.829987   94408400'}  

--- EXECUTOR ---  

--- Event: executor ---  
{'action_command': 'INDICATOR "TSLA" "SMA"'}  

--- TOOLS ---  
/home/rejith/Sites/Agent/tools.py:12: FutureWarning: YF.download() has changed argument auto_adjust default to True  
  df = yf.download(ticker, period="6mo", interval="1d")  
[*********************100%***********************]  1 of 1 completed  

--- Event: tools ---  
{'past_steps': ['Action: FETCH "TSLA"\nResult: Price            Close        High         Low        Open     Volume\nTicker            TSLA        TSLA        TSLA        TSLA       TSLA\nDate                                                                 \n2025-10-20  447.429993  449.799988  440.609985  443.869995   63719000\n2025-10-21  442.600006  449.299988  442.049988  445.760010   54412200\n2025-10-22  438.970001  445.540009  429.000000  443.450012   84023500\n2025-10-23  448.980011  449.399994  413.899994  420.000000  126709800\n2025-10-24  433.720001  451.679993  430.170013  446.829987   94408400', 'Action: INDICATOR "TSLA" "SMA"\nResult: Price            Close       SMA20\nTicker            TSLA            \nDate                              \n2025-10-20  447.429993  436.774498\n2025-10-21  442.600006  437.611998\n2025-10-22  438.970001  437.420998\n2025-10-23  448.980011  438.700497\n2025-10-24  433.720001  438.366498'], 'result': 'Price            Close       SMA20\nTicker            TSLA            \nDate                              \n2025-10-20  447.429993  436.774498\n2025-10-21  442.600006  437.611998\n2025-10-22  438.970001  437.420998\n2025-10-23  448.980011  438.700497\n2025-10-24  433.720001  438.366498'}  

--- EXECUTOR ---  

--- Event: executor ---  
{'action_command': 'BACKTEST "TSLA" "SMA"'}  

--- TOOLS ---  
/home/rejith/Sites/Agent/tools.py:27: FutureWarning: YF.download() has changed argument auto_adjust default to True  
  df = yf.download(ticker, period="1y", interval="1d")  
[*********************100%***********************]  1 of 1 completed  

--- Event: tools ---  
{'past_steps': ['Action: FETCH "TSLA"\nResult: Price            Close        High         Low        Open     Volume\nTicker            TSLA        TSLA        TSLA        TSLA       TSLA\nDate                                                                 \n2025-10-20  447.429993  449.799988  440.609985  443.869995   63719000\n2025-10-21  442.600006  449.299988  442.049988  445.760010   54412200\n2025-10-22  438.970001  445.540009  429.000000  443.450012   84023500\n2025-10-23  448.980011  449.399994  413.899994  420.000000  126709800\n2025-10-24  433.720001  451.679993  430.170013  446.829987   94408400', 'Action: INDICATOR "TSLA" "SMA"\nResult: Price            Close       SMA20\nTicker            TSLA            \nDate                              \n2025-10-20  447.429993  436.774498\n2025-10-21  442.600006  437.611998\n2025-10-22  438.970001  437.420998\n2025-10-23  448.980011  438.700497\n2025-10-24  433.720001  438.366498', 'Action: BACKTEST "TSLA" "SMA"\nResult: Backtest completed for TSLA (SMA).\nChart saved: charts/chart_TSLA_e97bc4.png\n\n             Returns  Strategy\nDate                          \n2025-10-20  0.473997  0.226798\n2025-10-21  0.463202  0.216003\n2025-10-22  0.455001  0.207801\n2025-10-23  0.477804  0.230605\n2025-10-24  0.443816  0.196616'], 'action_command': 'FINISH "Backtest completed for TSLA (SMA).\nChart saved: charts/chart_TSLA_e97bc4.png\n\n             Returns  Strategy\nDate                          \n2025-10-20  0.473997  0.226798\n2025-10-21  0.463202  0.216003\n2025-10-22  0.455001  0.207801\n2025-10-23  0.477804  0.230605\n2025-10-24  0.443816  0.196616"', 'result': 'Backtest completed for TSLA (SMA).\nChart saved: charts/chart_TSLA_e97bc4.png\n\n             Returns  Strategy\nDate                          \n2025-10-20  0.473997  0.226798\n2025-10-21  0.463202  0.216003\n2025-10-22  0.455001  0.207801\n2025-10-23  0.477804  0.230605\n2025-10-24  0.443816  0.196616'}

```

在你的 `charts` 文件夹中，你会找到一个生成的 PNG 文件，显示完整的回测结果！

![](https://www.hubwiz.com/blog/content/images/2025/11/image-45.png)

带有 26.67% 交易准确率的 TSLA :’)

## 4、我学到的东西和未来步骤

构建这个项目是我理解有状态 AI 代理的一种绝佳方式。推理（图表）和执行（工具）之间的清晰分离使系统非常灵活且易于扩展。

接下来呢？

- **更多工具:** 添加新闻情绪分析、投资组合优化甚至模拟交易的命令。
- **更智能的状态管理:** *past\_steps* 列表可能会变得很长。我可能会使用向量存储来进行长期记忆，以避免上下文窗口限制。
- **人机协作:** 添加一个可以暂停执行并请求人类批准或澄清的节点，将是迈向稳健性的巨大进步。

## 5、必要的警告

构建这个代理是一种极好的方式来理解有状态的 AI 工作流程。LangGraph 提供的框架确实感觉像是构建复杂、可靠代理的正确抽象，我很期待看到在此基础上还能构建出什么。

然而，必须用现实来平衡这种兴奋。我刚刚展示的系统只是一个技术演示。它是一个原型，旨在说明 AI 代理的架构，而不是一个强大的金融分析引擎。

回测逻辑是有意简化，整个管道缺乏进行实盘交易所需的严谨性。金融市场是复杂的，没有经过充分验证、风险管理以及对底层假设的深入了解就部署自动化策略，会导致严重的财务损失。

请将此代码作为 AI 开发的学习工具，而不是投资决策的基础。在考虑真实资金之前，请始终进行专业级别的回测和验证。

---

原文链接：[Building a Stateful AI Trading Agent with LangGraph](https://medium.datadriveninvestor.com/building-a-stateful-ai-trading-agent-with-langgraph-a31521bc14ea?ref=hubwiz.com)

汇智网翻译整理，转载请标明出处