From 852ffead43338277e24cca9c3a9a82c0240df315 Mon Sep 17 00:00:00 2001 From: Yijia-Xiao Date: Wed, 23 Sep 2026 23:23:22 +0000 Subject: [PATCH] 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__ --- cli/main.py | 2 +- tests/test_cli_decision_log.py | 2 +- tests/test_debate_opening.py | 2 +- tests/test_graph_end_to_end.py | 5 +- tests/test_i18n_coverage.py | 2 +- tests/test_instrument_identity.py | 2 +- tests/test_memory_log.py | 2 +- tests/test_news_analyst_prompt.py | 2 +- tests/test_portfolio_context.py | 2 +- tests/test_rating_integrity.py | 6 +- tests/test_signal_processing.py | 7 +- tests/test_structured_agent_prompts.py | 2 +- tests/test_structured_agents.py | 2 +- tests/test_symbol_normalization_paths.py | 2 +- tests/test_ticker_symbol_handling.py | 2 +- tests/test_tool_date_enforcement.py | 43 ++- tests/test_undated_tools_as_of.py | 12 +- tests/test_yahoo_snapshot.py | 4 +- tradingagents/agents/__init__.py | 4 +- tradingagents/agents/analysts/__init__.py | 0 .../agents/analysts/fundamentals_analyst.py | 5 +- .../agents/analysts/market_analyst.py | 9 +- tradingagents/agents/analysts/news_analyst.py | 5 +- .../agents/analysts/sentiment_analyst.py | 9 +- .../{utils/agent_utils.py => context.py} | 42 +-- tradingagents/agents/managers/__init__.py | 0 .../agents/managers/portfolio_manager.py | 6 +- .../agents/managers/research_manager.py | 7 +- tradingagents/agents/{utils => }/rating.py | 0 tradingagents/agents/researchers/__init__.py | 0 .../agents/researchers/bear_researcher.py | 2 +- .../agents/researchers/bull_researcher.py | 2 +- tradingagents/agents/risk_mgmt/__init__.py | 0 .../agents/risk_mgmt/aggressive_debator.py | 2 +- .../agents/risk_mgmt/conservative_debator.py | 2 +- .../agents/risk_mgmt/neutral_debator.py | 2 +- .../{utils/agent_states.py => state.py} | 0 .../agents/{utils => }/structured.py | 0 tradingagents/agents/tools.py | 284 ++++++++++++++++++ tradingagents/agents/trader/__init__.py | 0 tradingagents/agents/trader/trader.py | 6 +- .../agents/utils/core_stock_tools.py | 28 -- .../agents/utils/fundamental_data_tools.py | 85 ------ .../agents/utils/macro_data_tools.py | 39 --- .../utils/market_data_validation_tools.py | 26 -- tradingagents/agents/utils/memory.py | 2 +- tradingagents/agents/utils/news_data_tools.py | 66 ---- .../agents/utils/prediction_markets_tools.py | 33 -- .../utils/technical_indicators_tools.py | 39 --- tradingagents/backtest.py | 2 +- tradingagents/graph/conditional_logic.py | 2 +- tradingagents/graph/propagation.py | 5 +- tradingagents/graph/setup.py | 2 +- tradingagents/graph/trading_graph.py | 9 +- 54 files changed, 362 insertions(+), 464 deletions(-) create mode 100644 tradingagents/agents/analysts/__init__.py rename tradingagents/agents/{utils/agent_utils.py => context.py} (87%) create mode 100644 tradingagents/agents/managers/__init__.py rename tradingagents/agents/{utils => }/rating.py (100%) create mode 100644 tradingagents/agents/researchers/__init__.py create mode 100644 tradingagents/agents/risk_mgmt/__init__.py rename tradingagents/agents/{utils/agent_states.py => state.py} (100%) rename tradingagents/agents/{utils => }/structured.py (100%) create mode 100644 tradingagents/agents/tools.py create mode 100644 tradingagents/agents/trader/__init__.py delete mode 100644 tradingagents/agents/utils/core_stock_tools.py delete mode 100644 tradingagents/agents/utils/fundamental_data_tools.py delete mode 100644 tradingagents/agents/utils/macro_data_tools.py delete mode 100644 tradingagents/agents/utils/market_data_validation_tools.py delete mode 100644 tradingagents/agents/utils/news_data_tools.py delete mode 100644 tradingagents/agents/utils/prediction_markets_tools.py delete mode 100644 tradingagents/agents/utils/technical_indicators_tools.py diff --git a/cli/main.py b/cli/main.py index 99fde1aad..4c792928a 100644 --- a/cli/main.py +++ b/cli/main.py @@ -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 diff --git a/tests/test_cli_decision_log.py b/tests/test_cli_decision_log.py index a53a34232..c381ce7a8 100644 --- a/tests/test_cli_decision_log.py +++ b/tests/test_cli_decision_log.py @@ -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): diff --git a/tests/test_debate_opening.py b/tests/test_debate_opening.py index a20a08b8a..7e0396849 100644 --- a/tests/test_debate_opening.py +++ b/tests/test_debate_opening.py @@ -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", diff --git a/tests/test_graph_end_to_end.py b/tests/test_graph_end_to_end.py index 65e0bdf63..2bd846b89 100644 --- a/tests/test_graph_end_to_end.py +++ b/tests/test_graph_end_to_end.py @@ -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 diff --git a/tests/test_i18n_coverage.py b/tests/test_i18n_coverage.py index 67ca08b4f..e101dd1df 100644 --- a/tests/test_i18n_coverage.py +++ b/tests/test_i18n_coverage.py @@ -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" diff --git a/tests/test_instrument_identity.py b/tests/test_instrument_identity.py index c226b48b5..4c12a4769 100644 --- a/tests/test_instrument_identity.py +++ b/tests/test_instrument_identity.py @@ -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, diff --git a/tests/test_memory_log.py b/tests/test_memory_log.py index 1cb894443..015d2d0a9 100644 --- a/tests/test_memory_log.py +++ b/tests/test_memory_log.py @@ -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) diff --git a/tests/test_news_analyst_prompt.py b/tests/test_news_analyst_prompt.py index bc29ae9a6..4af6b04e8 100644 --- a/tests/test_news_analyst_prompt.py +++ b/tests/test_news_analyst_prompt.py @@ -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 diff --git a/tests/test_portfolio_context.py b/tests/test_portfolio_context.py index 12305a8ec..dbf5f80eb 100644 --- a/tests/test_portfolio_context.py +++ b/tests/test_portfolio_context.py @@ -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 = { diff --git a/tests/test_rating_integrity.py b/tests/test_rating_integrity.py index d50a0f7d3..55a5e5627 100644 --- a/tests/test_rating_integrity.py +++ b/tests/test_rating_integrity.py @@ -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): diff --git a/tests/test_signal_processing.py b/tests/test_signal_processing.py index e77288144..4a0440c6f 100644 --- a/tests/test_signal_processing.py +++ b/tests/test_signal_processing.py @@ -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 diff --git a/tests/test_structured_agent_prompts.py b/tests/test_structured_agent_prompts.py index d7b24061c..c89fae767 100644 --- a/tests/test_structured_agent_prompts.py +++ b/tests/test_structured_agent_prompts.py @@ -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): diff --git a/tests/test_structured_agents.py b/tests/test_structured_agents.py index efc104aeb..d385e0ea3 100644 --- a/tests/test_structured_agents.py +++ b/tests/test_structured_agents.py @@ -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 diff --git a/tests/test_symbol_normalization_paths.py b/tests/test_symbol_normalization_paths.py index fbddbcb41..d29974c3b 100644 --- a/tests/test_symbol_normalization_paths.py +++ b/tests/test_symbol_normalization_paths.py @@ -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 diff --git a/tests/test_ticker_symbol_handling.py b/tests/test_ticker_symbol_handling.py index 99db0b1eb..37c32a84c 100644 --- a/tests/test_ticker_symbol_handling.py +++ b/tests/test_ticker_symbol_handling.py @@ -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 diff --git a/tests/test_tool_date_enforcement.py b/tests/test_tool_date_enforcement.py index d3e8e7fb7..c313a2db7 100644 --- a/tests/test_tool_date_enforcement.py +++ b/tests/test_tool_date_enforcement.py @@ -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") diff --git a/tests/test_undated_tools_as_of.py b/tests/test_undated_tools_as_of.py index 254489a68..2ece8366d 100644 --- a/tests/test_undated_tools_as_of.py +++ b/tests/test_undated_tools_as_of.py @@ -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, diff --git a/tests/test_yahoo_snapshot.py b/tests/test_yahoo_snapshot.py index 066cf517e..51c852c08 100644 --- a/tests/test_yahoo_snapshot.py +++ b/tests/test_yahoo_snapshot.py @@ -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"} diff --git a/tradingagents/agents/__init__.py b/tradingagents/agents/__init__.py index f8aa4d373..5675c7d2e 100644 --- a/tradingagents/agents/__init__.py +++ b/tradingagents/agents/__init__.py @@ -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", diff --git a/tradingagents/agents/analysts/__init__.py b/tradingagents/agents/analysts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tradingagents/agents/analysts/fundamentals_analyst.py b/tradingagents/agents/analysts/fundamentals_analyst.py index 9588c0e18..f368a25de 100644 --- a/tradingagents/agents/analysts/fundamentals_analyst.py +++ b/tradingagents/agents/analysts/fundamentals_analyst.py @@ -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. diff --git a/tradingagents/agents/analysts/market_analyst.py b/tradingagents/agents/analysts/market_analyst.py index 3fe15dc54..b8abf576d 100644 --- a/tradingagents/agents/analysts/market_analyst.py +++ b/tradingagents/agents/analysts/market_analyst.py @@ -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 = ( diff --git a/tradingagents/agents/analysts/news_analyst.py b/tradingagents/agents/analysts/news_analyst.py index 2e701ea0d..fc9e0c9c9 100644 --- a/tradingagents/agents/analysts/news_analyst.py +++ b/tradingagents/agents/analysts/news_analyst.py @@ -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, diff --git a/tradingagents/agents/analysts/sentiment_analyst.py b/tradingagents/agents/analysts/sentiment_analyst.py index 9dca1bd6d..82f31f65a 100644 --- a/tradingagents/agents/analysts/sentiment_analyst.py +++ b/tradingagents/agents/analysts/sentiment_analyst.py @@ -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 diff --git a/tradingagents/agents/utils/agent_utils.py b/tradingagents/agents/context.py similarity index 87% rename from tradingagents/agents/utils/agent_utils.py rename to tradingagents/agents/context.py index 2454ee0b2..69276fd61 100644 --- a/tradingagents/agents/utils/agent_utils.py +++ b/tradingagents/agents/context.py @@ -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__) diff --git a/tradingagents/agents/managers/__init__.py b/tradingagents/agents/managers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tradingagents/agents/managers/portfolio_manager.py b/tradingagents/agents/managers/portfolio_manager.py index c85039236..442fbb5d6 100644 --- a/tradingagents/agents/managers/portfolio_manager.py +++ b/tradingagents/agents/managers/portfolio_manager.py @@ -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, diff --git a/tradingagents/agents/managers/research_manager.py b/tradingagents/agents/managers/research_manager.py index 0ffd2f4a2..f1bb2c4fc 100644 --- a/tradingagents/agents/managers/research_manager.py +++ b/tradingagents/agents/managers/research_manager.py @@ -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, diff --git a/tradingagents/agents/utils/rating.py b/tradingagents/agents/rating.py similarity index 100% rename from tradingagents/agents/utils/rating.py rename to tradingagents/agents/rating.py diff --git a/tradingagents/agents/researchers/__init__.py b/tradingagents/agents/researchers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tradingagents/agents/researchers/bear_researcher.py b/tradingagents/agents/researchers/bear_researcher.py index dddbefa52..d483e893d 100644 --- a/tradingagents/agents/researchers/bear_researcher.py +++ b/tradingagents/agents/researchers/bear_researcher.py @@ -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, diff --git a/tradingagents/agents/researchers/bull_researcher.py b/tradingagents/agents/researchers/bull_researcher.py index 28af77ea2..a671d1360 100644 --- a/tradingagents/agents/researchers/bull_researcher.py +++ b/tradingagents/agents/researchers/bull_researcher.py @@ -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, diff --git a/tradingagents/agents/risk_mgmt/__init__.py b/tradingagents/agents/risk_mgmt/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tradingagents/agents/risk_mgmt/aggressive_debator.py b/tradingagents/agents/risk_mgmt/aggressive_debator.py index c0555e0b6..4d977dc78 100644 --- a/tradingagents/agents/risk_mgmt/aggressive_debator.py +++ b/tradingagents/agents/risk_mgmt/aggressive_debator.py @@ -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, diff --git a/tradingagents/agents/risk_mgmt/conservative_debator.py b/tradingagents/agents/risk_mgmt/conservative_debator.py index 0a9d67541..e8207a890 100644 --- a/tradingagents/agents/risk_mgmt/conservative_debator.py +++ b/tradingagents/agents/risk_mgmt/conservative_debator.py @@ -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, diff --git a/tradingagents/agents/risk_mgmt/neutral_debator.py b/tradingagents/agents/risk_mgmt/neutral_debator.py index 661b70ab4..9abd04157 100644 --- a/tradingagents/agents/risk_mgmt/neutral_debator.py +++ b/tradingagents/agents/risk_mgmt/neutral_debator.py @@ -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, diff --git a/tradingagents/agents/utils/agent_states.py b/tradingagents/agents/state.py similarity index 100% rename from tradingagents/agents/utils/agent_states.py rename to tradingagents/agents/state.py diff --git a/tradingagents/agents/utils/structured.py b/tradingagents/agents/structured.py similarity index 100% rename from tradingagents/agents/utils/structured.py rename to tradingagents/agents/structured.py diff --git a/tradingagents/agents/tools.py b/tradingagents/agents/tools.py new file mode 100644 index 000000000..0ea4667e2 --- /dev/null +++ b/tradingagents/agents/tools.py @@ -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) diff --git a/tradingagents/agents/trader/__init__.py b/tradingagents/agents/trader/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tradingagents/agents/trader/trader.py b/tradingagents/agents/trader/trader.py index a82ee4bab..1b85ae404 100644 --- a/tradingagents/agents/trader/trader.py +++ b/tradingagents/agents/trader/trader.py @@ -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, diff --git a/tradingagents/agents/utils/core_stock_tools.py b/tradingagents/agents/utils/core_stock_tools.py deleted file mode 100644 index 16e04d830..000000000 --- a/tradingagents/agents/utils/core_stock_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/agents/utils/fundamental_data_tools.py b/tradingagents/agents/utils/fundamental_data_tools.py deleted file mode 100644 index 96b15fcca..000000000 --- a/tradingagents/agents/utils/fundamental_data_tools.py +++ /dev/null @@ -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)) diff --git a/tradingagents/agents/utils/macro_data_tools.py b/tradingagents/agents/utils/macro_data_tools.py deleted file mode 100644 index 90c8bd08d..000000000 --- a/tradingagents/agents/utils/macro_data_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/agents/utils/market_data_validation_tools.py b/tradingagents/agents/utils/market_data_validation_tools.py deleted file mode 100644 index 5ace13ef1..000000000 --- a/tradingagents/agents/utils/market_data_validation_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/agents/utils/memory.py b/tradingagents/agents/utils/memory.py index 6abb5f631..d7a7a9a1a 100644 --- a/tradingagents/agents/utils/memory.py +++ b/tradingagents/agents/utils/memory.py @@ -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: diff --git a/tradingagents/agents/utils/news_data_tools.py b/tradingagents/agents/utils/news_data_tools.py deleted file mode 100644 index 60ed7a4d7..000000000 --- a/tradingagents/agents/utils/news_data_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/agents/utils/prediction_markets_tools.py b/tradingagents/agents/utils/prediction_markets_tools.py deleted file mode 100644 index b127582d8..000000000 --- a/tradingagents/agents/utils/prediction_markets_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/agents/utils/technical_indicators_tools.py b/tradingagents/agents/utils/technical_indicators_tools.py deleted file mode 100644 index cc97c13ad..000000000 --- a/tradingagents/agents/utils/technical_indicators_tools.py +++ /dev/null @@ -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) diff --git a/tradingagents/backtest.py b/tradingagents/backtest.py index 5a7afef4d..c88844035 100644 --- a/tradingagents/backtest.py +++ b/tradingagents/backtest.py @@ -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 diff --git a/tradingagents/graph/conditional_logic.py b/tradingagents/graph/conditional_logic.py index 32bade664..ba8543769 100644 --- a/tradingagents/graph/conditional_logic.py +++ b/tradingagents/graph/conditional_logic.py @@ -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: diff --git a/tradingagents/graph/propagation.py b/tradingagents/graph/propagation.py index 0bb2e461a..50e387d35 100644 --- a/tradingagents/graph/propagation.py +++ b/tradingagents/graph/propagation.py @@ -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: diff --git a/tradingagents/graph/setup.py b/tradingagents/graph/setup.py index 586ae55d2..7277cb26f 100644 --- a/tradingagents/graph/setup.py +++ b/tradingagents/graph/setup.py @@ -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 diff --git a/tradingagents/graph/trading_graph.py b/tradingagents/graph/trading_graph.py index caab877c1..247f9ec59 100644 --- a/tradingagents/graph/trading_graph.py +++ b/tradingagents/graph/trading_graph.py @@ -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)