mirror of
https://github.com/TauricResearch/TradingAgents.git
synced 2026-09-27 23:12:39 +03:00
- the Research Manager's plan and the Portfolio Manager's decision live only in investment_plan and final_trade_decision; the debate states no longer copy them as judge_decision - the saved state log writes the Trader's plan as trader_investment_plan, its state key - the report tree, the final display and the live CLI read the decisions from those fields
88 lines
3.8 KiB
Python
88 lines
3.8 KiB
Python
"""What the agents are actually told.
|
|
|
|
Three problems the audit found: one analyst's brief reached the model as a Python
|
|
tuple, every analyst was asked for a trade call that nothing reads, and a report
|
|
that was never produced was presented as an empty labelled section, which invites
|
|
the next agent to fill it in from nothing.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
|
|
import pytest
|
|
|
|
ANALYSTS = ["market_analyst", "sentiment_analyst", "news_analyst", "fundamentals_analyst"]
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize("name", ANALYSTS)
|
|
def test_an_analyst_brief_is_text_not_a_python_object(name):
|
|
"""A trailing comma made one brief a tuple, so the model was handed its repr
|
|
(quotes, parens and all) instead of the instruction."""
|
|
import ast
|
|
import inspect
|
|
|
|
mod = importlib.import_module(f"tradingagents.agents.analysts.{name}")
|
|
tree = ast.parse(inspect.getsource(mod))
|
|
briefs = [node.value for node in ast.walk(tree)
|
|
if isinstance(node, ast.Assign)
|
|
and getattr(node.targets[0], "id", "") == "system_message"]
|
|
assert briefs, f"{name} has no system_message"
|
|
for brief in briefs:
|
|
assert not isinstance(brief, ast.Tuple), "the brief is a tuple, not text"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize("name", ANALYSTS)
|
|
def test_an_analyst_is_not_asked_for_a_trade_call_nothing_reads(name):
|
|
"""The stop signal is never consumed, and asking for it makes an analyst
|
|
open with a direction that then travels as evidence."""
|
|
import inspect
|
|
|
|
mod = importlib.import_module(f"tradingagents.agents.analysts.{name}")
|
|
assert "FINAL TRANSACTION PROPOSAL" not in inspect.getsource(mod)
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.parametrize("module, factory", [
|
|
("tradingagents.agents.researchers.bull_researcher", "create_bull_researcher"),
|
|
("tradingagents.agents.researchers.bear_researcher", "create_bear_researcher"),
|
|
("tradingagents.agents.risk_mgmt.aggressive_debator", "create_aggressive_debator"),
|
|
("tradingagents.agents.risk_mgmt.conservative_debator", "create_conservative_debator"),
|
|
("tradingagents.agents.risk_mgmt.neutral_debator", "create_neutral_debator"),
|
|
])
|
|
def test_a_report_that_was_never_produced_says_so(module, factory):
|
|
"""`--analysts market` leaves three reports empty; presenting them as blank
|
|
sections invites the model to invent the contents."""
|
|
from langchain_core.messages import AIMessage
|
|
|
|
mod = importlib.import_module(module)
|
|
seen = []
|
|
|
|
class _LLM:
|
|
def invoke(self, prompt, *a, **k):
|
|
seen.append(prompt if isinstance(prompt, str) else str(prompt))
|
|
return AIMessage("argument")
|
|
|
|
def with_structured_output(self, *a, **k):
|
|
raise NotImplementedError
|
|
|
|
state = {
|
|
"company_of_interest": "NVDA", "trade_date": "2026-08-14", "asset_type": "stock",
|
|
"instrument_context": "", "portfolio_context": "", "past_context": "",
|
|
"market_report": "RSI 61, price 178.", "sentiment_report": "", "news_report": "",
|
|
"fundamentals_report": "", "investment_plan": "P", "trader_investment_plan": "T",
|
|
"investment_debate_state": {"bull_history": "", "bear_history": "", "history": "",
|
|
"current_response": "", "count": 0},
|
|
"risk_debate_state": {"history": "", "latest_speaker": "", "count": 0,
|
|
"aggressive_history": "", "conservative_history": "", "neutral_history": "",
|
|
"current_aggressive_response": "", "current_conservative_response": "",
|
|
"current_neutral_response": ""},
|
|
}
|
|
getattr(mod, factory)(_LLM())(state)
|
|
|
|
prompt = " ".join(seen)
|
|
assert "not part of this run" in prompt or "not available" in prompt, prompt[:400]
|
|
assert "RSI 61" in prompt # the report that does exist is still passed through
|