TradingAgents/docs/agent/context/CONVENTIONS.md

6.4 KiB

Conventions

Configuration

  • Env var override pattern: TRADINGAGENTS_<UPPERCASE_KEY>=value — empty/unset preserves default. (default_config.py)
  • Per-tier overrides: each tier has {tier}_llm_provider and {tier}_backend_url, falling back to top-level llm_provider and backend_url. (default_config.py)
  • load_dotenv() runs at module level in default_config.py — import-order-independent. Check actual env var values when debugging auth. (default_config.py)
  • llm_provider and backend_url must always exist at top level — scanner_graph.py and trading_graph.py use them as fallbacks. (ADR 006)
  • mid_think_llm defaults to None, meaning mid-tier falls back to quick_think_llm. (default_config.py)

Agent Creation

  • Factory pattern: create_X(llm) returns a closure _node(state). Some factories take extra params: create_bull_researcher(llm, memory), create_trader(llm, memory). (tradingagents/agents/)
  • When bind_tools() is used, there MUST be a tool execution path — either ToolNode in graph or run_tool_loop() inline. (ADR 004)

Tool Execution

  • Trading graph: analysts use ToolNode in the LangGraph graph with conditional routing (should_continue_X). (graph/setup.py)
  • Scanner agents: use run_tool_loop() inline — no ToolNode, tools execute inside the agent node. (agents/utils/tool_runner.py)
  • MAX_TOOL_ROUNDS = 5 — max iterations of tool calling before returning. (tool_runner.py)
  • MIN_REPORT_LENGTH = 2000 — if first response is shorter and has no tool calls, a nudge message is appended asking the LLM to call tools. Fires at most once. (tool_runner.py)

Vendor Routing

  • Fail-fast by default (ADR 011). Only methods in FALLBACK_ALLOWED get cross-vendor fallback:
    • get_stock_data
    • get_market_indices
    • get_sector_performance
    • get_market_movers
    • get_industry_performance
  • Never add news, indicator, or financial-statement tools to FALLBACK_ALLOWED — data contracts differ across vendors. (ADR 011)
  • Functions inside route_to_vendor must RAISE on failure, not embed errors in return values. (interface.py)
  • Catch (AlphaVantageError, FinnhubError, ConnectionError, TimeoutError), not just RateLimitError. (interface.py)
  • Exception chaining required: raise RuntimeError(...) from last_error. (ADR 011)
  • 2-level routing: category-level (data_vendors config dict) + tool-level override (tool_vendors config dict). (interface.py)

yfinance Gotchas

  • top_companies has ticker as the DataFrame INDEX, not a column. Access via .index, not a column name. (ADR 003)
  • Sector.overview has NO performance data. Use ETF proxies (SPDR sector ETFs) for sector performance. (ADR 003)
  • Always use .head(10) for both download and display in industry performance. (ADR 009)

LangGraph State

  • Any state field written by parallel nodes MUST have a reducer (Annotated[str, reducer_fn]). (ADR 005)
  • ScannerState uses _last_value reducer (keeps newest value) for all report fields. (scanner_states.py)
  • State classes: AgentState (trading), InvestDebateState (debate sub-state), RiskDebateState (risk sub-state), ScannerState (scanner). (agent_states.py, scanner_states.py)

Threading & Rate Limiting

  • Never hold a lock during sleep() or IO. Pattern: release lock, sleep outside, re-acquire. (ADR 007)
  • Alpha Vantage: 75 calls/min (premium). (alpha_vantage_common.py)
  • Finnhub: 60 calls/min (free tier). (finnhub_common.py)
  • Finnhub paid-tier endpoints (/stock/candle, /financials-reported, /indicator) must never be called on free key. (ADR 010)

Ollama

  • Never hardcode localhost:11434. Use configured base_url from config. (ADR 001)

CLI Patterns

  • Typer for command definitions, Rich for live UI. (cli/main.py)
  • MessageBuffer — deque-based singleton tracking agent statuses, reports, tool calls. Fixed agents grouped by team (FIXED_AGENTS), analysts selectable. (cli/main.py)
  • StatsCallbackHandler — token and timing statistics for display. (cli/stats_handler.py)
  • All reports go under reports/daily/{date}/ — use helpers from report_paths.py: get_market_dir(date) for scan results, get_ticker_dir(date, ticker) for per-ticker analysis, get_eval_dir(date, ticker) for eval logs. Never hardcode report paths. (report_paths.py)

Pipeline Patterns

  • MacroBridge is the facade class for scan → filter → per-ticker analysis. (pipeline/macro_bridge.py)
  • ConvictionLevel = Literal["high", "medium", "low"]; CONVICTION_RANK = {"high": 3, "medium": 2, "low": 1}. (macro_bridge.py)
  • extract_json() handles DeepSeek R1 <think> blocks, markdown fences, and raw JSON. (json_utils.py)

Testing

  • Run tests: conda activate tradingagents && pytest tests/ -v
  • Skip integration tests: pytest tests/ -v -m "not integration"
  • Skip paid-tier tests: pytest tests/ -v -m "not paid_tier"
  • Mocking vendor methods: patch VENDOR_METHODS dict entries directly (it stores function refs), not module attributes. (interface.py)
  • Env isolation: always mock env vars before importlib.reload()load_dotenv() leaks real .env values otherwise.
  • callable() returns False on LangChain @tool objects — use hasattr(x, "invoke") instead.

Observability

  • Create one RunLogger per CLI command (analyze/scan/pipeline). Attach logger.callback to LLM constructors. (observability.py)
  • Call set_run_logger(logger) at run start so vendor/tool layers can access it via get_run_logger(). (observability.py)
  • Vendor calls: log_vendor_call(method, vendor, success, duration_ms) — called inside route_to_vendor. (observability.py, interface.py)
  • Tool calls: log_tool_call(tool_name, args_summary, success, duration_ms) — called inside run_tool_loop. (observability.py, tool_runner.py)
  • Write the run log at the end of each command: logger.write_log(report_dir / "run_log.jsonl"). (observability.py)

Error Handling

  • Fail-fast by default — no silent fallback unless method is in FALLBACK_ALLOWED. (ADR 011)
  • Alpha Vantage hierarchy: AlphaVantageErrorAPIKeyInvalidError, RateLimitError, ThirdPartyError, ThirdPartyTimeoutError, ThirdPartyParseError. (alpha_vantage_common.py)
  • Finnhub hierarchy: FinnhubErrorAPIKeyInvalidError, RateLimitError, ThirdPartyError, ThirdPartyTimeoutError, ThirdPartyParseError. (finnhub_common.py)