首页 / 文章

基于实时EODHD市场数据的分步式LangChain交易智能体

为何没有真实数据时大型语言模型交易代理会失败,ReAct循环的运作原理,以及如何基于EODHD价格、基本面数据与RSI构建三工具组合的LangChain代理。

发布者:REACTAPP.TOP阅读约 8 分钟1769 词

来源: Building a Trading Agent with LangChain and the EODHD API

视频中展示的许多所谓的人工智能交易代理其实并非真正的代理。它们不过是经过精心设计的提示词与虚构数据驱动的语言模型而已。一个真正的交易代理需要具备三样东西:推理能力、用于执行操作的工具,以及实时数据以避免产生幻觉。如果缺少了其中之一,其余两者几乎也就毫无意义。本文旨在帮助那些为金融分析构建交易代理、希望将LangChain与真实市场数据相连,或是尝试过基于大语言模型的“交易机器人”却发现没有可靠数据支撑的话,那一切都只是虚有其表的人。

简而言之,智能体是一种被赋予了各种工具(函数)以及自主决定何时、如何使用这些工具的能力的LLM,而非依靠训练时的知识来回答问题。LangChain是一个Python框架,它将模型、工具、内存和决策循环整合为一个可运行的整体。EODHD则提供了模型默认不具备的功能:通过REST API获取实时价格、基本面数据、技术指标以及最新的财经新闻。将这些元素结合在一起,就能打造出一个能够根据当前数据而非过时信息来回答“我现在是否应该关注这只股票?”这类问题的智能体。

模型并非薄弱环节

向任何大型语言模型询问某只股票的当前价格,它都会给出一个数字。但这个数字可能是几个月前的旧数据,甚至是凭空编造的。这些模型无法获取实时市场信息,不知道昨天发生了什么,也未曾看过最新的财报,然而许多项目仍像它们能获取这些信息一样使用它们。其结果可想而知:基于过时数据给出的建议、充满自信地输出的虚假基本面信息,以及基于根本不存在的价格计算出的技术分析。

这些情况并不能说明该模型不擅长金融领域。问题在于没人给它提供正确的输入数据。没有相应工具的模型就像没有窗户的图书馆:拥有大量知识,却无法了解当下情况。更大的模型也无法解决这个问题。只有能够获取可验证的外部数据,并让模型自行决定查询时机,才能解决问题。而这正是智能体的作用。

技术架构

LangChain负责处理智能体的逻辑。它接收用户的问题,选择相应的工具,执行调用,解析结果并生成答案。其中并无什么神奇之处,只不过是一个带有规则的推理循环而已。

EODHD则填补了数据方面的空白。通过其API,智能体能够获取实时及历史上的OHLCV价格、公司的各项基本面数据如比率、资产负债表和营收情况、RSI、移动平均线及MACD等技术指标,以及反映市场情绪的新闻。选择它来实现这类自动化功能的理由十分实际:其数据覆盖范围包括全球各大交易所,而不仅限于纽约证券交易所和纳斯达克。基本面数据、技术指标数据与新闻内容分别通过不同的接口提供,因此每类数据都可以成为独立的工具。此外,其免费套餐的额度足够充裕,便于在扩大规模之前进行原型测试。

将EODHD作为数据层加以考量

在任何数据被接入交易代理之前,都应对其进行认真评估。优点在于,价格、基本面信息、技术指标以及新闻内容都统一在同一个账户和计费方案下,无需将多个供应商的数据拼凑在一起。许多免费替代方案,如基于Yahoo的爬虫工具以及Alpha Vantage的有限套餐,仅支持美国市场的股票代码,而EODHD则能覆盖数十个交易所,这对于非美国市场的股票而言非常重要。返回的数据为结构化的JSON格式,包含可预知的字段(如close、change_p、PERatio等),因此开发工具封装十分简单,且无需依赖不稳定的HTML解析。您可以在付费之前构建并测试整个代理系统,涵盖价格、基本面数据以及RSI指标等功能。该服务的速率限制有明确说明且易于预测,这对于需要多次调用工具来解答同一问题的场景尤为重要。

当然也有缺点。免费套餐设有每日请求上限,对于个人关注列表来说尚可,但多用户生产环境的需求则无法满足。基本面数据更新遵循提供商的日程安排,而非财报发布时间——这是所有基本面数据API的共同限制,但若您的智能体依赖当日分析,则这一限制尤为重要。此外,工具描述仍需由您自行完成:无论API质量如何,编写不佳的LangChain工具都可能在错误的时间被调用或被模型误解。对于那些需要结构化金融数据且没有爬虫流程的推理智能体而言,相比自建数据层,使用托管API更为合适。

推理循环的工作原理

大多数教程都未详细解释“智能体”在机制上的含义。采用ReAct模式(推理加行动)的LangChain智能体会运行一个包含四个部分的循环。

  1. 大语言模型,负责读取问题与工具描述并决定下一步操作。
  2. 工具,即可调用的函数,每个函数都有名称、描述以及预期的输入和输出,例如 get_live_price、get_fundamentals 和 get_rsi。
  3. 执行器,负责实际运行所请求的工具,将输出结果传回模型,使其能够决定后续操作。
  4. 临时记录区,用于保存单次查询过程中的思考、行动和观察步骤,以便模型记住已检查过的内容。

