How to Add Custom Tools or Override Data Fetching Functions in TradingAgents
You can extend TradingAgents by decorating new Python functions with @tool and registering them in VENDOR_METHODS, or override existing data sources by updating default_config.py without touching core agent code.
TradingAgents (TauricResearch/TradingAgents) is built on a tool-oriented architecture that lets large language models invoke reusable data-fetching functions during reasoning. Because the routing layer in tradingagents/dataflows/interface.py is decoupled from agent implementations, you can introduce brand-new capabilities or swap vendor implementations by editing configuration files and the tool registry.
Architecture of the Tool and Routing System
TradingAgents organizes data access into four distinct layers that keep agent logic separate from data providers.
- Tool Definition – Python functions decorated with
@tool(fromlangchain_core.tools) located intradingagents/agents/utils/*.pysuch ascore_stock_tools.pyandtechnical_indicators_tools.py. - Routing Dispatcher – The
route_to_vendor()function intradingagents/dataflows/interface.pyresolves tool names to concrete implementations using theVENDOR_METHODSmapping. - Configuration –
tradingagents/default_config.pydefines the default vendor per data category (e.g.,technical_indicators) and optional per-tool overrides undertool_vendors. - Agent Integration – Analyst agents in
tradingagents/agents/analysts/*.pyimport tools and pass them to the LLM viallm.bind_tools(tools).
When an LLM calls a tool, LangChain invokes the Python function, which forwards the request to route_to_vendor(). The dispatcher reads the category from data_vendors, looks up the implementation in VENDOR_METHODS, and executes the vendor-specific function. If the primary vendor raises AlphaVantageRateLimitError, the system automatically falls back to the next configured vendor.
Adding a Custom Tool to TradingAgents
Creating a new tool requires three steps: defining the function, exposing it to agents, and wiring a vendor implementation.
Define the Tool Function
Create a new file (e.g., tradingagents/agents/utils/custom_tools.py) and decorate your function with @tool. Use Annotated type hints to provide descriptions for the LLM.
# tradingagents/agents/utils/custom_tools.py
from langchain_core.tools import tool
from typing import Annotated
from tradingagents.dataflows.interface import route_to_vendor
@tool
def get_earnings_calendar(
ticker: Annotated[str, "ticker symbol (e.g. AAPL, TSM)"],
from_date: Annotated[str, "YYYY-mm-dd start of range"],
to_date: Annotated[str, "YYYY-mm-dd end of range"],
) -> str:
"""
Fetch upcoming earnings releases for *ticker* between *from_date* and *to_date*.
"""
return route_to_vendor("get_earnings_calendar", ticker, from_date, to_date)
Expose the Tool to Agents
Import your new tool in tradingagents/agents/utils/agent_utils.py so analysts can access it.
# tradingagents/agents/utils/agent_utils.py
from tradingagents.agents.utils.custom_tools import get_earnings_calendar
Any analyst can now add get_earnings_calendar to its tools = [...] list and call llm.bind_tools(tools).
Wire the Vendor Implementation
Add a concrete implementation (e.g., in tradingagents/dataflows/y_finance.py) and register it in VENDOR_METHODS.
# tradingagents/dataflows/y_finance.py
def get_earnings_calendar_yfinance(ticker: str, from_date: str, to_date: str) -> str:
import yfinance as yf, pandas as pd
tk = yf.Ticker(ticker)
cal = tk.calendar.T
cal.index = pd.to_datetime(cal.index).date
mask = (cal.index >= pd.to_datetime(from_date).date()) & (cal.index <= pd.to_datetime(to_date).date())
return cal.loc[mask].to_csv()
Then extend the mapping in tradingagents/dataflows/interface.py:
# tradingagents/dataflows/interface.py
VENDOR_METHODS["get_earnings_calendar"] = {
"yfinance": get_earnings_calendar_yfinance,
# Additional vendors can be added here
}
Overriding Data Fetching Behavior
You can replace existing data sources without modifying agent code by using configuration-based overrides or direct registry edits.
Override via Configuration
To force a specific tool to use a custom vendor, add an entry to tool_vendors in default_config.py.
# tradingagents/default_config.py
"tool_vendors": {
"get_stock_data": "my_private_api",
},
Then register your implementation in VENDOR_METHODS:
# tradingagents/dataflows/interface.py
VENDOR_METHODS["get_stock_data"] = {
"my_private_api": my_private_api_get_stock_data,
"yfinance": get_YFin_data_online,
"alpha_vantage": get_alpha_vantage_stock,
}
The routing dispatcher will now attempt my_private_api first, falling back to yfinance or alpha_vantage if your implementation raises a rate-limit error.
Direct Code Replacement (Experimental)
For rapid prototyping, you can replace the function reference directly in interface.py:
# tradingagents/dataflows/interface.py
VENDOR_METHODS["get_stock_data"]["yfinance"] = my_private_api_get_stock_data
This approach bypasses configuration files and is best reserved for temporary testing.
End-to-End Code Examples
Example 1: Adding an Earnings Calendar Tool
This example demonstrates the complete flow for adding a custom earnings calendar tool backed by yfinance.
# tradingagents/agents/utils/custom_tools.py
from langchain_core.tools import tool
from typing import Annotated
from tradingagents.dataflows.interface import route_to_vendor
@tool
def get_earnings_calendar(
ticker: Annotated[str, "ticker symbol (e.g. AAPL)"],
from_date: Annotated[str, "start date YYYY-mm-dd"],
to_date: Annotated[str, "end date YYYY-mm-dd"],
) -> str:
"""Return upcoming earnings releases for *ticker* in the given date range."""
return route_to_vendor("get_earnings_calendar", ticker, from_date, to_date)
# tradingagents/dataflows/y_finance.py
def get_earnings_calendar_yfinance(ticker: str, from_date: str, to_date: str) -> str:
import yfinance as yf, pandas as pd
tk = yf.Ticker(ticker)
cal = tk.calendar.T
cal.index = pd.to_datetime(cal.index).date
mask = (cal.index >= pd.to_datetime(from_date).date()) & (cal.index <= pd.to_datetime(to_date).date())
return cal.loc[mask].to_csv()
# tradingagents/dataflows/interface.py
"get_earnings_calendar": {
"yfinance": get_earnings_calendar_yfinance,
},
# tradingagents/agents/utils/agent_utils.py
from tradingagents.agents.utils.custom_tools import get_earnings_calendar
When an analyst includes get_earnings_calendar in its tool list, the LLM can emit a call like {"name": "get_earnings_calendar", "args": {"ticker": "AAPL", "from_date": "2024-07-01", "to_date": "2024-07-30"}}, and the system will return a CSV string of upcoming earnings dates.
Example 2: Overriding Stock Data with a Private API
This example shows how to route get_stock_data through an internal HTTP service.
# tradingagents/dataflows/private_api.py
def get_stock_data_private(ticker: str, start_date: str, end_date: str) -> str:
import requests, pandas as pd
resp = requests.get(
"https://private.api/ohlcv",
params={"symbol": ticker, "start": start_date, "end": end_date},
)
df = pd.DataFrame(resp.json())
return df.to_csv()
# tradingagents/dataflows/interface.py
VENDOR_METHODS["get_stock_data"] = {
"private_api": get_stock_data_private,
"yfinance": get_YFin_data_online,
"alpha_vantage": get_alpha_vantage_stock,
}
# tradingagents/default_config.py
"tool_vendors": {
"get_stock_data": "private_api",
},
All agents calling get_stock_data will now hit your private service first, with graceful fallback to public vendors if the private call raises an exception.
Key Files for Customization
| Path | Role |
|---|---|
tradingagents/agents/utils/core_stock_tools.py |
Core stock-price tool (get_stock_data) |
tradingagents/agents/utils/technical_indicators_tools.py |
Technical-indicator tool (get_indicators) |
tradingagents/agents/utils/fundamental_data_tools.py |
Fundamental-data suite (get_fundamentals) |
tradingagents/agents/utils/news_data_tools.py |
News-related tools (get_news, get_global_news) |
tradingagents/agents/utils/agent_utils.py |
Central import hub for all tools |
tradingagents/dataflows/interface.py |
Routing dispatcher (route_to_vendor) and VENDOR_METHODS map |
tradingagents/default_config.py |
Default vendor selections and per-tool overrides |
tradingagents/agents/analysts/*.py |
Example agents that assemble tool lists |
tradingagents/graph/trading_graph.py |
Workflow graph showing tool node integration |
Summary
- Define new tools using the
@tooldecorator fromlangchain_core.toolsand place them intradingagents/agents/utils/. - Register vendor implementations in the
VENDOR_METHODSdictionary insidetradingagents/dataflows/interface.py. - Configure per-tool overrides in
tradingagents/default_config.pyunder thetool_vendorskey to change data sources without code changes. - Expose new tools through
tradingagents/agents/utils/agent_utils.pyto make them available to analyst agents. - Rely on automatic fallback handling when vendors raise rate-limit errors.
Frequently Asked Questions
Do I need to modify the agent code to add a new tool?
No. Agents import tools from tradingagents/agents/utils/agent_utils.py. Adding your tool to that file (or importing it there from your custom module) makes it available to all analysts without changing any agent-specific logic in tradingagents/agents/analysts/.
How does the routing layer choose which data vendor to use?
The route_to_vendor() function checks default_config.py for a tool_vendors entry matching the tool name. If found, it uses that vendor; otherwise, it falls back to the category default in data_vendors. It then resolves the implementation using the VENDOR_METHODS mapping and executes the function. If that function raises AlphaVantageRateLimitError, the dispatcher automatically tries the next vendor in the mapping.
Can I override a specific tool like get_stock_data without affecting other tools?
Yes. Add a specific entry to tool_vendors in default_config.py mapping get_stock_data to your custom vendor name. Then register your implementation in VENDOR_METHODS["get_stock_data"] alongside existing vendors like yfinance and alpha_vantage. Other tools will continue using their default vendors.
What happens if my custom data source fails?
If your implementation raises AlphaVantageRateLimitError (or other recognized exceptions), the routing layer automatically falls back to the next vendor configured in the VENDOR_METHODS list for that tool. This ensures agents remain functional even if a primary data source is temporarily unavailable.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →