fix(agents): serve the run's instrument, whatever symbol the model passes

- the nine ticker-taking tools read company_of_interest from graph state, like trade_date; the model no longer supplies a symbol (it had passed indicator names such as RSI, a real ticker)
- the News Analyst prompt and the tool descriptions no longer ask for a ticker
This commit is contained in:
Yijia-Xiao
2026-09-29 05:57:17 +00:00
parent 9470b92150
commit 052a98d6eb
5 changed files with 56 additions and 43 deletions
+7 -7
View File
@@ -1,7 +1,8 @@
"""Guard the news analyst prompt against tool-signature drift (#1116).
The prompt used to advertise ``get_news(query, ...)`` while the tool takes a
``ticker``, tricking the LLM into hallucinating free-text query calls.
The prompt used to advertise ``get_news(query, ...)``, tricking the LLM into
hallucinating free-text query calls. The instrument now comes from the run's
state, so the model passes only the window.
"""
import inspect
@@ -12,14 +13,13 @@ from tradingagents.agents.tools import get_news
@pytest.mark.unit
def test_get_news_takes_ticker_not_query():
arg_names = set(get_news.args.keys())
assert "ticker" in arg_names
assert "query" not in arg_names
def test_get_news_takes_the_window_not_a_query():
arg_names = set(get_news.tool_call_schema.model_json_schema()["properties"])
assert arg_names == {"start_date", "end_date"}
@pytest.mark.unit
def test_news_prompt_matches_get_news_signature():
src = inspect.getsource(na)
assert "get_news(ticker, start_date, end_date)" in src
assert "get_news(start_date, end_date)" in src
assert "get_news(query" not in src
+29 -7
View File
@@ -68,8 +68,31 @@ def test_trade_date_is_hidden_from_the_model(tool):
assert "trade_date" not in tool.tool_call_schema.model_json_schema()["properties"]
INSTRUMENT_TOOLS = [
tools.get_stock_data, tools.get_indicators, tools.get_verified_market_snapshot,
tools.get_fundamentals, tools.get_balance_sheet, tools.get_cashflow,
tools.get_income_statement, tools.get_news, tools.get_insider_transactions,
]
@pytest.mark.unit
@pytest.mark.parametrize("tool", INSTRUMENT_TOOLS, ids=lambda t: t.name)
def test_the_instrument_is_hidden_from_the_model(tool):
"""The model once passed an indicator name as the symbol ("rsi", "atr" are
real tickers), and another company's data reached the report."""
properties = tool.tool_call_schema.model_json_schema()["properties"]
assert "symbol" not in properties and "ticker" not in properties
@pytest.mark.unit
def test_a_tool_serves_the_run_instrument_whatever_the_model_passes():
args = _run(tools.get_indicators, {"symbol": "RSI", "indicator": "rsi", "curr_date": TRADE_DATE}, tools)
assert args[1] == "NVDA"
class _State(MessagesState):
trade_date: str
company_of_interest: str
def _run(tool, args, module):
@@ -82,28 +105,27 @@ def _run(tool, args, module):
graph.compile().invoke({
"messages": [AIMessage("", tool_calls=[{"name": tool.name, "args": args, "id": "1"}])],
"trade_date": TRADE_DATE,
"company_of_interest": "NVDA",
})
return routed.call_args.args
@pytest.mark.unit
def test_statement_tool_with_omitted_date_uses_the_run_date():
args = _run(tools.get_balance_sheet, {"ticker": "AAPL"}, tools)
args = _run(tools.get_balance_sheet, {}, 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(tools.get_fundamentals,
{"ticker": "AAPL", "curr_date": "2026-09-14"}, tools)
assert args == ("get_fundamentals", "AAPL", TRADE_DATE)
args = _run(tools.get_fundamentals, {"curr_date": "2026-09-14"}, tools)
assert args == ("get_fundamentals", "NVDA", TRADE_DATE)
@pytest.mark.unit
def test_future_window_from_the_model_is_clamped():
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)
args = _run(tools.get_stock_data, {"start_date": "2026-08-01", "end_date": "2026-09-14"}, tools)
assert args == ("get_stock_data", "NVDA", "2026-08-01", TRADE_DATE)
@pytest.mark.unit
@@ -25,7 +25,7 @@ def create_news_analyst(llm):
instrument_context = get_instrument_context_from_state(state)
system_message = (
f"You are a news researcher tasked with analyzing recent news and trends over the past week. Please write a comprehensive report of the current state of the world that is relevant for trading and macroeconomics. Use the available tools: get_news(ticker, start_date, end_date) for {asset_label}-specific news by ticker symbol, get_global_news(curr_date, look_back_days, limit) for broader macroeconomic news, get_macro_indicators(indicator, curr_date, look_back_days) to ground macro commentary in actual data from FRED (e.g. 'cpi', 'core_pce', 'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve'), and get_prediction_markets(topic, limit) for live market-implied probabilities of forward-looking events (e.g. 'Fed rate cut', 'recession 2026', geopolitical or sector events). Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
f"You are a news researcher tasked with analyzing recent news and trends over the past week. Please write a comprehensive report of the current state of the world that is relevant for trading and macroeconomics. Use the available tools: get_news(start_date, end_date) for news about the {asset_label} under analysis, get_global_news(curr_date, look_back_days, limit) for broader macroeconomic news, get_macro_indicators(indicator, curr_date, look_back_days) to ground macro commentary in actual data from FRED (e.g. 'cpi', 'core_pce', 'unemployment', 'fed_funds_rate', '10y_treasury', 'yield_curve'), and get_prediction_markets(topic, limit) for live market-implied probabilities of forward-looking events (e.g. 'Fed rate cut', 'recession 2026', geopolitical or sector events). Provide specific, actionable insights with supporting evidence to help traders make informed decisions."
+ """ Make sure to append a Markdown table at the end of the report to organize key points in the report, organized and easy to read."""
+ get_language_instruction()
)
+1 -1
View File
@@ -116,7 +116,7 @@ def build_instrument_context(
instrument_label = "asset" if is_crypto else "instrument"
context = (
f"The {instrument_label} to analyze is `{ticker}`. "
"Use this exact ticker in every tool call, report, and recommendation, "
"The tools serve this instrument; refer to it by this exact ticker in every report and recommendation, "
"preserving any exchange suffix (e.g. `.TO`, `.L`, `.HK`, `.T`, `-USD`)."
)
+18 -27
View File
@@ -17,20 +17,19 @@ from tradingagents.dataflows.vendors.yahoo.snapshot import build_verified_market
@tool
def get_stock_data(
symbol: Annotated[str, "ticker symbol of the company"],
symbol: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve stock price data (OHLCV) for the instrument under analysis.
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.
str: A formatted dataframe containing the price data for the instrument over the 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)
@@ -38,22 +37,21 @@ def get_stock_data(
@tool
def get_indicators(
symbol: Annotated[str, "ticker symbol of the company"],
symbol: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve a single technical indicator for the instrument under analysis.
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.
str: A formatted dataframe containing the technical indicators for the instrument and indicator.
"""
# LLMs sometimes pass multiple indicators as a comma-separated string;
# split and process each individually.
@@ -70,7 +68,7 @@ def get_indicators(
@tool
def get_verified_market_snapshot(
symbol: Annotated[str, "ticker symbol of the company"],
symbol: Annotated[str, InjectedState("company_of_interest")],
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"
@@ -93,15 +91,14 @@ def get_verified_market_snapshot(
@tool
def get_fundamentals(
ticker: Annotated[str, "ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve comprehensive fundamental data for the instrument under analysis.
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
@@ -111,16 +108,15 @@ def get_fundamentals(
@tool
def get_balance_sheet(
ticker: Annotated[str, "ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve balance sheet data for the instrument under analysis.
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:
@@ -131,16 +127,15 @@ def get_balance_sheet(
@tool
def get_cashflow(
ticker: Annotated[str, "ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve cash flow statement data for the instrument under analysis.
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:
@@ -151,16 +146,15 @@ def get_cashflow(
@tool
def get_income_statement(
ticker: Annotated[str, "ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve income statement data for the instrument under analysis.
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:
@@ -171,16 +165,15 @@ def get_income_statement(
@tool
def get_news(
ticker: Annotated[str, "Ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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.
Retrieve news data for the instrument under analysis.
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:
@@ -216,14 +209,12 @@ def get_global_news(
@tool
def get_insider_transactions(
ticker: Annotated[str, "ticker symbol"],
ticker: Annotated[str, InjectedState("company_of_interest")],
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
"""