refactor(agents): organise the agents package by what each module holds

- tools.py: the analysts' data tools, previously seven modules under utils
- context.py (was agent_utils, without its tool re-exports), state.py, rating.py and structured.py sit beside schemas.py
- every role package has an __init__
This commit is contained in:
Yijia-Xiao
2026-09-24 04:37:40 +00:00
parent 6097b582d9
commit 852ffead43
54 changed files with 362 additions and 464 deletions
+1 -1
View File
@@ -42,7 +42,7 @@ from cli.utils import (
select_research_depth,
select_shallow_thinking_agent,
)
from tradingagents.agents.utils.rating import is_review
from tradingagents.agents.rating import is_review
from tradingagents.backtest import iter_grid, run_backtest, summarize
from tradingagents.dataflows.symbols import safe_ticker_component
from tradingagents.default_config import DEFAULT_CONFIG
+1 -1
View File
@@ -76,7 +76,7 @@ class _FakeGraph:
return {"messages": [], "company_of_interest": ticker}
def process_signal(self, text):
from tradingagents.agents.utils.rating import parse_rating
from tradingagents.agents.rating import parse_rating
return parse_rating(text)
def record_decision(self, ticker, trade_date, final_state):
+1 -1
View File
@@ -12,12 +12,12 @@ from unittest.mock import MagicMock
import pytest
from tradingagents.agents.context import opponent_argument_or_opening
from tradingagents.agents.researchers.bear_researcher import create_bear_researcher
from tradingagents.agents.researchers.bull_researcher import create_bull_researcher
from tradingagents.agents.risk_mgmt.aggressive_debator import create_aggressive_debator
from tradingagents.agents.risk_mgmt.conservative_debator import create_conservative_debator
from tradingagents.agents.risk_mgmt.neutral_debator import create_neutral_debator
from tradingagents.agents.utils.agent_utils import opponent_argument_or_opening
_REPORTS = {
"company_of_interest": "AAPL", "asset_type": "stock",
+2 -3
View File
@@ -17,9 +17,8 @@ from langchain_core.outputs import ChatGeneration, ChatResult
from langchain_core.runnables import RunnableLambda
from pydantic import Field
from tradingagents.agents import schemas
from tradingagents.agents import context, schemas
from tradingagents.agents.analysts import sentiment_analyst
from tradingagents.agents.utils import agent_utils
from tradingagents.dataflows import router
from tradingagents.dataflows.vendors.yahoo import market as yahoo_market, snapshot
from tradingagents.default_config import DEFAULT_CONFIG
@@ -108,7 +107,7 @@ def offline(monkeypatch, tmp_path):
monkeypatch.setattr(sentiment_analyst, "fetch_stocktwits_messages", lambda *a, **k: "no posts")
monkeypatch.setattr(sentiment_analyst, "fetch_reddit_posts", lambda *a, **k: "no posts")
monkeypatch.setattr(yahoo_market.yf, "Ticker", lambda s: type("T", (), {"info": {"longName": "NVIDIA"}})())
agent_utils.resolve_instrument_identity.cache_clear()
context.resolve_instrument_identity.cache_clear()
return called
+1 -1
View File
@@ -10,7 +10,7 @@ from pathlib import Path
import pytest
from tradingagents.agents.utils.agent_utils import get_language_instruction
from tradingagents.agents.context import get_language_instruction
_AGENTS_DIR = Path(__file__).resolve().parents[1] / "tradingagents" / "agents"
+1 -1
View File
@@ -7,7 +7,7 @@ from unittest.mock import patch
import pytest
from langchain_core.messages import AIMessage, HumanMessage, RemoveMessage
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
build_instrument_context,
create_msg_delete,
get_instrument_context_from_state,
+1 -1
View File
@@ -196,7 +196,7 @@ class TestTradingMemoryLogCore:
def test_an_unreadable_decision_is_tagged_for_review(self, tmp_path):
"""Not a Hold: a fabricated rating is quoted back to the next run as a
call that was never made, and counted in the backtest figures."""
from tradingagents.agents.utils.rating import RATING_REVIEW
from tradingagents.agents.rating import RATING_REVIEW
log = make_log(tmp_path)
log.store_decision("MSFT", "2026-01-12", DECISION_NO_RATING)
+1 -1
View File
@@ -8,7 +8,7 @@ import inspect
import pytest
import tradingagents.agents.analysts.news_analyst as na
from tradingagents.agents.utils.news_data_tools import get_news
from tradingagents.agents.tools import get_news
@pytest.mark.unit
+1 -1
View File
@@ -13,7 +13,7 @@ import json
import pytest
from tradingagents.agents.utils.agent_utils import get_portfolio_context_from_state
from tradingagents.agents.context import get_portfolio_context_from_state
from tradingagents.portfolio import PortfolioContext, load_portfolio
HOLDING = {
+3 -3
View File
@@ -11,7 +11,7 @@ from __future__ import annotations
import pytest
from tradingagents.agents.utils.rating import RATING_REVIEW, extract_rating, parse_rating
from tradingagents.agents.rating import RATING_REVIEW, extract_rating, parse_rating
INVERTED = ("The aggressive analyst pushed hard for a Buy on the AI backlog, but the "
"conservative case on margin compression carried the debate. "
@@ -74,8 +74,8 @@ def test_the_memory_log_records_review_rather_than_a_tradeable_hold(tmp_path):
@pytest.mark.unit
def test_the_signal_and_the_log_agree_on_the_same_decision(tmp_path):
from tradingagents.agents.rating import parse_rating
from tradingagents.agents.utils.memory import TradingMemoryLog
from tradingagents.agents.utils.rating import parse_rating
log = TradingMemoryLog({"memory_log_path": str(tmp_path / "m.md")})
for text in (INVERTED, REFUSAL, "**Rating**: Buy\n\nAccumulate."):
@@ -121,7 +121,7 @@ def test_the_cli_says_when_a_run_produced_no_usable_rating(monkeypatch, tmp_path
pass
def process_signal(self, text):
from tradingagents.agents.utils.rating import parse_rating
from tradingagents.agents.rating import parse_rating
return parse_rating(text)
def get_graph_args(self, callbacks=None):
+1 -6
View File
@@ -6,12 +6,7 @@ header, so the rating is read deterministically; no second model call is made.
import pytest
from tradingagents.agents.utils.rating import (
RATING_REVIEW,
RATINGS_5_TIER,
extract_rating,
parse_rating,
)
from tradingagents.agents.rating import RATING_REVIEW, RATINGS_5_TIER, extract_rating, parse_rating
# ---------------------------------------------------------------------------
# Heuristic parser
+1 -1
View File
@@ -18,8 +18,8 @@ import pytest
import tradingagents.agents.analysts.sentiment_analyst as sentiment
from tradingagents.agents.managers.portfolio_manager import create_portfolio_manager
from tradingagents.agents.managers.research_manager import create_research_manager
from tradingagents.agents.structured import NO_EXTERNAL_TOOLS
from tradingagents.agents.trader.trader import create_trader
from tradingagents.agents.utils.structured import NO_EXTERNAL_TOOLS
def _capturing_llm(captured: dict, result):
+1 -1
View File
@@ -197,7 +197,7 @@ def _structured_trader_llm(captured: dict, proposal: TraderProposal | None = Non
def test_invoke_structured_falls_back_when_result_is_none():
# A thinking model can answer in plain text, leaving the parser with None.
# That must fall back to free text, not crash on render(None) (#1051).
from tradingagents.agents.utils.structured import invoke_structured_or_freetext
from tradingagents.agents.structured import invoke_structured_or_freetext
structured = MagicMock()
structured.invoke.return_value = None
+1 -1
View File
@@ -7,7 +7,7 @@ hit the right instrument instead of failing/mismatching.
"""
import pandas as pd
import tradingagents.agents.utils.agent_utils as au
import tradingagents.agents.context as au
import tradingagents.dataflows.vendors.yahoo.market as yahoo_market
import tradingagents.dataflows.vendors.yahoo.news as ynews
from tradingagents.graph.trading_graph import TradingAgentsGraph
+1 -1
View File
@@ -3,7 +3,7 @@ import unittest
import pytest
from cli.utils import normalize_ticker_symbol
from tradingagents.agents.utils.agent_utils import build_instrument_context
from tradingagents.agents.context import build_instrument_context
@pytest.mark.unit
+18 -25
View File
@@ -15,14 +15,7 @@ from langchain_core.messages import AIMessage
from langgraph.graph import END, START, MessagesState, StateGraph
from langgraph.prebuilt import ToolNode
from tradingagents.agents.utils import (
core_stock_tools,
fundamental_data_tools,
macro_data_tools,
market_data_validation_tools,
news_data_tools,
technical_indicators_tools,
)
from tradingagents.agents import tools
from tradingagents.dataflows.date_window import as_of, as_of_window
TRADE_DATE = "2026-08-14"
@@ -56,16 +49,16 @@ def test_as_of_window(start, end, expected):
DATED_TOOLS = [
core_stock_tools.get_stock_data,
fundamental_data_tools.get_fundamentals,
fundamental_data_tools.get_balance_sheet,
fundamental_data_tools.get_cashflow,
fundamental_data_tools.get_income_statement,
news_data_tools.get_news,
news_data_tools.get_global_news,
technical_indicators_tools.get_indicators,
macro_data_tools.get_macro_indicators,
market_data_validation_tools.get_verified_market_snapshot,
tools.get_stock_data,
tools.get_fundamentals,
tools.get_balance_sheet,
tools.get_cashflow,
tools.get_income_statement,
tools.get_news,
tools.get_global_news,
tools.get_indicators,
tools.get_macro_indicators,
tools.get_verified_market_snapshot,
]
@@ -95,28 +88,28 @@ def _run(tool, args, module):
@pytest.mark.unit
def test_statement_tool_with_omitted_date_uses_the_run_date():
args = _run(fundamental_data_tools.get_balance_sheet, {"ticker": "AAPL"}, fundamental_data_tools)
args = _run(tools.get_balance_sheet, {"ticker": "AAPL"}, tools)
assert args[-1] == TRADE_DATE # #1331: an omitted date no longer means unfiltered
@pytest.mark.unit
def test_future_curr_date_from_the_model_is_clamped():
args = _run(fundamental_data_tools.get_fundamentals,
{"ticker": "AAPL", "curr_date": "2026-09-14"}, fundamental_data_tools)
args = _run(tools.get_fundamentals,
{"ticker": "AAPL", "curr_date": "2026-09-14"}, tools)
assert args == ("get_fundamentals", "AAPL", TRADE_DATE)
@pytest.mark.unit
def test_future_window_from_the_model_is_clamped():
args = _run(core_stock_tools.get_stock_data,
{"symbol": "AAPL", "start_date": "2026-08-01", "end_date": "2026-09-14"}, core_stock_tools)
args = _run(tools.get_stock_data,
{"symbol": "AAPL", "start_date": "2026-08-01", "end_date": "2026-09-14"}, tools)
assert args == ("get_stock_data", "AAPL", "2026-08-01", TRADE_DATE)
@pytest.mark.unit
def test_direct_call_without_state_is_unchanged():
with mock.patch.object(news_data_tools, "route_to_vendor", return_value="ok") as routed:
news_data_tools.get_news.func("AAPL", "2026-09-01", "2026-09-08")
with mock.patch.object(tools, "route_to_vendor", return_value="ok") as routed:
tools.get_news.func("AAPL", "2026-09-01", "2026-09-08")
assert routed.call_args.args == ("get_news", "AAPL", "2026-09-01", "2026-09-08")
+6 -6
View File
@@ -13,7 +13,7 @@ from unittest import mock
import pandas as pd
import pytest
from tradingagents.agents.utils import news_data_tools, prediction_markets_tools
from tradingagents.agents import tools
from tradingagents.dataflows.vendors import polymarket
from tradingagents.dataflows.vendors.alpha_vantage import news as alpha_vantage_news
from tradingagents.dataflows.vendors.yahoo import (
@@ -82,8 +82,8 @@ def test_polymarket_serves_a_current_run():
@pytest.mark.unit
@pytest.mark.parametrize("tool", [news_data_tools.get_insider_transactions,
prediction_markets_tools.get_prediction_markets], ids=lambda t: t.name)
@pytest.mark.parametrize("tool", [tools.get_insider_transactions,
tools.get_prediction_markets], ids=lambda t: t.name)
def test_trade_date_is_injected_not_model_visible(tool):
assert "trade_date" in tool.func.__code__.co_varnames
props = tool.tool_call_schema.model_json_schema()["properties"]
@@ -98,7 +98,7 @@ def test_a_historical_run_is_told_the_identity_is_current(monkeypatch):
They are usually right for a past date, but a company that renamed or was
reclassified since would read wrong, and every agent is told to anchor to
this identity, so the run has to know which date it describes."""
from tradingagents.agents.utils.agent_utils import build_instrument_context
from tradingagents.agents.context import build_instrument_context
identity = {"company_name": "Example Corp", "sector": "Technology",
"industry": "Software", "exchange": "NMS"}
@@ -110,7 +110,7 @@ def test_a_historical_run_is_told_the_identity_is_current(monkeypatch):
@pytest.mark.unit
def test_a_current_run_is_not_cluttered_with_a_vintage_note(monkeypatch):
from tradingagents.agents.utils.agent_utils import build_instrument_context
from tradingagents.agents.context import build_instrument_context
from tradingagents.dataflows.date_window import get_current_date
today = build_instrument_context("EXMP", "stock", {"company_name": "Example Corp"},
@@ -285,7 +285,7 @@ def _dates_after(text: str, cutoff: str) -> list[str]:
def test_an_unavailable_notice_names_no_date_after_the_run():
"""A notice explaining why data is missing named where the vendor's coverage
starts or today's date, both after a historical run's date."""
from tradingagents.agents.utils.agent_utils import build_instrument_context
from tradingagents.agents.context import build_instrument_context
from tradingagents.dataflows.date_window import (
coverage_gap,
get_current_date,
+1 -3
View File
@@ -66,9 +66,7 @@ class TestVerifiedSnapshot:
@pytest.mark.unit
class TestTool:
def test_tool_delegates_to_builder(self, monkeypatch):
from tradingagents.agents.utils.market_data_validation_tools import (
get_verified_market_snapshot,
)
from tradingagents.agents.tools import get_verified_market_snapshot
monkeypatch.setattr(validator, "load_ohlcv", lambda s, d, fill_gaps=True: _sample_ohlcv())
out = get_verified_market_snapshot.invoke(
{"symbol": "COF", "curr_date": "2026-05-20"}
+2 -2
View File
@@ -2,6 +2,7 @@ from .analysts.fundamentals_analyst import create_fundamentals_analyst
from .analysts.market_analyst import create_market_analyst
from .analysts.news_analyst import create_news_analyst
from .analysts.sentiment_analyst import create_sentiment_analyst
from .context import create_msg_delete
from .managers.portfolio_manager import create_portfolio_manager
from .managers.research_manager import create_research_manager
from .researchers.bear_researcher import create_bear_researcher
@@ -9,9 +10,8 @@ from .researchers.bull_researcher import create_bull_researcher
from .risk_mgmt.aggressive_debator import create_aggressive_debator
from .risk_mgmt.conservative_debator import create_conservative_debator
from .risk_mgmt.neutral_debator import create_neutral_debator
from .state import AgentState, InvestDebateState, RiskDebateState
from .trader.trader import create_trader
from .utils.agent_states import AgentState, InvestDebateState, RiskDebateState
from .utils.agent_utils import create_msg_delete
__all__ = [
"AgentState",
@@ -1,13 +1,12 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import get_instrument_context_from_state, get_language_instruction
from tradingagents.agents.tools import (
get_balance_sheet,
get_cashflow,
get_fundamentals,
get_income_statement,
get_insider_transactions,
get_instrument_context_from_state,
get_language_instruction,
)
# The tools this analyst is offered; its tool node is built from the same tuple.
@@ -1,12 +1,7 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tradingagents.agents.utils.agent_utils import (
get_indicators,
get_instrument_context_from_state,
get_language_instruction,
get_stock_data,
get_verified_market_snapshot,
)
from tradingagents.agents.context import get_instrument_context_from_state, get_language_instruction
from tradingagents.agents.tools import get_indicators, get_stock_data, get_verified_market_snapshot
# The tools this analyst is offered; its tool node is built from the same tuple.
TOOLS = (
@@ -1,9 +1,8 @@
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import get_instrument_context_from_state, get_language_instruction
from tradingagents.agents.tools import (
get_global_news,
get_instrument_context_from_state,
get_language_instruction,
get_macro_indicators,
get_news,
get_prediction_markets,
@@ -21,17 +21,14 @@ from datetime import datetime, timedelta
from langchain_core.messages import AIMessage
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from tradingagents.agents.context import get_instrument_context_from_state, get_language_instruction
from tradingagents.agents.schemas import SentimentReport, render_sentiment_report
from tradingagents.agents.utils.agent_utils import (
get_instrument_context_from_state,
get_language_instruction,
get_news,
)
from tradingagents.agents.utils.structured import (
from tradingagents.agents.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
)
from tradingagents.agents.tools import get_news
from tradingagents.dataflows.vendors.reddit import fetch_reddit_posts
from tradingagents.dataflows.vendors.stocktwits import fetch_stocktwits_messages
@@ -1,3 +1,6 @@
"""Prompt context shared by the agents: instrument identity, output language,
portfolio, and the message reset between analysts."""
import functools
import logging
from collections.abc import Mapping
@@ -5,48 +8,9 @@ from typing import Any
from langchain_core.messages import HumanMessage, RemoveMessage
# Import tools from separate utility files
from tradingagents.agents.utils.core_stock_tools import get_stock_data
from tradingagents.agents.utils.fundamental_data_tools import (
get_balance_sheet,
get_cashflow,
get_fundamentals,
get_income_statement,
)
from tradingagents.agents.utils.macro_data_tools import get_macro_indicators
from tradingagents.agents.utils.market_data_validation_tools import get_verified_market_snapshot
from tradingagents.agents.utils.news_data_tools import (
get_global_news,
get_insider_transactions,
get_news,
)
from tradingagents.agents.utils.prediction_markets_tools import get_prediction_markets
from tradingagents.agents.utils.technical_indicators_tools import get_indicators
from tradingagents.dataflows.date_window import get_current_date
from tradingagents.dataflows.vendors.yahoo.fundamentals import get_company_profile
# Public surface: the data tools are imported here so agents and the graph
# import them from one place, plus the instrument/language helpers defined below.
__all__ = [
"get_stock_data",
"get_indicators",
"get_fundamentals",
"get_balance_sheet",
"get_cashflow",
"get_income_statement",
"get_news",
"get_global_news",
"get_insider_transactions",
"get_macro_indicators",
"get_prediction_markets",
"get_verified_market_snapshot",
"build_instrument_context",
"resolve_instrument_identity",
"get_instrument_context_from_state",
"get_language_instruction",
"create_msg_delete",
]
logger = logging.getLogger(__name__)
@@ -10,13 +10,13 @@ back gracefully to free-text generation.
from __future__ import annotations
from tradingagents.agents.schemas import PortfolioDecision, render_pm_decision
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
get_portfolio_context_from_state,
)
from tradingagents.agents.utils.structured import (
from tradingagents.agents.schemas import PortfolioDecision, render_pm_decision
from tradingagents.agents.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
@@ -2,12 +2,9 @@
from __future__ import annotations
from tradingagents.agents.context import get_instrument_context_from_state, get_language_instruction
from tradingagents.agents.schemas import ResearchPlan, render_research_plan
from tradingagents.agents.utils.agent_utils import (
get_instrument_context_from_state,
get_language_instruction,
)
from tradingagents.agents.utils.structured import (
from tradingagents.agents.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
@@ -1,4 +1,4 @@
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
opponent_argument_or_opening,
@@ -1,4 +1,4 @@
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
opponent_argument_or_opening,
@@ -1,4 +1,4 @@
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
get_portfolio_context_from_state,
@@ -1,4 +1,4 @@
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
get_portfolio_context_from_state,
@@ -1,4 +1,4 @@
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
get_portfolio_context_from_state,
+284
View File
@@ -0,0 +1,284 @@
"""The data tools the analysts call.
Each dated tool takes the run's ``trade_date`` from graph state (``InjectedState``)
and never serves data past it, whatever date the model asks for.
"""
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of, as_of_window
from tradingagents.dataflows.router import route_to_vendor
from tradingagents.dataflows.vendors.yahoo.snapshot import build_verified_market_snapshot
@tool
def get_stock_data(
symbol: Annotated[str, "ticker symbol of the company"],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
end_date: Annotated[str, "End date in yyyy-mm-dd format"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve stock price data (OHLCV) for a given ticker symbol.
Uses the configured core_stock_apis vendor.
Args:
symbol (str): Ticker symbol of the company, e.g. AAPL, TSM
start_date (str): Start date in yyyy-mm-dd format
end_date (str): End date in yyyy-mm-dd format
Returns:
str: A formatted dataframe containing the stock price data for the specified ticker symbol in the specified date range.
"""
start_date, end_date = as_of_window(start_date, end_date, trade_date)
return route_to_vendor("get_stock_data", symbol, start_date, end_date)
@tool
def get_indicators(
symbol: Annotated[str, "ticker symbol of the company"],
indicator: Annotated[str, "technical indicator to get the analysis and report of"],
curr_date: Annotated[str, "The current trading date you are trading on, YYYY-mm-dd"],
look_back_days: Annotated[int, "how many days to look back"] = 30,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve a single technical indicator for a given ticker symbol.
Uses the configured technical_indicators vendor.
Args:
symbol (str): Ticker symbol of the company, e.g. AAPL, TSM
indicator (str): A single technical indicator name, e.g. 'rsi', 'macd'. Call this tool once per indicator.
curr_date (str): The current trading date you are trading on, YYYY-mm-dd
look_back_days (int): How many days to look back, default is 30
Returns:
str: A formatted dataframe containing the technical indicators for the specified ticker symbol and indicator.
"""
# LLMs sometimes pass multiple indicators as a comma-separated string;
# split and process each individually.
curr_date = as_of(curr_date, trade_date)
indicators = [i.strip().lower() for i in indicator.split(",") if i.strip()]
results = []
for ind in indicators:
try:
results.append(route_to_vendor("get_indicators", symbol, ind, curr_date, look_back_days))
except ValueError as e:
results.append(str(e))
return "\n\n".join(results)
@tool
def get_verified_market_snapshot(
symbol: Annotated[str, "ticker symbol of the company"],
curr_date: Annotated[str, "the current trading date, YYYY-mm-dd"],
look_back_days: Annotated[
int, "number of recent trading rows to include for sanity-checking"
] = 30,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""Deterministic verification snapshot for exact market-data claims.
Returns the latest OHLCV row on or before curr_date, common technical
indicators, and recent closes. Call this before making exact claims about
price levels, Bollinger bands, RSI, MACD, moving averages, support /
resistance, or historical comparisons, and treat it as the source of truth.
"""
return build_verified_market_snapshot(symbol, as_of(curr_date, trade_date), look_back_days)
@tool
def get_fundamentals(
ticker: Annotated[str, "ticker symbol"],
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve comprehensive fundamental data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing comprehensive fundamental data
"""
return route_to_vendor("get_fundamentals", ticker, as_of(curr_date, trade_date))
@tool
def get_balance_sheet(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve balance sheet data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing balance sheet data
"""
return route_to_vendor("get_balance_sheet", ticker, freq, as_of(curr_date, trade_date))
@tool
def get_cashflow(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve cash flow statement data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing cash flow statement data
"""
return route_to_vendor("get_cashflow", ticker, freq, as_of(curr_date, trade_date))
@tool
def get_income_statement(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve income statement data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing income statement data
"""
return route_to_vendor("get_income_statement", ticker, freq, as_of(curr_date, trade_date))
@tool
def get_news(
ticker: Annotated[str, "Ticker symbol"],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
end_date: Annotated[str, "End date in yyyy-mm-dd format"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve news data for a given ticker symbol.
Uses the configured news_data vendor.
Args:
ticker (str): Ticker symbol
start_date (str): Start date in yyyy-mm-dd format
end_date (str): End date in yyyy-mm-dd format
Returns:
str: A formatted string containing news data
"""
start_date, end_date = as_of_window(start_date, end_date, trade_date)
return route_to_vendor("get_news", ticker, start_date, end_date)
@tool
def get_global_news(
curr_date: Annotated[str, "Current date in yyyy-mm-dd format"],
look_back_days: Annotated[int | None, "Days to look back; omit to use the configured default"] = None,
limit: Annotated[int | None, "Max articles to return; omit to use the configured default"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve global news data.
Uses the configured news_data vendor. Defaults for look_back_days and
limit come from DEFAULT_CONFIG (global_news_lookback_days,
global_news_article_limit); pass explicit values to override.
Args:
curr_date (str): Current date in yyyy-mm-dd format
look_back_days (int): Number of days to look back; omit to inherit config
limit (int): Maximum number of articles to return; omit to inherit config
Returns:
str: A formatted string containing global news data
"""
return route_to_vendor("get_global_news", as_of(curr_date, trade_date), look_back_days, limit)
@tool
def get_insider_transactions(
ticker: Annotated[str, "ticker symbol"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve insider transaction information about a company.
Uses the configured news_data vendor.
Args:
ticker (str): Ticker symbol of the company
Returns:
str: A report of insider transaction data
"""
return route_to_vendor("get_insider_transactions", ticker, trade_date or None)
@tool
def get_macro_indicators(
indicator: Annotated[
str,
"Macro indicator: a friendly alias such as 'cpi', 'core_pce', "
"'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve', "
"'real_gdp', 'vix', or a raw FRED series ID such as 'CPIAUCSL'.",
],
curr_date: Annotated[str, "Current date in yyyy-mm-dd format; the end of the window"],
look_back_days: Annotated[
int | None, "Trailing window length in days; omit for a 1-year window"
] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve a macroeconomic indicator time series from FRED (Federal Reserve
Economic Data): policy rates, Treasury yields, inflation, labor, and growth.
Returns the series title, units, frequency, the latest value, the change
over the window, and a recent observation table. Uses the configured
macro_data vendor.
Args:
indicator (str): Friendly alias or raw FRED series ID
curr_date (str): Current date in yyyy-mm-dd format
look_back_days (int): Trailing window length; omit for a 1-year window
Returns:
str: A formatted markdown report of the macro series
"""
return route_to_vendor("get_macro_indicators", indicator, as_of(curr_date, trade_date), look_back_days)
@tool
def get_prediction_markets(
topic: Annotated[
str,
"Event topic/keyword, e.g. 'Fed rate cut', 'recession 2026', "
"'US election', or a sector/company event.",
],
limit: Annotated[int | None, "Max markets to return; omit for a default of 6"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve live, market-implied probabilities for forward-looking events from
prediction markets (Polymarket): Fed decisions, recession, elections,
geopolitics, crypto. Returns the most-traded open markets matching the
topic, each with its implied probability, traded volume, resolution date,
and recent move. Uses the configured prediction_markets vendor.
Args:
topic (str): Event keyword(s) to search
limit (int): Max markets to return; omit for a default of 6
Returns:
str: A formatted markdown report of matching prediction markets
"""
return route_to_vendor("get_prediction_markets", topic, limit, trade_date or None)
+3 -3
View File
@@ -6,13 +6,13 @@ import functools
from langchain_core.messages import AIMessage
from tradingagents.agents.schemas import TraderProposal, render_trader_proposal
from tradingagents.agents.utils.agent_utils import (
from tradingagents.agents.context import (
get_instrument_context_from_state,
get_language_instruction,
get_portfolio_context_from_state,
)
from tradingagents.agents.utils.structured import (
from tradingagents.agents.schemas import TraderProposal, render_trader_proposal
from tradingagents.agents.structured import (
NO_EXTERNAL_TOOLS,
bind_structured,
invoke_structured_or_freetext,
@@ -1,28 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of_window
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_stock_data(
symbol: Annotated[str, "ticker symbol of the company"],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
end_date: Annotated[str, "End date in yyyy-mm-dd format"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve stock price data (OHLCV) for a given ticker symbol.
Uses the configured core_stock_apis vendor.
Args:
symbol (str): Ticker symbol of the company, e.g. AAPL, TSM
start_date (str): Start date in yyyy-mm-dd format
end_date (str): End date in yyyy-mm-dd format
Returns:
str: A formatted dataframe containing the stock price data for the specified ticker symbol in the specified date range.
"""
start_date, end_date = as_of_window(start_date, end_date, trade_date)
return route_to_vendor("get_stock_data", symbol, start_date, end_date)
@@ -1,85 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_fundamentals(
ticker: Annotated[str, "ticker symbol"],
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve comprehensive fundamental data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing comprehensive fundamental data
"""
return route_to_vendor("get_fundamentals", ticker, as_of(curr_date, trade_date))
@tool
def get_balance_sheet(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve balance sheet data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing balance sheet data
"""
return route_to_vendor("get_balance_sheet", ticker, freq, as_of(curr_date, trade_date))
@tool
def get_cashflow(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve cash flow statement data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing cash flow statement data
"""
return route_to_vendor("get_cashflow", ticker, freq, as_of(curr_date, trade_date))
@tool
def get_income_statement(
ticker: Annotated[str, "ticker symbol"],
freq: Annotated[str, "reporting frequency: annual/quarterly"] = "quarterly",
curr_date: Annotated[str, "current date you are trading at, yyyy-mm-dd"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve income statement data for a given ticker symbol.
Uses the configured fundamental_data vendor.
Args:
ticker (str): Ticker symbol of the company
freq (str): Reporting frequency: annual/quarterly (default quarterly)
curr_date (str): Current date you are trading at, yyyy-mm-dd
Returns:
str: A formatted report containing income statement data
"""
return route_to_vendor("get_income_statement", ticker, freq, as_of(curr_date, trade_date))
@@ -1,39 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_macro_indicators(
indicator: Annotated[
str,
"Macro indicator: a friendly alias such as 'cpi', 'core_pce', "
"'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve', "
"'real_gdp', 'vix', or a raw FRED series ID such as 'CPIAUCSL'.",
],
curr_date: Annotated[str, "Current date in yyyy-mm-dd format; the end of the window"],
look_back_days: Annotated[
int | None, "Trailing window length in days; omit for a 1-year window"
] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve a macroeconomic indicator time series from FRED (Federal Reserve
Economic Data): policy rates, Treasury yields, inflation, labor, and growth.
Returns the series title, units, frequency, the latest value, the change
over the window, and a recent observation table. Uses the configured
macro_data vendor.
Args:
indicator (str): Friendly alias or raw FRED series ID
curr_date (str): Current date in yyyy-mm-dd format
look_back_days (int): Trailing window length; omit for a 1-year window
Returns:
str: A formatted markdown report of the macro series
"""
return route_to_vendor("get_macro_indicators", indicator, as_of(curr_date, trade_date), look_back_days)
@@ -1,26 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of
from tradingagents.dataflows.vendors.yahoo.snapshot import build_verified_market_snapshot
@tool
def get_verified_market_snapshot(
symbol: Annotated[str, "ticker symbol of the company"],
curr_date: Annotated[str, "the current trading date, YYYY-mm-dd"],
look_back_days: Annotated[
int, "number of recent trading rows to include for sanity-checking"
] = 30,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""Deterministic verification snapshot for exact market-data claims.
Returns the latest OHLCV row on or before curr_date, common technical
indicators, and recent closes. Call this before making exact claims about
price levels, Bollinger bands, RSI, MACD, moving averages, support /
resistance, or historical comparisons, and treat it as the source of truth.
"""
return build_verified_market_snapshot(symbol, as_of(curr_date, trade_date), look_back_days)
+1 -1
View File
@@ -3,7 +3,7 @@
import re
from pathlib import Path
from tradingagents.agents.utils.rating import parse_rating
from tradingagents.agents.rating import parse_rating
class TradingMemoryLog:
@@ -1,66 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of, as_of_window
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_news(
ticker: Annotated[str, "Ticker symbol"],
start_date: Annotated[str, "Start date in yyyy-mm-dd format"],
end_date: Annotated[str, "End date in yyyy-mm-dd format"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve news data for a given ticker symbol.
Uses the configured news_data vendor.
Args:
ticker (str): Ticker symbol
start_date (str): Start date in yyyy-mm-dd format
end_date (str): End date in yyyy-mm-dd format
Returns:
str: A formatted string containing news data
"""
start_date, end_date = as_of_window(start_date, end_date, trade_date)
return route_to_vendor("get_news", ticker, start_date, end_date)
@tool
def get_global_news(
curr_date: Annotated[str, "Current date in yyyy-mm-dd format"],
look_back_days: Annotated[int | None, "Days to look back; omit to use the configured default"] = None,
limit: Annotated[int | None, "Max articles to return; omit to use the configured default"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve global news data.
Uses the configured news_data vendor. Defaults for look_back_days and
limit come from DEFAULT_CONFIG (global_news_lookback_days,
global_news_article_limit); pass explicit values to override.
Args:
curr_date (str): Current date in yyyy-mm-dd format
look_back_days (int): Number of days to look back; omit to inherit config
limit (int): Maximum number of articles to return; omit to inherit config
Returns:
str: A formatted string containing global news data
"""
return route_to_vendor("get_global_news", as_of(curr_date, trade_date), look_back_days, limit)
@tool
def get_insider_transactions(
ticker: Annotated[str, "ticker symbol"],
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve insider transaction information about a company.
Uses the configured news_data vendor.
Args:
ticker (str): Ticker symbol of the company
Returns:
str: A report of insider transaction data
"""
return route_to_vendor("get_insider_transactions", ticker, trade_date or None)
@@ -1,33 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_prediction_markets(
topic: Annotated[
str,
"Event topic/keyword, e.g. 'Fed rate cut', 'recession 2026', "
"'US election', or a sector/company event.",
],
limit: Annotated[int | None, "Max markets to return; omit for a default of 6"] = None,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve live, market-implied probabilities for forward-looking events from
prediction markets (Polymarket): Fed decisions, recession, elections,
geopolitics, crypto. Returns the most-traded open markets matching the
topic, each with its implied probability, traded volume, resolution date,
and recent move. Uses the configured prediction_markets vendor.
Args:
topic (str): Event keyword(s) to search
limit (int): Max markets to return; omit for a default of 6
Returns:
str: A formatted markdown report of matching prediction markets
"""
return route_to_vendor("get_prediction_markets", topic, limit, trade_date or None)
@@ -1,39 +0,0 @@
from typing import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState
from tradingagents.dataflows.date_window import as_of
from tradingagents.dataflows.router import route_to_vendor
@tool
def get_indicators(
symbol: Annotated[str, "ticker symbol of the company"],
indicator: Annotated[str, "technical indicator to get the analysis and report of"],
curr_date: Annotated[str, "The current trading date you are trading on, YYYY-mm-dd"],
look_back_days: Annotated[int, "how many days to look back"] = 30,
trade_date: Annotated[str, InjectedState("trade_date")] = "",
) -> str:
"""
Retrieve a single technical indicator for a given ticker symbol.
Uses the configured technical_indicators vendor.
Args:
symbol (str): Ticker symbol of the company, e.g. AAPL, TSM
indicator (str): A single technical indicator name, e.g. 'rsi', 'macd'. Call this tool once per indicator.
curr_date (str): The current trading date you are trading on, YYYY-mm-dd
look_back_days (int): How many days to look back, default is 30
Returns:
str: A formatted dataframe containing the technical indicators for the specified ticker symbol and indicator.
"""
# LLMs sometimes pass multiple indicators as a comma-separated string;
# split and process each individually.
curr_date = as_of(curr_date, trade_date)
indicators = [i.strip().lower() for i in indicator.split(",") if i.strip()]
results = []
for ind in indicators:
try:
results.append(route_to_vendor("get_indicators", symbol, ind, curr_date, look_back_days))
except ValueError as e:
results.append(str(e))
return "\n\n".join(results)
+1 -1
View File
@@ -21,8 +21,8 @@ from dataclasses import dataclass, field
from datetime import datetime, timedelta
from pathlib import Path
from tradingagents.agents.rating import RATING_REVIEW
from tradingagents.agents.utils.memory import TradingMemoryLog
from tradingagents.agents.utils.rating import RATING_REVIEW
from tradingagents.dataflows.date_window import get_current_date
from tradingagents.dataflows.symbols import safe_ticker_component
from tradingagents.graph.trading_graph import TradingAgentsGraph
+1 -1
View File
@@ -1,6 +1,6 @@
# TradingAgents/graph/conditional_logic.py
from tradingagents.agents.utils.agent_states import AgentState
from tradingagents.agents.state import AgentState
class ConditionalLogic:
+1 -4
View File
@@ -2,10 +2,7 @@
from typing import Any
from tradingagents.agents.utils.agent_states import (
InvestDebateState,
RiskDebateState,
)
from tradingagents.agents.state import InvestDebateState, RiskDebateState
class Propagator:
+1 -1
View File
@@ -20,7 +20,7 @@ from tradingagents.agents import (
create_sentiment_analyst,
create_trader,
)
from tradingagents.agents.utils.agent_states import AgentState
from tradingagents.agents.state import AgentState
from .analyst_execution import build_analyst_execution_plan
from .conditional_logic import ConditionalLogic
+3 -6
View File
@@ -8,12 +8,9 @@ from datetime import datetime, timedelta
from pathlib import Path
from typing import Any
from tradingagents.agents.utils.agent_utils import (
build_instrument_context,
resolve_instrument_identity,
)
from tradingagents.agents.context import build_instrument_context, resolve_instrument_identity
from tradingagents.agents.rating import parse_rating
from tradingagents.agents.utils.memory import TradingMemoryLog
from tradingagents.agents.utils.rating import parse_rating
from tradingagents.dataflows.config import run_config, set_config
from tradingagents.dataflows.date_window import get_current_date
from tradingagents.dataflows.symbols import safe_ticker_component
@@ -370,7 +367,7 @@ class TradingAgentsGraph:
Returns ``(final_state, signal)`` where ``signal`` is one of the 5-tier
ratings (Buy / Overweight / Hold / Underweight / Sell) or ``"REVIEW"``
when the decision had no parseable rating (#1170); guard with
``tradingagents.agents.utils.rating.is_review`` before mapping it to the
``tradingagents.agents.rating.is_review`` before mapping it to the
PortfolioRating enum.
"""
trade_date = _validate_trade_date(trade_date)