6.4 KiB
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_providerand{tier}_backend_url, falling back to top-levelllm_providerandbackend_url. (default_config.py) load_dotenv()runs at module level indefault_config.py— import-order-independent. Check actual env var values when debugging auth. (default_config.py)llm_providerandbackend_urlmust always exist at top level —scanner_graph.pyandtrading_graph.pyuse them as fallbacks. (ADR 006)mid_think_llmdefaults toNone, meaning mid-tier falls back toquick_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 — eitherToolNodein graph orrun_tool_loop()inline. (ADR 004)
Tool Execution
- Trading graph: analysts use
ToolNodein the LangGraph graph with conditional routing (should_continue_X). (graph/setup.py) - Scanner agents: use
run_tool_loop()inline — noToolNode, 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_ALLOWEDget cross-vendor fallback:get_stock_dataget_market_indicesget_sector_performanceget_market_moversget_industry_performance
- Never add news, indicator, or financial-statement tools to
FALLBACK_ALLOWED— data contracts differ across vendors. (ADR 011) - Functions inside
route_to_vendormust RAISE on failure, not embed errors in return values. (interface.py) - Catch
(AlphaVantageError, FinnhubError, ConnectionError, TimeoutError), not justRateLimitError. (interface.py) - Exception chaining required:
raise RuntimeError(...) from last_error. (ADR 011) - 2-level routing: category-level (
data_vendorsconfig dict) + tool-level override (tool_vendorsconfig dict). (interface.py)
yfinance Gotchas
top_companieshas ticker as the DataFrame INDEX, not a column. Access via.index, not a column name. (ADR 003)Sector.overviewhas 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) ScannerStateuses_last_valuereducer (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 configuredbase_urlfrom 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 fromreport_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
MacroBridgeis 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_METHODSdict entries directly (it stores function refs), not module attributes. (interface.py) - Env isolation: always mock env vars before
importlib.reload()—load_dotenv()leaks real.envvalues otherwise. callable()returns False on LangChain@toolobjects — usehasattr(x, "invoke")instead.
Observability
- Create one
RunLoggerper CLI command (analyze/scan/pipeline). Attachlogger.callbackto LLM constructors. (observability.py) - Call
set_run_logger(logger)at run start so vendor/tool layers can access it viaget_run_logger(). (observability.py) - Vendor calls:
log_vendor_call(method, vendor, success, duration_ms)— called insideroute_to_vendor. (observability.py,interface.py) - Tool calls:
log_tool_call(tool_name, args_summary, success, duration_ms)— called insiderun_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:
AlphaVantageError→APIKeyInvalidError,RateLimitError,ThirdPartyError,ThirdPartyTimeoutError,ThirdPartyParseError. (alpha_vantage_common.py) - Finnhub hierarchy:
FinnhubError→APIKeyInvalidError,RateLimitError,ThirdPartyError,ThirdPartyTimeoutError,ThirdPartyParseError. (finnhub_common.py)