像“我们现在应该关注AAPL.US吗?”这样的问题会触发这样的处理流程。首先模型需要获取价格信息:

Thought: I need the current price first.
Action: get_live_price("AAPL.US")
Observation: AAPL.US: current price 227.50, change +0.8%

然后它认为仅价格这一因素意义不大,进而考虑动量与估值:

Thought: Price alone isn't enough — check momentum.
Action: get_rsi("AAPL.US")
Observation: AAPL.US — RSI(14): 58.3Thought: Not overbought. Check if valuation is reasonable.
Action: get_fundamentals("AAPL.US")
Observation: AAPL.US — PE: 31.2, Market Cap: 3.4T, Dividend Yield: 0.4%Thought: I now have enough data to answer.
Final Answer: [synthesized response]

这就是带有系统提示的智能体与聊天机器人之间的架构差异。模型会在运行时根据各项观测结果来决定工具调用的顺序,不存在固定的脚本。这也正是数据源的质量和结构如此重要的原因。如果get_fundamentals返回的是错误或过时的数据,基于这些数据生成的每一个推理结果都会继承该错误,且不会给出任何可见的警告。每个推理步骤都依赖于前一步的可靠性,这也是金融智能体需要真正结构化的API而非爬取或缓存的数据的主要原因。

构建它

该智能体拥有三个与EODHD相关的工具:实时价格、基本面数据和技术指标。首先需要安装相关软件包:

pip install langchain langchain-openai requests

这些工具本身只是普通的函数。每个函数都会调用一个 EODHD 接口,然后返回结构清晰、易于模型读取的输出数据:

import requests
from langchain.tools import tool
EODHD_API_KEY = "YOUR_API_KEY"
BASE_URL = "https://eodhd.com/api"
@tool
def get_live_price(ticker: str) -> str:
    """Returns the current price of a stock. Example ticker: AAPL.US"""
    url = f"{BASE_URL}/real-time/{ticker}"
    params = {"api_token": EODHD_API_KEY, "fmt": "json"}
    r = requests.get(url, params=params).json()
    return f"{ticker}: current price {r['close']}, change {r['change_p']}%"
@tool
def get_fundamentals(ticker: str) -> str:
    """Returns key fundamental metrics: PE ratio, market cap, dividend yield."""
    url = f"{BASE_URL}/fundamentals/{ticker}"
    params = {"api_token": EODHD_API_KEY}
    r = requests.get(url, params=params).json()
    highlights = r.get("Highlights", {})
    return (
        f"{ticker} - PE: {highlights.get('PERatio')}, "
        f"Market Cap: {highlights.get('MarketCapitalization')}, "
        f"Dividend Yield: {highlights.get('DividendYield')}"
    )
@tool
def get_rsi(ticker: str) -> str:
    """Returns the 14-day RSI to assess overbought or oversold conditions."""
    url = f"{BASE_URL}/technical/{ticker}"
    params = {"api_token": EODHD_API_KEY, "function": "rsi", "period": 14, "fmt": "json"}
    r = requests.get(url, params=params).json()
    latest = r[-1]
    return f"{ticker} - RSI(14): {latest['rsi']} as of {latest['date']}"

然后从聊天模型、相关工具以及ReAct提示词中组装出该智能体:

from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain import hub

llm = ChatOpenAI(model="gpt-4o", temperature=0)
tools = [get_live_price, get_fundamentals, get_rsi]

prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
prompt = hub.pull("hwchase17/react")
agent = create_react_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, verbose=True)

共有三种工具,一个大型语言模型以及一个根据问题从中进行选择的循环。其调用方式如下:

response = executor.invoke({
    "input": "Should we be looking at AAPL.US right now?"
})
print(response["output"])

该代理并非凭记忆回复。它会首先调用get_live_price,接着调用get_rsi,在需要估值时再调用get_fundamentals,然后将这三项结果整合为基于真实数据的答案,而非凭空编造的虚假内容。其自然扩展包括可在单次查询中比较多只股票的多股票代理、通过情感分析接口整合新闻信息的处理流程,以及定期扫描关注列表并发送警报的功能。

核心要点

没有真实数据的智能体只是个能生成有说服力文本的工具,而非分析工具。LangChain负责任务协调,而可靠的数据API则解决核心问题,即获取实时且准确的金融信息。在ReAct智能体中,每一步都会继承前一步数据的可信度,因此数据层与提示词同样重要。相比将所有逻辑都塞进一个庞大的提示词中,那些各司其职的轻量级工具反而更易于扩展。

以下是一些常见问题。在交易场景中,代理是什么?它是一个让大语言模型能够选择从价格、基本面数据到新闻等各类工具,从而利用真实数据而非仅靠训练过的知识来完成任务的系统。为何要使用LangChain而不是直接调用API?因为它能管理决策流程,决定使用哪种工具、何时使用以及如何整合多个结果,而这些在手动处理时需要为每种情况单独编写代码。尝试此功能是否需要付费的EODHD账户?不需要,免费版本就足以用于原型开发。与自行构建处理流程相比,主要权衡点是什么?你需要放弃对数据来源的完全控制,以换取速度和可靠性——一个结构化的API可以替代多个爬虫或供应商集成,而一旦脱离原型阶段,就需要考虑免费版本设定的每日使用上限。