Complete reference for the tradingagents package: configuration, graph flow,
state, structured output, the vendor data contract, strategy overlays,
persistence, reporting, CLI, and scripts. Generated values are checked against
the code (config keys, tool/vendor tables). For environment and operational
quirks see docs/AGENT_ONBOARDING.md; for the daily workflow see
docs/howto_end_to_end.md.
Two ways to configure: DEFAULT_CONFIG in tradingagents/default_config.py,
overridden by any TRADINGAGENTS_* env var in the table below (value type is
coerced from the existing default; a TRADINGAGENTS_MAX_WORKERS example lives
in batch.py).
The table is generated from the code — _ENV_OVERRIDES plus the literal
os.getenv("TRADINGAGENTS_...", fallback) reads inside DEFAULT_CONFIG (the
union is the surface). tests/test_api_reference_env_table.py re-derives that
surface and asserts this table lists exactly it, keys each row correctly, uses
one cell count, and lists no env var twice — so "complete" is checked, not
claimed. Re-run py -3.12 scripts/gen_api_reference_table.py --write after
changing a config key; --check exits non-zero when the table is stale.
| Env var | Config key | Notes |
|---|---|---|
TRADINGAGENTS_LLM_PROVIDER |
llm_provider |
default openai |
TRADINGAGENTS_DEEP_THINK_LLM |
deep_think_llm |
default gpt-5.5 |
TRADINGAGENTS_QUICK_THINK_LLM |
quick_think_llm |
default gpt-5.4-mini |
TRADINGAGENTS_LLM_BACKEND_URL |
backend_url |
default None |
TRADINGAGENTS_OUTPUT_LANGUAGE |
output_language |
default English |
TRADINGAGENTS_MAX_DEBATE_ROUNDS |
max_debate_rounds |
default 1 |
TRADINGAGENTS_MAX_RISK_ROUNDS |
max_risk_discuss_rounds |
per-round override; wins over RESEARCH_DEPTH |
TRADINGAGENTS_RESEARCH_DEPTH |
research_depth |
ONE depth knob (1/3/5): drives BOTH research and risk debate rounds to the same level |
TRADINGAGENTS_ENABLE_DEBATE |
enable_debate |
structured multi-agent debate on (default off — legacy one-shot chain unchanged) |
TRADINGAGENTS_DEBATE_BULL_MODEL |
debate_bull_model |
per-role family:id models; empty = quick/deep fallback. BULL drives bull (research) + aggressive (risk); BEAR drives bear + conservative; JUDGE drives both blind judges; neutral = quick |
TRADINGAGENTS_DEBATE_BEAR_MODEL |
debate_bear_model |
default empty |
TRADINGAGENTS_DEBATE_JUDGE_MODEL |
debate_judge_model |
default empty |
TRADINGAGENTS_DEBATE_NEUTRAL_MODEL |
debate_neutral_model |
family:id for the neutral RISK debater; empty = quick fallback |
TRADINGAGENTS_DEBATE_JUDGE_ENSEMBLE |
debate_judge_ensemble |
default 1 |
TRADINGAGENTS_DEBATE_MAX_ROUNDS |
debate_max_rounds |
5 (DebaterTurnPayload round_index 1..5) |
TRADINGAGENTS_DEBATE_MAX_OUTPUT_TOKENS |
debate_max_output_tokens |
|
TRADINGAGENTS_DEBATE_TEMPERATURE |
debate_temperature |
default 0.1 |
TRADINGAGENTS_DEBATE_JSON_MODE |
debate_json_mode |
default true |
TRADINGAGENTS_DEBATE_MIN_GAIN |
debate_min_gain |
termination knobs |
TRADINGAGENTS_DEBATE_STOP_CONSECUTIVE |
debate_stop_consecutive |
default 2 |
TRADINGAGENTS_DEBATE_CONSENSUS_THRESH |
debate_consensus_thresh |
default 0.85 |
TRADINGAGENTS_DEBATE_SCORING_WEIGHTS |
debate_scoring_weights |
default {'evidence': 0.6, 'novelty': 0.25, 'constraint': 0.15} |
TRADINGAGENTS_DEBATE_ABSTAIN_ALLOWED |
debate_abstain_allowed |
default true |
TRADINGAGENTS_DEBATE_FAST_ABORT |
debate_fast_abort |
default true |
TRADINGAGENTS_DEBATE_REGEN_MAX |
debate_regen_max |
R1' bounded single-role regeneration budget (1) |
TRADINGAGENTS_DEBATE_DIVERGENCE_CAP_ROUNDS |
debate_divergence_cap_rounds |
default 1 |
TRADINGAGENTS_DEBATE_REWEIGHT_TO_BASELINE |
debate_reweight_to_baseline |
R2' base α toward baseline (0.5) |
TRADINGAGENTS_DEBATE_ENTRENCH_THRESH |
debate_entrench_thresh |
I_entrench above this -> penalty (0.8) |
TRADINGAGENTS_DEBATE_DIVERGENCE_MIN |
debate_divergence_min |
default 0.15 |
TRADINGAGENTS_DEBATE_BASELINE_FALLBACK |
debate_baseline_fallback |
R1': an unverifiable debate degrades to the baseline stances (true, default); false stops the run with DebateBaselineFallbackError |
TRADINGAGENTS_DEBATE_REQUIRE_CAPABILITY_MATRIX |
debate_require_capability_matrix |
R3 startup check: raises DebateCapabilityError for a debate role (assessed on its debate_*_model or the tier fallback) that cannot meet its floor (default false = warn only) |
TRADINGAGENTS_CHECKPOINT_ENABLED |
checkpoint_enabled |
default false |
TRADINGAGENTS_BENCHMARK_TICKER |
benchmark_ticker |
default None |
TRADINGAGENTS_TEMPERATURE |
temperature |
default None |
TRADINGAGENTS_TOP_P |
top_p |
default None |
TRADINGAGENTS_FREQUENCY_PENALTY |
frequency_penalty |
default None |
TRADINGAGENTS_PRESENCE_PENALTY |
presence_penalty |
default None |
TRADINGAGENTS_LLM_MAX_RETRIES |
llm_max_retries |
default None |
TRADINGAGENTS_MAX_TOKENS |
max_tokens |
default None; a hard cap on output tokens forwarded to every provider chat client (Gemini as max_output_tokens) |
TRADINGAGENTS_FINNHUB_API_KEY |
finnhub_api_key |
|
TRADINGAGENTS_FMP_API_KEY |
fmp_api_key |
|
TRADINGAGENTS_EODHD_API_KEY |
eodhd_api_key |
EODHD daily OHLCV (free 20 calls/day; EOD plan $19.99/mo = 100k calls/day @ 1000/min, 30+ years) — a replacement for the moomoo K-line quota (100 calls/7 days) |
TRADINGAGENTS_MASSIVE_API_KEY |
massive_api_key |
|
TIINGO_API_KEY |
tiingo_api_key |
Tiingo market data (free Starter tier: EOD OHLCV + fundamental statements + IEX quote + crypto; ~1,000 calls/day) |
TWELVEDATA_API_KEY |
twelve_data_api_key |
Twelve Data (free "Basic": 800 credits/day, 8/min; realtime US stocks/forex/crypto quotes + historical time-series OHLCV; tail of core_stock_apis) |
STOCKDATA_API_KEY |
stockdata_api_key |
StockData.org (free "$0/mo": 100 requests/day; quote/EOD/intraday/news; tail of core_stock_apis + news_data) |
NEWSAPI_API_KEY |
newsapi_api_key |
NewsAPI.org (free Developer: 100 req/day; global macro headlines; tail of news_data/get_global_news) |
BENZINGA_API_KEY |
benzinga_api_key |
Benzinga (live key registered 2026-09-20; ticker news = headline+link, plus calendars/fundamentals/insider/congress on the same key). GDELT is keyless. |
TRADINGAGENTS_PATENTSVIEW_API_KEY |
patentsview_api_key |
|
TRADINGAGENTS_ENABLE_MASSIVE_FLAT |
enable_massive_flat |
default false |
TRADINGAGENTS_MASSIVE_FLAT_DIR |
massive_flat_dir |
default data/massive_flat |
TRADINGAGENTS_TOOL_CALL_LOG_DIR |
tool_call_log_dir |
default empty |
TRADINGAGENTS_ALPACA_API_KEY_ID |
alpaca_api_key_id |
|
TRADINGAGENTS_ALPACA_API_SECRET |
alpaca_api_secret |
|
TRADINGAGENTS_ENABLE_ALPACA |
enable_alpaca |
default false |
TRADINGAGENTS_MOOMOO_HOST |
moomoo_host |
default 127.0.0.1 |
TRADINGAGENTS_MOOMOO_PORT |
moomoo_port |
default 11111 |
TRADINGAGENTS_MOOMOO_ACCOUNT |
moomoo_account |
default None |
TRADINGAGENTS_MOOMOO_AUTOSTART |
moomoo_autostart |
default false |
TRADINGAGENTS_MOOMOO_OPEND_PATH |
moomoo_opend_path |
default None |
TRADINGAGENTS_ENABLE_STRATEGY_OVERLAYS |
enable_strategy_overlays |
default true |
TRADINGAGENTS_ENABLE_REFLECTION |
enable_reflection |
default true |
TRADINGAGENTS_ENABLE_SENTIMENT |
enable_sentiment |
when on (default True), the sentiment report gets the computed StockTwits score + surprise velocity injected |
TRADINGAGENTS_ENABLE_ORDERFLOW |
enable_orderflow |
default false |
TRADINGAGENTS_ENABLE_POSITION_CONTRACT |
enable_position_contract |
default false |
TRADINGAGENTS_ENABLE_CALIBRATION |
enable_calibration |
default false |
TRADINGAGENTS_ENABLE_AGREEMENT |
enable_agreement |
consensus/agreement scaling of the size |
TRADINGAGENTS_ENABLE_COMPOSITE_RANK |
enable_composite_rank |
default false |
TRADINGAGENTS_ENABLE_FACTOR_PROFILE |
enable_factor_profile |
default false |
TRADINGAGENTS_ENABLE_TOPK_DROP |
enable_topk_drop |
default false |
TRADINGAGENTS_ENABLE_ENHANCED_INDEX |
enable_enhanced_index |
default false |
TRADINGAGENTS_BACKTEST_LIMIT_THRESHOLD |
backtest_limit_threshold |
default 0.0 |
TRADINGAGENTS_BACKTEST_VOLUME_PARTICIPATION |
backtest_volume_participation |
default 0.0 |
TRADINGAGENTS_BACKTEST_DEAL_PRICE |
backtest_deal_price |
default close |
TRADINGAGENTS_ENABLE_PIT_REGISTRY |
enable_pit_registry |
default false |
TRADINGAGENTS_ENABLE_FACTOR_MODEL |
enable_factor_model |
default false |
TRADINGAGENTS_ENABLE_DECISION_GUARDRAIL |
enable_decision_guardrail |
default false |
TRADINGAGENTS_ENABLE_SKILL_OVERLAYS |
enable_skill_overlays |
inert (docstring-only mentions) |
TRADINGAGENTS_SKILL_DIR |
skill_dir |
default empty |
TRADINGAGENTS_ENABLE_NEWS_RELEVANCE |
enable_news_relevance |
default false |
TRADINGAGENTS_ENABLE_REPORT_ATTRIBUTION |
enable_report_attribution |
default false |
TRADINGAGENTS_ENABLE_REPORT_INFLUENCE |
enable_report_influence |
default false |
TRADINGAGENTS_ENABLE_PREDICTION_LEDGER |
enable_prediction_ledger |
default false |
TRADINGAGENTS_PREDICTION_HORIZON_DAYS |
prediction_horizon_days |
default 60 |
TRADINGAGENTS_LLM_TIER_MAP |
llm_tier_map |
default {} |
TRADINGAGENTS_LLM_TIER_MODEL |
llm_tier_model |
default {} |
TRADINGAGENTS_MONITOR_NOTIFY |
monitor_notify |
default false |
TRADINGAGENTS_MONITOR_WEBHOOK |
monitor_webhook |
default empty |
TRADINGAGENTS_ENABLE_FACTOR_PROPOSAL_LOOP |
enable_factor_proposal_loop |
default false |
TRADINGAGENTS_ENABLE_TUNER |
enable_tuner |
default false |
TRADINGAGENTS_ENABLE_INDEPENDENT_VOTE |
enable_independent_vote |
when on, the 3 risk + bull/bear stances are sampled INDEPENDENTLY before the debate and the agreement/consensus (G3 + the PM's dissent flag + the G1 contract multiply) comes from those uncontaminated pre-debate opinions — the debate stays the risk-surfacing layer |
TRADINGAGENTS_ENABLE_SENTIMENT_FACTOR |
enable_sentiment_factor |
when on (default off), the position scale multiplies by 1 ± sentiment_factor_max_scale ONLY when the name's measured news-sentiment rank IC ≥ sentiment_factor_min_ic (else neutral 1.0, never blocks) |
TRADINGAGENTS_SENTIMENT_FACTOR_MIN_IC |
sentiment_factor_min_ic |
measured rank-IC floor for the sentiment fold (default 0.02) |
TRADINGAGENTS_SENTIMENT_FACTOR_MAX_SCALE |
sentiment_factor_max_scale |
max +/- position-scale move from the sentiment fold (default 0.2) |
TRADINGAGENTS_SENTIMENT_FACTOR_MIN_SCALE |
sentiment_factor_min_scale |
floor for the sentiment fold scale (default 0.5) |
TRADINGAGENTS_VOLATILITY_ESTIMATOR |
volatility_estimator |
overlay sizing estimator: close (default) \ |
TRADINGAGENTS_COVARIANCE_SHRINKAGE_ENABLE |
covariance_shrinkage_enable |
Ledoit-Wolf shrunk covariance in the covariance allocators (advisory, default off) |
TRADINGAGENTS_COVARIANCE_SHRINKAGE_TARGET |
covariance_shrinkage_target |
shrinkage target: scaled_identity (default) \ |
TRADINGAGENTS_ENABLE_KELLY_ALLOC |
enable_kelly_alloc |
allocation block uses multi-asset fractional Kelly (advisory, default off) |
TRADINGAGENTS_KELLY_FRACTION |
kelly_alloc_fraction |
fractional-Kelly scaling (default 0.25) |
TRADINGAGENTS_ENABLE_EXITS |
enable_exits |
default false |
TRADINGAGENTS_ENABLE_EXIT_ENGINE |
enable_exit_engine |
default false |
TRADINGAGENTS_ENABLE_RISK_GOVERNOR |
enable_risk_governor |
default false |
TRADINGAGENTS_ENABLE_DECISION_AUDIT |
enable_decision_audit |
on, the PM's report shows a claim-vs-computed audit note |
TRADINGAGENTS_REGIME_STATE_ENABLE |
regime_state_enable |
regime-state size multiplier |
TRADINGAGENTS_VOL_CAP_ENABLE |
vol_cap_enable |
volatility-cap size multiplier |
TRADINGAGENTS_ENABLE_HARD_GUARDS |
enable_hard_guards |
hard-guard labels (max_portfolio_risk / data_quality_failure / insufficient_liquidity) |
TRADINGAGENTS_ALPHA_LEDGER_ENABLE |
alpha_ledger_enable |
default false |
TRADINGAGENTS_KNIFE_COMPOSITE_ENABLE |
knife_composite_enable |
default false |
TRADINGAGENTS_KNIFE_SCORE_WEIGHTS |
knife_score_weights |
default [0.25, 0.2, 0.2, 0.2, 0.15] |
TRADINGAGENTS_KNIFE_SCORE_BANDS |
knife_score_bands |
default [1.5, 2.5, 3.0] |
TRADINGAGENTS_VALUE_DIP_KNIFE_ENABLE |
value_dip_knife_enable |
default false |
TRADINGAGENTS_VALUE_DIP_VDU_ENABLE |
value_dip_vdu_enable |
strict: VDU ladder hard-gates entry (dry-up + trigger candle + RVOL>=1.3 + momentum confirmation); candidate=False -> no entry |
TRADINGAGENTS_VALUE_DIP_REQUIRE_CATALYST |
value_dip_require_catalyst |
strict: value-dip needs re-rating evidence |
TRADINGAGENTS_VALUE_DIP_REGIME_GATE |
value_dip_regime_gate |
strict: block dip entries in high-vol/fast-downtrend/catalyst windows |
TRADINGAGENTS_VALUE_DIP_REGIME_VOL_CAP |
value_dip_regime_vol_cap |
vol_pct above this blocks MR entries (gate ON) |
TRADINGAGENTS_VALUE_DIP_REGIME_DOWNTREND_BAND |
value_dip_regime_downtrend_band |
knife guard: price below 200-SMA by this |
TRADINGAGENTS_VALUE_DIP_REGIME_HALVE |
value_dip_regime_halve |
instead of block, size x0.5 |
TRADINGAGENTS_RISK_DAILY_LOSS_BUDGET_PCT |
risk_daily_loss_budget_pct |
daily realized-loss cap -> de-risk |
TRADINGAGENTS_RISK_HWM_SOFT_PCT |
risk_hwm_soft_pct |
drawdown-from-HWM tiers |
TRADINGAGENTS_RISK_HWM_HARD_PCT |
risk_hwm_hard_pct |
default 0.2 |
TRADINGAGENTS_BREAKEVEN_TRIGGER |
breakeven_trigger |
atr \ |
TRADINGAGENTS_STOP_NEVER_WIDEN |
stop_never_widen |
unified stop never widened |
TRADINGAGENTS_MIN_HOLDING_DAYS |
min_holding_days |
turnover guards |
TRADINGAGENTS_MAX_TRADES_PER_PERIOD |
max_trades_per_period |
turnover guards |
TRADINGAGENTS_SLEEVE_TAG_ENABLED |
sleeve_tag_enabled |
default true |
TRADINGAGENTS_DRIFT_THRESHOLD |
drift_threshold |
alpha-decay win-rate drift trigger |
TRADINGAGENTS_ENABLE_PREOPEN_RVOL |
enable_preopen_rvol |
P1: pre-market RVOL vs 30d pre-open avg (Alpaca free IEX) |
TRADINGAGENTS_PREOPEN_RVOL_INSTITUTIONAL_X |
preopen_rvol_institutional_x |
P1: pre-market RVOL vs 30d pre-open avg (Alpaca free IEX) |
TRADINGAGENTS_PSR_BENCHMARK_SHARPE |
psr_benchmark_sharpe |
PSR benchmark Sharpe (default 0.0) |
TRADINGAGENTS_ROLLING_WINDOW |
rolling_window |
rolling evaluation window (default 132) |
TRADINGAGENTS_DOWNSIDE_MAR |
downside_mar |
minimum acceptable return for downside measures (default 0.0) |
TRADINGAGENTS_TRAILING_STOP_PCT |
trailing_stop_pct |
peak-to-exit trail % for trailing exits (default 0.05) |
TRADINGAGENTS_ENABLE_TRAILING_EXIT |
enable_trailing_exit |
peak-trailing / give-back exits on (default OFF) |
TRADINGAGENTS_RISK_PARITY_ENABLED |
risk_parity_enabled |
risk-parity / min-variance allocation on (default OFF) |
TRADINGAGENTS_RISK_MANAGER_DRAWDOWN_PCT |
risk_manager_drawdown_pct |
risk-manager drawdown trigger (default 0.05) |
TRADINGAGENTS_ENABLE_RISK_MANAGER |
enable_risk_manager |
two-pass risk manager on (default OFF; not wired to runtime) |
TRADINGAGENTS_VOLUME_SHARE_VOL_LIMIT |
volume_share_vol_limit |
volume-share slippage volume limit (default 0.1) |
TRADINGAGENTS_VOLUME_SHARE_PRICE_IMPACT |
volume_share_price_impact |
volume-share slippage price impact (default 0.025) |
TRADINGAGENTS_ENABLE_EVENTS |
enable_events |
default true |
TRADINGAGENTS_CATALYST_WINDOW_DAYS |
catalyst_window_days |
default 5 |
TRADINGAGENTS_CATALYST_BASELINE_MOVE |
catalyst_baseline_move |
default 0.02 |
TRADINGAGENTS_CATALYST_MACRO_WINDOW_DAYS |
catalyst_macro_window_days |
default 3 |
TRADINGAGENTS_CATALYST_MACRO_SCALE |
catalyst_macro_scale |
default 0.6 |
TRADINGAGENTS_CATALYST_FED_WINDOW_DAYS |
catalyst_fed_window_days |
default 10 |
TRADINGAGENTS_CATALYST_FED_SCALE |
catalyst_fed_scale |
default 0.6 |
TRADINGAGENTS_CATALYST_MISS_SCALE |
catalyst_miss_scale |
default 0.5 |
TRADINGAGENTS_CATALYST_SCALE_FLOOR |
catalyst_scale_floor |
default 0.25 |
TRADINGAGENTS_CATALYST_HARD_BLOCK_DAYS |
catalyst_hard_block_days |
# default 5 (forward earnings blackout) |
TRADINGAGENTS_MARKET_STRESS_INDEX |
market_stress_index |
default empty |
TRADINGAGENTS_MARKET_STRESS_VOL_CAP |
market_stress_vol_cap |
default 0.85 |
TRADINGAGENTS_MIN_DOLLAR_VOLUME |
min_dollar_volume |
default None |
TRADINGAGENTS_MAX_SPREAD_BPS |
max_spread_bps |
default None |
TRADINGAGENTS_RISK_MAX_DRAWDOWN_PCT |
risk_max_drawdown_pct |
default 0.1 |
TRADINGAGENTS_RISK_STANDALONE_CVAR_LIMIT_PCT |
risk_standalone_cvar_limit_pct |
the analyzed name's own daily-tail CVaR limit; default 0.03 |
TRADINGAGENTS_RISK_PORTFOLIO_CVAR_BUDGET_PCT |
risk_portfolio_cvar_budget_pct |
the portfolio (book) daily-tail CVaR budget; default 0.03 |
TRADINGAGENTS_RISK_CVAR_MIN_OBSERVATIONS |
risk_cvar_min_observations |
the observation floor a CVaR leg must clear before it publishes; default 250 |
TRADINGAGENTS_RISK_BASKET_TICKERS |
risk_basket_tickers |
default [] |
TRADINGAGENTS_RISK_BASKET_WEIGHTS |
risk_basket_weights |
default {} |
TRADINGAGENTS_HOLDINGS_TICKERS |
holdings_tickers |
Option B: actual-book tickers for the PM holdings read; empty = the risk basket is used (Option A) |
TRADINGAGENTS_HOLDINGS_WEIGHTS |
holdings_weights |
Option B: actual-book weights (fractions of the whole book incl. cash; <1.0 remainder = cash sleeve). See scripts/positions_to_basket.py to derive them from position CSVs |
TRADINGAGENTS_MOOMOO_MAX_CONNECTIONS |
moomoo_max_connections |
default 25 |
TRADINGAGENTS_MOOMOO_CALL_TIMEOUT |
moomoo_call_timeout |
per-call wall-clock timeout (s) for moomoo SDK calls; the SDK's own ReqInfo.wait() allows 20s, this caps a degraded gateway at 5s (default) so a run can't stall on hundreds of slow calls |
TRADINGAGENTS_MOOMOO_SCREEN_ENABLE |
moomoo_screen_enable |
default false |
TRADINGAGENTS_MOOMOO_SCREEN_PE_MAX |
moomoo_screen_pe_max |
default 30.0 |
TRADINGAGENTS_MOOMOO_SCREEN_ROE_MIN |
moomoo_screen_roe_min |
default 0.12 |
TRADINGAGENTS_MOOMOO_SCREEN_MAX_CHG5D |
moomoo_screen_max_chg5d |
default -0.05 |
TRADINGAGENTS_MOOMOO_SCREEN_MAX_RSI |
moomoo_screen_max_rsi |
default 35.0 |
TRADINGAGENTS_MOOMOO_SCREEN_PB_MIN |
moomoo_screen_pb_min |
default None |
TRADINGAGENTS_MOOMOO_SCREEN_PB_MAX |
moomoo_screen_pb_max |
default None |
TRADINGAGENTS_MOOMOO_SCREEN_DIP_DAYS |
moomoo_screen_dip_days |
default 5 |
TRADINGAGENTS_MOOMOO_SCREEN_EXCHANGES |
moomoo_screen_exchanges |
default NYSE,NASDAQ |
TRADINGAGENTS_RISK_COMPACT_REPORT |
risk_compact_report |
default false |
TRADINGAGENTS_GOOGLE_THINKING_LEVEL |
google_thinking_level |
default None |
TRADINGAGENTS_OPENAI_REASONING_EFFORT |
openai_reasoning_effort |
default None |
TRADINGAGENTS_ANTHROPIC_EFFORT |
anthropic_effort |
default None |
TRADINGAGENTS_ANALYST_CONCURRENCY |
analyst_concurrency |
default 1 |
TRADINGAGENTS_ANALYST_FORCED_TOOLS |
analyst_forced_tools |
default [] |
TRADINGAGENTS_ANALYST_FORCED_TOOLS_MAX_PARALLEL |
analyst_forced_tools_max_parallel |
default 1 |
TRADINGAGENTS_ANALYST_FORCED_TOOLS_TIMEOUT_S |
analyst_forced_tools_timeout_s |
default 30 |
TRADINGAGENTS_ANALYST_FORCED_TOOLS_SUMMARY_WINDOW |
analyst_forced_tools_summary_window |
default 12000 |
TRADINGAGENTS_ANALYST_TOOLS_MODEL_SUPPLIED |
analyst_tools_model_supplied |
default [] |
TRADINGAGENTS_OPENROUTER_REASONING_EFFORT |
openrouter_reasoning_effort |
default empty |
TRADINGAGENTS_OPENROUTER_IGNORE_PROVIDERS |
openrouter_ignore_providers |
comma-separated provider slugs to always skip (sent as provider.ignore in the OpenRouter request body via extra_body) to block slow/unreliable endpoints; empty = no restriction |
TRADINGAGENTS_OPENROUTER_STREAMING |
openrouter_streaming |
stream OpenRouter responses; default false. A large prompt spends a long time in prefill with nothing on the wire, and OpenRouter only commits HTTP 200 + headers once a provider accepts the request (later failures arrive in-stream, not as a bare 504). langchain aggregates the chunks, so invoke()/tool loops are unchanged |
TRADINGAGENTS_OPENROUTER_SESSION_ID |
openrouter_session_id |
sticky-routing key sent as the request-body session_id: auto = one generated id per client (per symbol run), a literal string (≤256 chars) pins a named session, empty (default) = off. Keeps each turn on the provider holding the warmed prefix cache; OpenRouter's sticky routing is disabled by provider.order, so this repo never sends one |
TRADINGAGENTS_OPENROUTER_PROVIDER_SORT |
openrouter_provider_sort |
provider.sort: price \ |
TRADINGAGENTS_OPENROUTER_PREFERRED_MAX_LATENCY |
openrouter_preferred_max_latency |
provider.preferred_max_latency in seconds; empty/0 = not sent |
TRADINGAGENTS_OPENROUTER_PREFERRED_MIN_THROUGHPUT |
openrouter_preferred_min_throughput |
provider.preferred_min_throughput in tokens/sec; empty/0 = not sent |
TRADINGAGENTS_OPENROUTER_REQUIRE_PARAMETERS |
openrouter_require_parameters |
true = only hosts that honor every parameter in the request (tools, max_tokens) |
TRADINGAGENTS_OPENROUTER_ALLOW_FALLBACKS |
openrouter_allow_fallbacks |
default true (OpenRouter's own default, so nothing is sent); false = fail instead of falling back when the preferred host is unavailable |
TRADINGAGENTS_MAX_OUTPUT_TOKENS |
max_output_tokens |
per-role max output tokens (hard ceiling via max_tokens); default 8000; the fallback for both tiers |
TRADINGAGENTS_MAX_OUTPUT_TOKENS_QUICK |
max_output_tokens_quick |
quick-tier cap (analysts / researchers / debaters / trader); default 8000 (raised from 6000 after 2026-08-27 reports truncated mid-sentence at the 6000 cap) |
TRADINGAGENTS_MAX_OUTPUT_TOKENS_DEEP |
max_output_tokens_deep |
deep-tier cap (Research Manager + Portfolio Manager); default 2500 |
TRADINGAGENTS_BACKUP_LLM |
backup_llm |
backup model for truncation-continuation retries: provider:model (or a bare model id using TRADINGAGENTS_LLM_PROVIDER). When ANY LLM response is cut at the output cap, the continuation retry runs on this model instead of re-paying the one that truncated. Empty = same-model continuations (legacy behavior) |
TRADINGAGENTS_ENABLE_VALUE_DIP |
enable_value_dip |
default false |
TRADINGAGENTS_ENABLE_TRANCHE_RISK |
enable_tranche_risk |
default false |
TRADINGAGENTS_TRANCHE_WEIGHTS |
tranche_weights |
default [0.3, 0.3, 0.4] |
TRADINGAGENTS_TRANCHE_STOP_MULT |
tranche_stop_mult |
default 1.5 |
TRADINGAGENTS_TRANCHE_RISK_PCT |
tranche_risk_pct |
default 0.015 |
TRADINGAGENTS_TRANCHE_ACCOUNT |
tranche_account |
default 100000.0 |
TRADINGAGENTS_MIN_RR |
min_rr |
default 2.0 |
TRADINGAGENTS_MAX_STOP_FRACTION |
max_stop_fraction |
default 0.10 |
TRADINGAGENTS_ENABLE_LIQUIDITY_GATE |
enable_liquidity_gate |
on, the risk governor sizes against the ILLIQ/float-turnover/IWF liquidity verdict (Strategies/risk2.md) |
TRADINGAGENTS_ENABLE_COMPUTED_CONTEXT |
enable_computed_context |
default true |
TRADINGAGENTS_ENABLE_PRE_MARKET_REVIEW |
enable_pre_market_review |
default false |
TRADINGAGENTS_ENABLE_JV_VERDICT |
enable_jev_verdict |
default false |
TRADINGAGENTS_ENABLE_MSDEC_DECIDER |
enable_msdec_decider |
default false (the Microsoft microsoft/microsoft-decision-1 judge; replaced enable_pplx_decider on 2026-10-11) |
TRADINGAGENTS_ENABLE_NOUL_DECIDER |
enable_noul_decider |
default false |
TRADINGAGENTS_ENABLE_LUNA_DECIDER |
enable_luna_decider |
default false |
TRADINGAGENTS_FCF_YIELD_FLOOR |
fcf_yield_floor |
default 0.06 |
TRADINGAGENTS_ENABLE_ETF_ENGINE |
enable_etf_engine |
default false |
TRADINGAGENTS_ENABLE_CORRELATION_PENALTY |
enable_correlation_penalty |
when on, the allocation plan (allocation_block / get_allocation) down-weights names whose average pairwise correlation with the rest of the book exceeds correlation_threshold (risk-parity concentration control) |
TRADINGAGENTS_CORRELATION_THRESHOLD |
correlation_threshold |
avg pairwise correlation above this triggers the penalty (default 0.6) |
TRADINGAGENTS_MAX_PAIRWISE_CORR |
max_pairwise_corr |
hard cluster ceiling on pairwise position correlation (default off) |
TRADINGAGENTS_TRAILING_STOP_ATR_MULT |
trailing_stop_atr_mult |
ATR-multiplied trailing stop (atr*mult) instead of static %% (default off) |
TRADINGAGENTS_CORRELATION_PENALTY_FRAC |
correlation_penalty_frac |
weight cut for a penalized name, renormalized across the book (default 0.3) |
TRADINGAGENTS_ENABLE_OPTIONS_SURFACE |
enable_options_surface |
CBOE delayed options chain -> IV/greeks surface (default OFF) |
TRADINGAGENTS_ENABLE_RISK_FREE_CURVE |
enable_risk_free_curve |
NY Fed SOFR + Treasury par-yield curve (default OFF) |
TRADINGAGENTS_ENABLE_SCREENER |
enable_screener |
yfinance universe screener (symbol/PE/EPS/beta/mkt-cap) (default OFF) |
TRADINGAGENTS_ENABLE_MARKET_MOVERS |
enable_market_movers |
yfinance gainers/losers/actives (default OFF) |
TRADINGAGENTS_ENABLE_BENZINGA_SURFACE |
enable_benzinga_surface |
Benzinga event surface: guidance revisions / FDA milestones / secondary offerings / analyst actions / news retractions (default OFF) |
TRADINGAGENTS_ENABLE_MARKET_ROUTING |
enable_market_routing |
DSA §3.4 market-classified routing on (default OFF; the chain is bit-identical when off) |
TRADINGAGENTS_MARKET_SOURCE_PRIORITY |
market_source_priority |
JSON market -> comma-separated vendor list, e.g. {"US": "eodhd,tiingo,yfinance,moomoo"}; reorders that method's chain for the market (takes effect with enable_market_routing; unregistered vendors in the list are skipped) |
TRADINGAGENTS_ENABLE_SPREAD_ESTIMATOR |
enable_spread_estimator |
quote-free Corwin-Schultz / Abdi-Ranaldo spread FLOOR (Q1) |
TRADINGAGENTS_ENABLE_BOOK_RISK_SIZING |
enable_book_risk_sizing |
minimum-CVaR book sizing under the CVaR budget (Q2) |
TRADINGAGENTS_ENABLE_CONFORMAL_BANDS |
enable_conformal_bands |
calibrated valuation bands with realized coverage (Q3) |
TRADINGAGENTS_ENABLE_RETURN_DECOMPOSITION |
enable_return_decomposition |
overnight vs intraday return decomposition (Q5) |
TRADINGAGENTS_ENABLE_TEXT_FACTORS |
enable_text_factors |
Loughran-McDonald tone / readability / divergence (Q6) |
TRADINGAGENTS_ENABLE_BOCPD |
enable_bocpd |
Bayesian online changepoint read in get_shift_detection (Q8) |
TRADINGAGENTS_BOCPD_HAZARD_MODE |
bocpd_hazard_mode |
default constant |
TRADINGAGENTS_ENABLE_RN_SKEW_PROXY |
enable_rn_skew_proxy |
default false |
TRADINGAGENTS_ENABLE_TREND_SPECTRAL |
enable_trend_spectral |
default false |
TRADINGAGENTS_ENABLE_TRIAL_LEDGER |
enable_trial_ledger |
default false |
TRADINGAGENTS_ENABLE_JUMP_ROBUST_PROXIES |
enable_jump_robust_proxies |
default false |
TRADINGAGENTS_ENABLE_MP_LOWER_SPECTRUM |
enable_mp_lower_spectrum |
default false |
TRADINGAGENTS_ENABLE_LONG_MEMORY |
enable_long_memory |
default false |
TRADINGAGENTS_ENABLE_METRIC_AUTHORITY |
enable_metric_authority |
default false |
TRADINGAGENTS_ENABLE_FACT_LAYER |
enable_fact_layer |
default false |
TRADINGAGENTS_ENABLE_BOOTSTRAP_INTERVALS |
enable_bootstrap_intervals |
default false |
TRADINGAGENTS_ENABLE_EVENT_IV_LIFT |
enable_event_iv_lift |
default false |
TRADINGAGENTS_ENABLE_TRIADIC_STRESS |
enable_triadic_stress |
default false |
TRADINGAGENTS_ENABLE_REFUSAL_LEDGER |
enable_refusal_ledger |
default false |
TRADINGAGENTS_ENABLE_MATERIALITY_VERDICT |
enable_materiality_verdict |
default false |
TRADINGAGENTS_ENABLE_ACCURACY_CEILING |
enable_accuracy_ceiling |
default false |
TRADINGAGENTS_ENABLE_SPECTRAL_NULL_BAND |
enable_spectral_null_band |
default false |
TRADINGAGENTS_ENABLE_RND_RECOVERY |
enable_rnd_recovery |
default false |
TRADINGAGENTS_ENABLE_EIGEN_ROTATION |
enable_eigen_rotation |
default false |
TRADINGAGENTS_ENABLE_DRAWDOWN_ENVELOPE |
enable_drawdown_envelope |
default false |
TRADINGAGENTS_ENABLE_HMM_HEAVY_TAILS |
enable_hmm_heavy_tails |
default false |
TRADINGAGENTS_ENABLE_TAIL_RISK_LAYER |
enable_tail_risk_layer |
default false |
TRADINGAGENTS_ENABLE_TURBULENCE_READ |
enable_turbulence_read |
default false |
TRADINGAGENTS_ENABLE_FACTOR_AVAILABILITY_GATE |
enable_factor_availability_gate |
default false |
TRADINGAGENTS_ENABLE_FORWARD_STRESS_PROBABILITY |
enable_forward_stress_probability |
default false |
TRADINGAGENTS_ENABLE_PROMPT_CONDITION_HARNESS |
enable_prompt_condition_harness |
default false |
TRADINGAGENTS_ENABLE_COVERAGE_WINDOW |
enable_coverage_window |
default false |
TRADINGAGENTS_ENABLE_ALTMAN_VARIANTS |
enable_altman_variants |
default false |
TRADINGAGENTS_ENABLE_F_SCORE_DETAIL |
enable_f_score_detail |
default false |
TRADINGAGENTS_ENABLE_GROWTH_SCORES |
enable_growth_scores |
default false |
TRADINGAGENTS_ENABLE_WEIGHTED_SENTIMENT_AGG |
enable_weighted_sentiment_agg |
default false |
TRADINGAGENTS_ENABLE_CROWD_RATIO_BANDS |
enable_crowd_ratio_bands |
default false |
TRADINGAGENTS_ENABLE_ANALYST_REVISION_INDEX |
enable_analyst_revision_index |
default false |
TRADINGAGENTS_ENABLE_QUALITY_COMPOSITE |
enable_quality_composite |
default false |
TRADINGAGENTS_ENABLE_SCORE_EVAL_ROWS |
enable_score_eval_rows |
default false |
TRADINGAGENTS_ENABLE_WEIGHTED_SENTIMENT_WINDOW |
enable_weighted_sentiment_window |
default false |
TRADINGAGENTS_ENABLE_EVIDENCE_SYMMETRY |
enable_evidence_symmetry |
default false |
TRADINGAGENTS_ENABLE_FUNDAMENTAL_SCORE |
enable_fundamental_score |
default false |
TRADINGAGENTS_ENABLE_TECHNICAL_SCORE |
enable_technical_score |
default false |
TRADINGAGENTS_ENABLE_MOMENTUM_SCORE |
enable_momentum_score |
default false |
TRADINGAGENTS_ENABLE_SENTIMENT_SCORE |
enable_sentiment_score |
default false |
TRADINGAGENTS_ENABLE_NEWS_SCORE |
enable_news_score |
default false |
TRADINGAGENTS_ENABLE_TRADE_SCORE |
enable_trade_score |
default false |
TRADINGAGENTS_ENABLE_EVENT_STATE |
enable_event_state |
default false |
TRADINGAGENTS_ENABLE_EVENT_CALENDARS |
enable_event_calendars |
default false |
TRADINGAGENTS_ENABLE_MOOMOO_SNAPSHOT |
enable_moomoo_snapshot |
default false |
TRADINGAGENTS_ENABLE_ANALYST_ESTIMATES |
enable_analyst_estimates |
default false |
TRADINGAGENTS_ENABLE_EODHD_RATES |
enable_eodhd_rates |
default false |
TRADINGAGENTS_ENABLE_REGIME_SCORE |
enable_regime_score |
default false |
TRADINGAGENTS_ENABLE_RISK_SCORE |
enable_risk_score |
default false |
TRADINGAGENTS_ENABLE_QUANT_SCORECARD |
enable_quant_scorecard |
default false |
TRADINGAGENTS_ENABLE_DECISION_PACKET |
enable_decision_packet |
default false |
TRADINGAGENTS_ENABLE_CONTEXT_EXPANSION |
enable_context_expansion |
default false |
TRADINGAGENTS_ENABLE_DECISION_CHALLENGE |
enable_decision_challenge |
default false |
TRADINGAGENTS_EVIDENCE_SYMMETRY_PAIRS |
evidence_symmetry_pairs |
default [] |
TRADINGAGENTS_ENABLE_THRESHOLD_GATE |
enable_threshold_gate |
inert - declared, read by nothing (see docs/gate_registry.md §4) |
TRADINGAGENTS_ENABLE_REGIME |
enable_regime |
inert |
TRADINGAGENTS_ENABLE_FACTORS |
enable_factors |
inert |
TRADINGAGENTS_ENABLE_PREOPEN_DEPTH |
enable_preopen_depth |
P2: live IEX quote-depth thin-book proxy |
TRADINGAGENTS_ENABLE_SECTOR_MULTIFACTOR |
enable_sector_multifactor |
sector-ranking reads (multi-factor, industry group, breadth, vendor constituents) |
TRADINGAGENTS_ENABLE_SECTOR_INDUSTRY |
enable_sector_industry |
default false |
TRADINGAGENTS_ENABLE_SECTOR_BREADTH |
enable_sector_breadth |
default false |
TRADINGAGENTS_ENABLE_SECTOR_EODHD_CONSTITUENTS |
enable_sector_eodhd_constituents |
default false |
TRADINGAGENTS_ENABLE_MECHANICAL_VOLUME_DISCOUNT |
enable_mechanical_volume_discount |
default false |
TRADINGAGENTS_ENABLE_SECURITY_CONTEXT |
enable_security_context |
default false |
TRADINGAGENTS_RISK_AUDIT_ENABLED |
risk_audit_enabled |
hash-chained risk audit ledger |
TRADINGAGENTS_MAX_POSITION_PCT |
max_position_pct |
single-name position cap (governor) |
TRADINGAGENTS_RISK_MAX_POSITION_PCT |
risk_max_position_pct |
whole-book position cap (governor) |
TRADINGAGENTS_SECTOR_CAP_LIMIT |
sector_cap_limit |
sector concentration cap (governor) |
TRADINGAGENTS_RESULTS_DIR |
results_dir |
default computed: os.path.join(_TRADINGAGENTS_HOME, 'logs') |
TRADINGAGENTS_CACHE_DIR |
data_cache_dir |
default computed: os.path.join(_TRADINGAGENTS_HOME, 'cache') |
TRADINGAGENTS_LLM_FAILURE_JOURNAL_DIR |
llm_failure_journal_dir |
default empty |
TRADINGAGENTS_VERIFY_MODEL |
report_verify_model |
advisory LLM report-verifier model (empty = quick tier); see scripts/report_verify.py |
TRADINGAGENTS_VERIFY_MAX_CALLS |
report_verify_max_calls |
verifier LLM calls per report tree (default 12) |
TRADINGAGENTS_MEMORY_LOG_PATH |
memory_log_path |
default computed: os.path.join(_TRADINGAGENTS_HOME, 'memory', 'trading_memory.md') |
(Secrets are read from env inside the vendors; TRADINGAGENTS_DISABLE_REDDIT=1
in .env turns off Reddit fetches.)
DEFAULT_CONFIG is range-checked by default_config.validate_config() when it
is built, so a well-typed but out-of-range override (e.g.
TRADINGAGENTS_TOP_P=1.5, tranche weights that do not sum to ~1) raises a
ValueError listing every violation at config load instead of silently skewing
an unattended run.
Every gate is flippable from .env. docs/gate_registry.md is the canonical list: each gate
with its switch, what it can block, where it is enforced, the test that proves it fires, and the
flags that are declared but inert (read by no code). tests/test_gate_env_toggles.py keeps that
document true: it asserts each key exists and has an override row, that flipping it through the real
loader works in both directions, and that every wired/inert claim matches the source.
Run py -3.12 -c "from tradingagents.default_config import DEFAULT_CONFIG as D; print(sorted(D))"
for the canonical list; the important groups:
LLM - llm_provider, deep_think_llm, quick_think_llm, backend_url,
openai_reasoning_effort, google_thinking_level, anthropic_effort,
temperature, llm_max_retries.
Graph/run - max_debate_rounds, max_risk_discuss_rounds, max_recur_limit,
checkpoint_enabled, analyst_concurrency (1 = sequential; >1 = parallel
analysts), output_language.
Persistence - data_cache_dir (/.tradingagents/cache),
/.tradingagents/logs), results_dir (memory_log_path
(~/.tradingagents/memory/trading_memory.md), memory_log_max_entries (None =
unbounded rotation), benchmark_ticker + benchmark_map (per-exchange alpha
benchmarks: .NS->^NSEI, .BO->^BSESN, .T->^N225, .HK->^HSI, .L->^FTSE,
.TO->^GSPTSE, .AX->^AXJO, .SS->000001.SS, .SZ->399001.SZ, ''->SPY).
News/input limits - news_article_limit=20, global_news_article_limit=10,
global_news_lookback_days=7, global_news_queries (macro headlines).
Data vendors - data_vendors: dict (per-category chains), tool_vendors: dict
(per-tool overrides), enable_market_routing + market_source_priority
(opt-in DSA §3.4 per-market priority chains; see §6),
finnhub_api_key, fmp_api_key, alpaca_api_key_id,
alpaca_api_secret, enable_alpaca, moomoo_host/port/account/autostart/ opend_path/max_connections/call_timeout (moomoo_call_timeout = per-call
wall-clock timeout in seconds, default 5.0; the SDK's own ReqInfo.wait()
allows 20s per call, so a degraded gateway can burn 20s per call across
hundreds of calls — the value screener's gating pass makes ~7 calls/symbol —
which is how a web job hits its subprocess budget).
Vendor cache - vendor_cache_enabled=True, vendor_cache_ttl_seconds=21600,
vendor_cache_skip_categories={'news_data'}.
Strategy flags - see section 5; enable_regime, enable_factors,
enable_threshold_gate default False; enable_sentiment default True
(computed sentiment score + surprise velocity injected into the sentiment
report). Only enable_events (B1 catalyst gate), enable_reflection,
enable_sentiment, and enable_strategy_overlays default True;
enable_orderflow, enable_position_contract, enable_calibration,
enable_agreement, enable_independent_vote, enable_composite_rank,
enable_exits and enable_risk_governor default
False (opt-in;
the dev machine enables most via the gitignored .env). Sizing: position_sizing='kelly', target_vol=0.15,
kelly_fraction=0.25, position_odds=1.0, breakeven_atr=1.0,
target_atr=4.0, sector_cap_limit=0.35, risk_max_drawdown_pct=0.10,
risk_standalone_cvar_limit_pct=0.03, risk_portfolio_cvar_budget_pct=0.03,
risk_max_position_pct=0.45,
enable_tranche_risk (default False) + tranche_weights (0.3,0.3,0.4),
tranche_stop_mult (1.5), tranche_risk_pct (0.015), tranche_account (100000)
— the deterministic tranche-scaling risk fold (Value_Dip_swing_Continue.md):
with enable_position_contract + enable_risk_governor on, the governor sizes
and throttles against the worst-case 3-tranche scale-in with config-frozen
parameters (never the LLM), enforcing BOTH the capital-at-risk budget (sum of
per-tranche losses at the hard stop) and the peak-deployed-at-scale-in
per-trade cap;
risk_stress_shock_pct_1 / risk_stress_shock_pct_2 (R2 scenario shocks, def -10%/-30%)
evaluate_cost_bps=10, calibration_min_n=5, consensus_seeds=1,
max_book_names=10, max_name_weight=0.25, risk_audit_enabled default True.
Correlation-aware allocation (industry-practice item 1):
enable_correlation_penalty (default False) + correlation_threshold (0.6)
correlation_penalty_frac(0.3) — when on,allocation_blockand theget_allocationtool down-weight names whose average pairwise correlation with the rest of the book exceeds the threshold before the per-name/per-sector caps (risk-parity style concentration control; names without a measurable return series are never penalized). Catalyst:catalyst_hard_block_days=0- when > 0, an earnings print inside that many days makes the risk governor REJECT new risk (section 5).
Round-2 quant formula additions (docs/design_quant_formulas_research_round2.md,
phases Q1-Q8) - every flag defaults to False and its tool returns a DISABLED
sentinel when off, so a run's artefacts cannot change silently:
enable_spread_estimator (quote-free spread FLOOR folded into
get_liquidity_risk, plus the get_spread_estimate tool and the
spread-implied Almgren-Chriss temporary impact),
enable_book_risk_sizing (+ book_risk_sizing_min_scenarios=60,
book_risk_sizing_max_delta=0.05) for get_book_risk_budget,
enable_conformal_bands (+ conformal_alpha=0.1, conformal_min_pairs=40,
conformal_window=250) for get_valuation_band,
enable_return_decomposition for get_return_decomposition,
enable_text_factors for get_disclosure_tone, and enable_bocpd for the
Bayesian changepoint line inside get_shift_detection. The report verifier
gains two text-only families with no flag (they only fire on the cited lines):
tone_claim_conflict and valuation_band_conflict.
Round-3 scoring & sentiment additions
(docs/design_quant_formulas_research_round3.md,
docs/implementation_plan_quant_formula_additions_round3.md, phases S1-S11) -
ten flags, every one default False, every added line additive, and every
unavailable input rendered "unavailable" rather than scored 0:
enable_altman_variants (S1: altman_variant / altman_zone - the
Z'/Z''/Z''-EM family plus the distress zones, X4 basis printed, funds and
financials unavailable), enable_f_score_detail (S2:
piotroski_f_score_detailed - per-signal booleans, the paper's 0-1/8-9 bands,
and deviations recording every substitution), enable_growth_scores (S10:
growth_score G1-G8 + overpriced_score C1-C6, industry medians from the
resolved peer universe), enable_weighted_sentiment_agg (S4:
aggregate_weighted_sentiment beside the byte-identical
aggregate_daily_sentiment), enable_crowd_ratio_bands (S5: bull/bear ratio +
dispersion, display-only bands), enable_analyst_revision_index (S6:
analyst_revisions.revision_ratio and the get_analyst_revision_index tool),
enable_quality_composite (S3: factors.category_scores 0-100 with the
quality bands - never decision_guardrail.SCORE_BANDS),
enable_fundamental_score (WP-2: the four FundamentalScore category
sub-scores over the peer panel + their RESEARCH_ONLY composite; with it off the
tool is not in any toolset at all),
enable_technical_score (WP-3: the nine TechnicalScore category sub-scores
over the run's own bars + their weighted composite, with the non-monotonic inputs
band-mapped over their producers' own edges),
enable_regime_score (WP-4: the market-level RegimeScore + the two-path
disagreement read; NOT enable_regime, which is inert),
enable_risk_score (WP-5: the inverted 0-100 RiskScore, 100 = low risk, with
the three shipped sign conventions aligned in the score and printed per row),
enable_sentiment_score (WP-7: the ten SentimentScore category sub-scores + the
confirmation quadrant; two tone sources are refused rather than averaged),
enable_news_score (WP-6: NewsScore; the five unsupplied categories print NA
with a reason, and materiality comes from EventScore),
enable_event_state (WP-8: the per-family event imminence + the earnings hard
block passed through verbatim),
enable_trade_score (WP-11: the four-engine decision composite; it reaches no
gate, no size and no opportunity_score),
enable_score_eval_rows (S8: alpha_health.score_evaluation_rows),
enable_weighted_sentiment_window (S7: weighted_rolling_sentiment, the
10-day exponential window plus the min_history warm-up guard), and
enable_evidence_symmetry (S11: evidence_gather.symmetry_report, the
mirrored discretionary budget - driven by the declared pair specs in
evidence_symmetry_pairs / TRADINGAGENTS_EVIDENCE_SYMMETRY_PAIRS, inert while
that list is empty - and the gated argument-plan call). The peer
universe behind S3/S10 is the screener scan universe, resolved once by
strategies/peer_universe.py::resolve_peer_universe (plan §1.1).
deep_think_llm models the Research Manager + Portfolio Manager; quick the
analysts/researchers/debaters/reflection. Client code:
llm_clients/factory.py -> llm_clients/{openai,anthropic,google,azure, bedrock}_client.py + model_catalog.py (CLI model lists).
| Provider | Config value | Env key | Notes |
|---|---|---|---|
openai |
openai |
OPENAI_API_KEY |
Responses API; openai_reasoning_effort |
google |
google |
GOOGLE_API_KEY |
google_thinking_level |
anthropic |
anthropic |
ANTHROPIC_API_KEY |
anthropic_effort |
xai |
xai |
XAI_API_KEY |
|
deepseek |
deepseek |
DEEPSEEK_API_KEY |
reasoning_content round-trip |
mistral |
mistral |
MISTRAL_API_KEY |
|
kimi |
kimi |
MOONSHOT_API_KEY |
|
groq |
groq |
GROQ_API_KEY |
|
nvidia |
nvidia |
NVIDIA_API_KEY |
|
openrouter |
openrouter |
OPENROUTER_API_KEY |
default provider in .env |
qwen / qwen-cn |
qwen/qwen-cn |
DASHSCOPE_API_KEY / DASHSCOPE_CN_API_KEY |
intl / CN |
glm / glm-cn |
glm/glm-cn |
ZHIPU_API_KEY / ZHIPU_CN_API_KEY |
|
minimax / minimax-cn |
minimax/minimax-cn |
MINIMAX_API_KEY / MINIMAX_CN_API_KEY |
reasoning_split |
ollama |
ollama |
OLLAMA_BASE_URL |
keyless local |
openai_compatible |
openai_compatible |
TRADINGAGENTS_LLM_BACKEND_URL |
vLLM / LM Studio / llama.cpp |
azure |
azure |
Azure OpenAI env | see .env.enterprise.example |
bedrock |
bedrock |
AWS creds / AWS_BEARER_TOKEN_BEDROCK |
pip install ".[bedrock]" |
Request timeout. Every client request (incl. streaming) is bounded by a
300 s default: request_timeout for the OpenAI-compatible family,
default_request_timeout for Anthropic (the SDKs' real fields — the legacy
timeout passthrough key is mapped to them, and a config timeout /
request_timeout / default_request_timeout passed to create_llm_client
wins over the default). This prevents a stalled provider (e.g. DeepSeek at
US-night peak) from hanging a chain.invoke / the truncation-retry loop
indefinitely — a stuck job now raises and degrades instead of appearing to
re-try forever.
AgentState (LangGraph MessagesState): messages, company_of_interest, asset_type, instrument_context, trade_date, sender, market_report, sentiment_report, news_report, fundamentals_report, investment_debate_state, investment_plan, trader_investment_plan, risk_debate_state, final_trade_decision, past_context.
InvestDebateState:bull_history, bear_history, history, current_response, judge_decision, count.RiskDebateState:aggressive_history, conservative_history, neutral_history, history, latest_speaker, current_aggressive_response, current_conservative_response, current_neutral_response, judge_decision, count.
Pipeline (see graph/setup.py for wiring):
START -> analysts* (tool loop each) -> Bull/Bear debate (rounds) -> Research Manager
-> Trader -> Aggressive/Conservative/Neutral debate -> Portfolio Manager -> END
analyst_concurrency > 1wraps analysts in isolated per-analyst subgraphs running in threads.graph/checkpointer.py: per-ticker SQLite checkpoints keyed on ticker+date+graph-shape (analyst set, debate/risk rounds, asset type, concurrency).--checkpointenables; cleared on successful completion.graph/conditional_logic.py: routers bound to the message tail; the path maps insetup.pyare complete so a fall-through can never raise.
| Agent | Schema | Fields |
|---|---|---|
| Research Manager | ResearchPlan |
recommendation (5-tier), rationale, strategic_actions |
| Trader | TraderProposal |
action (Buy/Hold/Sell), reasoning, entry_price?, stop_loss?, target_price? (the §101 exit level — the card's own exit target, quoted, never invented), position_sizing? |
| Portfolio Manager | PortfolioDecision |
rating, executive_summary, investment_thesis, entry_price? (§100's final entry price from the card), price_target?, time_horizon?, confidence (0-1), position_size, stop_loss?, consensus (high/low) |
| Sentiment Analyst | SentimentReport |
overall_band (6-tier), overall_score (0-10), confidence (low/med/high), narrative (+ computed fields) |
Each agent falls back to free-text when the provider lacks structured output; rendered markdown preserves backward-compatible headers for the log/site.
Applied after the graph in graph/trading_graph.py::_apply_strategy_overlays:
| Layer | Flag (default) | Module | Effect |
|---|---|---|---|
| Regime / size | enable_strategy_overlays (T) |
strategies/regime.py, size.py |
vol-percentile/trend label + vol-target scale |
| Order flow | enable_orderflow (F) |
strategies/orderflow.py |
distribution/divergence fold -> scale |
| Catalyst (B1) | enable_events (T) |
strategies/catalyst.py |
earnings/macro/Fed scale + verdict; catalyst_hard_block_days > 0 turns an imminent print into a risk-governor REJECT |
| Position contract | enable_position_contract (F) |
strategies/contract.py |
min(Kelly, risk/stop)volflowagreecatalyst; the dollar stop/BE/target anchor on the entry the decision publishes (contract.published_entry → §103's final_entry_price, else a tranche plan's weighted avg_entry, else the last close), so the published stop never sits above position.entry_price |
| Risk governor | enable_risk_governor (F) |
strategies/risk_governor.py |
PASS/WARN/REJECT, risk_halt; CVaR from the configured risk_basket_tickers weighted mix (book_risk.portfolio_cvar) when set, else the analyzed name's series. If the weights sum < 1.0 the remainder is treated as zero-return cash (dilutes the tail) - "include cash as overall portfolio". With enable_tranche_risk on it also sizes/throttles against the worst-case 3-tranche scale-in (strategies/value_dip.py::tranche_risk_read): the peak-deployed-at-scale-in fraction vs the per-trade cap and the capital-at-risk budget (sum of per-tranche losses at the hard stop vs tranche_risk_pct) |
| Calibration | enable_calibration (F) |
strategies/calibration.py |
calibrated P from ledger |
| Agreement | enable_agreement (F) |
strategies/consensus.py |
debate agreement -> size; with enable_independent_vote the agreement comes from the INDEPENDENT pre-debate stances, not the debate transcript (no conformity contamination) |
| Sentiment factor | enable_sentiment_factor (F) |
strategies/sentiment_research.py + overlays.fold_sentiment_into_overlay |
position scale x 1 ± sentiment_factor_max_scale ONLY when the name's measured rank IC ≥ sentiment_factor_min_ic; else neutral 1.0 (never blocks) |
| Exits | enable_exits (F) |
strategies/exits.py |
stops/BE/targets |
| Reflection | enable_reflection (T) |
strategies/reflection.py |
ledger, analyst hit-rates |
| Composite rank | enable_composite_rank (F) |
strategies/factors.py |
EY + momentum + 52w composite |
| Factor profile | enable_factor_profile (F) |
strategies/factor_expressions.py |
get_factor_profile — Alpha158-style 16-factor subset (momentum/reversal/volatility/value) off the OHLCV cache with expression-string cache + learn/infer fit-apply split (moments fit on train only); advisory, gated |
| Topk-Drop alloc | enable_topk_drop (F) |
strategies/portfolio_strategy.py |
screener alloc block + get_topk_drop_plan — hold top-k by score, sell worst-held, equal-weight (turnover = 2·drop/topk) |
| Enhanced-index alloc | enable_enhanced_index (F) |
strategies/portfolio_strategy.py |
screener alloc block + get_enhanced_index_tilt — convex program (long-only, Σw=1, turnover cap, b_dev, force-hold/sell masks, two-stage fallback); scipy SLSQP + pure-python fallback, cvxpy optional |
| Signal analysis | — (always-on pure calc) | strategies/signal_analysis.py |
rank IC/ICIR, quantile long-short, IC-decay half-life, pred-autocorrelation; strategy_quality_report gains the with/without-cost excess-return table |
| Baseline surface | — (always-on pure calc) | strategies/benchmark_surface.py |
benchmark_rows / benchmark_surface (FinRL 8): every arm on ONE shared window and ONE stated cost schedule, the classic six columns beside mean / variance / loss-side CVaR / P(return>=threshold), plus each baseline's per-metric delta; a strategy-only call is a named refusal ("a raw single curve is not a result"). Cost is charged to the strategy arm and, by default, not to the baselines |
| Per-run economics | — (always-on pure calc) | strategies/run_economics.py |
run_economics(run_card, verify_flags=...) (Dream-RSI Phase B): the run card's cap-bound llm_cost_est joined to the verifier's own per-stem/prose blocks, with an unpriced leg named and never zeroed; depth is None because the run card does not record the research-depth knob |
| Cross-sectional null | — (always-on pure calc) | null_harness.cross_sectional_null |
H3 Stage 3 over REAL per-name PnL: a one-sample t across names (df = N-1) plus an exact two-sided sign test (ties dropped); distinguishable_from_zero / consistent_with_zero / unavailable, never a pass by absence |
| Selection turnover | — (always-on pure calc) | evaluate.selection_turnover |
E4: per-fold winner changes over a {fold: {candidate: score}} matrix, with the distinct-winner count and the modal share. Reports no informativeness verdict on purpose - a caller prints it beside cscv_pbo over the same matrix |
| Market stress (turbulence) | enable_turbulence_read (F) |
strategies/market_stress.py + get_turbulence_read (market analyst) |
FinRL's turbulence index: the day's cross-section vs its own strictly-prior window (the scored row is excluded), Sigma from covariance_models.ledoit_wolf_shrink, and an own-history quantile gate. A market-state WARNING - it sizes nothing, enters no score, and refuses a one-name panel |
| Skill overlays | enable_skill_overlays (F) + skill_dir |
strategies/skills.py + strategies/skills/*.yaml |
declarative YAML strategy skills (instructions/tools/regimes/priority + bounded score adjustments), regime-from-opinion thresholds (bullish>=70 / bearish<=30 / 35-65), router precedence user->regime->priority |
| Prediction ledger + cost | enable_prediction_ledger (F) + prediction_horizon_days |
strategies/prediction_ledger.py + llm_cost.py |
log every decision as a scorable prediction (rating/direction/levels/confidence/horizon); score against realized closes at N days (return, hit, MAE/MFE, stop/target); provider cost estimate for quality-per-dollar |
| Report disclosure + invalidation | enable_report_attribution (F) |
strategies/report_disclosure.py + reporting.write_report_tree |
computed driver attribution (sum-100), consensus support/oppose, watch_conditions/next_check_time, >=1 invalidation per decision (stop/tp/data-quality/manual fallback), sources-used-vs-empty + models footers |
| News relevance + coalescing | enable_news_relevance (F) |
strategies/news_relevance.py + dataflows/news_cache.py |
deterministic relevance scoring (code/company/official +8, macro -12, clamp), spam admission, degrade triple (all_failed/empty/unavailable), owner-wait coalescing TTL cache |
| Market routing + health | enable_market_routing + market_source_priority / vendor_breaker_* (F) |
dataflows/market_router.py + vendor_breaker.py + effective_date.py |
market-classified priority chains (opt-in TRADINGAGENTS_ENABLE_MARKET_ROUTING + TRADINGAGENTS_MARKET_SOURCE_PRIORITY), 3-fail/300s breaker + half-open probe + negative capability cache, VendorResult honesty fields (fallback_from/stale/data_quality/missing_fields), effective-trading-date + all-closed skip (fail-open). NOTE: the negative capability cache half is no longer opt-in — the vendor modules (fmp_common.fmp_get, massive._get, finnhub.get_analyst_ratings_finnhub) now consult it unconditionally, so a 401/403 verdict is not re-asked per call; the market-routing breaker itself stays default-off (see docs/developer/03-dataflow-vendors.md §3.1b) |
| Decision guardrail | enable_decision_guardrail (F) |
strategies/decision_guardrail.py |
post-PM downgrade-only stabilizer (risk-cap at Hold, near-resistance/no-flow, near-support/no-outflow) with recorded guardrail_reason; versioned 0-100 <-> rating consistency check; PM data_quality/guardrail_reason/risk_cap fields + confidence cap on stale data; per-field integrity retry in structured.retry_structured_missing_fields |
| Backtest tradability | backtest_limit_threshold / backtest_volume_participation / backtest_deal_price |
strategies/market_tradability.py |
limit-up/down gates, suspended days (NaN close), participation caps, deal-price selector; wired into scripts/backtest_strategy.py fills (flags --limit-threshold etc.) + fill_model honesty block |
Off by default: enable_regime, enable_factors,
enable_threshold_gate. enable_sentiment is now on (computed sentiment
score + surprise velocity injected into the sentiment report). Pure
strategies/risk_manager.py (manage_risk two-pass exit override +
trailing_stop_targets) also ships advisory: enable_risk_manager is OFF by
default and it is not wired into the runtime graph yet. Strategy-eval
scripts: scripts/evaluate_config_gate.py
(G5 walk-forward/PBO; --cost-grid sweeps round-trip costs, --benchmark adds the
baseline-relative surface, --folds adds selection turnover beside the PBO over the
same matrix), scripts/strategy_quality_report.py
(ledger metrics; the loss-side benchmark_surface block is always on, --benchmark
renders the benchmark's own arms, --trees joins each tree's cap-bound cost to its
verifier quality), scripts/orderflow_evaluate.py (ledger evaluation),
scripts/risk_report.py (risk audit).
Everything flows through route_to_vendor(method, *args, **kwargs) in
dataflows/interface.py. The vendor chain per category is configured in
data_vendors (default chains: eodhd,moomoo,yfinance, fred,moomoo,
polymarket,moomoo, moomoo,finnhub,yfinance, sec_edgar). Errors propagate through
dataflows/errors.py, the router converts to sentinel strings
(NO_DATA_AVAILABLE, DATA_UNAVAILABLE optional, DATA_DISABLED).
technical_indicators / fundamental_data / news_data end their chains with
alpha_vantage (key-gated last fallback via ALPHA_VANTAGE_API_KEY in .env).
Typed absence reasons (yfinance P1). When a chain ends on a typed
failure, route_to_vendor_typed attaches a machine-readable absence
dict to the VendorResult envelope: {reason, source, retryable, detail}
where reason ∈ no_data | rate_limited | not_configured | error | unknown.
The reason follows the verdict: no_data when the returned sentinel is
NO_DATA_AVAILABLE (from the no-data vendor), the first real error for
optional DATA_UNAVAILABLE. route_to_vendor (string path) keeps its plain
contract — the reason is carried via a per-call contextvar side channel read
(and reset) by route_to_vendor_typed, never in the returned string. The
OHLCV read envelope (analysis_tools._ohlcv) surfaces absence, the web
/api/ohlcv returns it on the no-history path, and run_card.json carries a
data_absence block (null on success paths). Direct string callers
(scripts/*) are unchanged.
yfinance pin (yfinance P5). yfinance~=1.4 (verified 1.4+); the
guarded quirks (#986 exclusive-end, #1021 stale frames, unnamed index,
statement newest-first, rate-limit retries in yf_retry) are documented in
dataflows/README.md with a version-bump checklist — a 1.x bump is a
deliberate step with a vendor-suite re-run.
core_stock_apis:get_stock_datatechnical_indicators:get_indicatorsfundamental_data:get_fundamentals,get_balance_sheet,get_cashflow,get_income_statement,get_basic_financials,get_company_peers,get_insider_activity,get_form4_insider,get_filing_sections,get_risk_factors,get_8k_filingsnews_data:get_news,get_global_news,get_insider_transactions,get_massive_newsnews_sentiment(optional):get_news_sentiment— daily series viaeodhd,alpha_vantage,gdelt(EODHD/sentimentsprimary)macro_data:get_macro_indicatorsprediction_markets:get_prediction_marketsanalyst_ratings:get_analyst_ratingsearnings_calendar:get_earnings_calendaroptions_data:get_options_chainsec_filings:get_sec_filingsshort_interest:get_short_interest,get_short_volume- computed-analysis tools (bound to the analyst tool loops, see 6.4):
get_swing_set,get_relative_strength,get_earnings_event_read,get_catalyst_scale,get_position_sizing,get_risk_gate,get_regime_read,get_volatility_contraction,get_orderflow_read,get_analyst_verdict,get_earnings_surprise,get_portfolio_weights,get_sector_rank,get_strategy_quality,get_margin_of_safety,get_composite_rank,get_tail_risk,get_credit_spread_read,get_session_discipline,get_earnings_quality,get_bollinger_pct_b,get_tranche_plan,get_trade_expectancy,get_fcf_yield,get_valuation_z_score,get_value_dip_setup,get_balance_sheet_health,get_macd_divergence,get_vdu_entry_setup,get_support_structure,get_decline_driver_check,get_extended_indicators(Ichimoku/CCI/ROC/momentum/TRIX/Force/A-D/VPT/CMF/ anchored VWAP/golden-death —strategies.extended_indicators),get_candlestick_patterns(doji/hammer/shooting-star/engulfing/morning+ evening star —strategies.extended_indicators.scan_candlesticks),get_news_sentiment_series(daily news-sentiment -1..1 + 7d SMA + latest innovation,news_sentimentchain),get_sentiment_lead_lag(Pearson/Spearman cross-correlation vs forward returns —strategies.sentiment_research) - moomoo-only optional:
capital_flow(get_capital_flow),smart_money(get_smart_money),economic_calendar(get_economic_calendar),fed_watch(get_fed_watch),market_breadth(get_market_breadth),revenue_breakdown(get_revenue_breakdown),corporate_actions(get_corporate_actions),earnings_catalyst(get_earnings_catalyst),institution_data(get_institution_holdings),earnings_surprise(get_earnings_surprise_history),expected_move(get_expected_move).
- stock/indicators/financials/insiders:
alpha_vantage,yfinance,moomoo,eodhd(OHLCV only),tiingo,twelve_data(OHLCV only),stockdata(OHLCV only) - news/global-news:
alpha_vantage,yfinance,finnhub,moomoo,massive,eodhd,stockdata,gdelt,benzinga,newsapi,seekingalpha(GDELT keyless article list; NewsAPI 100 req/day; Benzinga free tier; seekingalpha = keyless opinion-only tail served last — GDELT/Benzinga opt-in vianews_datachain, not default) - news-sentiment:
eodhd/sentiments(primary, EOD plan),alpha_vantageNEWS_SENTIMENT(25 req/day),gdelt(refuses: its article-list response carries no per-article tone) - quant calculators (tools,
strategies/*):get_volatility_estimators(Parkinson/GK/YZ/EWMA/GARCH),get_garch_volatility,get_covariance_read(Ledoit-Wolf shrunk + EWMA covariance),get_concentration_read(active share / effective holdings / HHI / entropy),get_tail_decomposition(incremental/component VaR),get_tail_extreme_var(EVT/GPD extreme-quantile VaR/ES),get_mean_reversion_quality(AR(1)/OU half-life), Roll spread + Kyle lambda (get_liquidity_risk/get_kyle_lambda),get_kelly_alloc(multi-asset fractional Kelly), preferred YTM/duration (capital_income--fi), credit hazard/default-prob (get_credit_spread_read), variance-swap strike (get_variance_premium), implementation shortfall (strategy_qualityavg_is_bp),get_turbulence_read(the cross-section's Mahalanobis distance from its strictly-prior history under a shrunk covariance + an own-history quantile gate —strategies.market_stress,enable_turbulence_read) - market snapshot fallbacks: Massive -> EODHD -> Tiingo -> Twelve Data
- crypto prices fallbacks: Tiingo -> Twelve Data
- macro:
fred,massive,moomoo(optional) - prediction markets:
polymarket,moomoo(optional, SG/MY-gated) - analyst ratings + earnings calendar:
finnhub,moomoo,yfinance(keyless fallback:get_analyst_ratings_yfinance= recommendation summary + price-target consensus;get_earnings_calendar_yfinance= earnings dates + EPS surprise + the days-to-print countdown, as do the Finnhub/moomoo calendar paths - quote that countdown instead of computing it). Institutional holdings also add a keylessyfinanceoption (get_institution_holdings_yfinance). - finnhub free-tier extra (key-gated):
get_basic_financials(metrics),get_company_peers,get_insider_activity(insider sentiment);get_fundamentalsalso acceptsfinnhubas a vendor - options chain:
yfinance,moomoo - SEC filings:
sec_edgar(when EDGAR fails for any reason — e.g. HTTP 403 from SEC fair-access throttling or a non-US ticker with no EDGAR record — theget_sec_filingstool falls back to Massive'sget_form4_insider_massiveForm-4 insider-activity data, returned under an explicit label so the agent does not mistake it for the full 8-K/10-K set) - insider transactions (Form 4):
moomoo,yfinance,alpha_vantage,sec_edgar(thenews_datacategory override), thenbenzinga— the firehose stays last, because its endpoint takes no ticker filter and pages the market-wide stream.sec_edgaris the keyless EDGAR-native open-market read (get_insider_transactions_sec_edgar): issuer-scoped and quota-free, so it outranks the untargeted firehose, though it is narrower than the top three (open-market only, no ownership rollup). It needs the optional extra (pip install ".[edgar]"); without it the vendor raises a typed no-data and the chain advances. Argued indocs/developer/12-data-providers.md - short interest:
yfinance,moomoo,massive - short-sale volume (Reg SHO daily) + ATS dark-pool flow:
finra(official public tier, keyless, as-of dates rendered + staleness-gated) - congressional trades:
congress(keyless House/Senate Stock Watcher mirrors) - patent activity:
patentsview(free keyPATENTSVIEW_API_KEY) - FX snapshot:
fx(keyless yfinance DXY/pairs) - TGA balance:
treasury_fiscal(keyless Treasury Fiscal Data) - earnings transcripts:
fmp(free tier) - all A-series/tier tools:
moomooonly (optional)
VENDOR_LIST is derived from VENDOR_METHODS (single source of truth) and is
therefore exactly the set of registered vendors, sorted:
alpha_vantage, benzinga, cboe, congress, eodhd, federal_reserve, finnhub, finra, fmp, fred, fx, gdelt, massive, moomoo, newsapi, patentsview, polymarket, sec_edgar, seekingalpha, stockdata, tiingo, treasury_fiscal, twelve_data, yfinance.
EODHD (TRADINGAGENTS_EODHD_API_KEY, key-gated) — the primary OHLCV
vendor (EOD plan $19.99/mo = 100k calls/day @ 1000/min, 30+ years):
get_stock_data_eodhd serves daily bars as the same CSV shape yfinance/moomoo
produce, registered first in the core_stock_apis chain
(eodhd,moomoo,yfinance by default) so moomoo/yfinance stay as fallbacks.
The EOD plan also unlocks get_news_eodhd (news), get_corporate_actions_eodhd
(splits + dividends), get_exchange_symbols_eodhd (full US symbol list,
~18k common stocks) — the screener's default --universe eodhd-us source —
replacement; get_top_movers_symbols_eodhd is the machine-readable symbol
table behind it, consumed by the screener's --universe eodhd-losers
(equity-filtered against the exchange-symbol common-stock list).
--value-dip-loose relaxes the value-dip technical entry to RSI<=35 OR
%b<=0.10 and appends a ranked near-miss table).
These back the get_market_snapshot / get_top_movers tools
when Massive 403s on the free plan.
Fundamentals/technicals/intraday/options are not on the EOD plan (they
need the $59.99 Fundamentals feed), so those chains keep moomoo/yfinance
first. A --vendor eodhd preset (batch.py/pipeline.py) puts EODHD first
in the OHLCV + news + corporate-actions chains and disables the moomoo-only
enrichment.
Tiingo (TIINGO_API_KEY, free Starter tier, key-gated) — additive market
data: deep EOD OHLCV (/tiingo/daily/{t}/prices, 7+ yrs, with resampleFreq
daily/weekly/monthly/annually), fundamental statements in JSON
(/tiingo/fundamentals/{t}/statements — income/balance/cashflow keyed by
dataCode, rendered to canonical-friendly label : value blocks that
statement_parsing maps via _ROW_ALIASES), a delayed IEX quote
(/iex/{t}), and crypto OHLCV (/tiingo/crypto/prices). News (403) and
intraday (404) are NOT on the free tier. Low caps (~1,000 calls/day, 50/hr,
500 symbols/mo) mean it sits last in the chains after eodhd/moomoo/massive,
relying on the 6h disk TTL cache; a 429 degrades to the next vendor via
VendorRateLimitError. The IEX snapshot backs get_market_snapshot as a
third fallback (Massive -> EODHD -> Tiingo).
Massive.com (MASSIVE_API_KEY, key-gated) — US-centric additive vendor:
get_massive_news (get_news chain + dedicated tool on the news/social
nodes) returning per-article structured sentiment (positive/negative/neutral
- reasoning); and
get_macro_indicators_massive(macro_datachain) serving treasury-yields / inflation / inflation-expectations / labor-market (aliases match the FRED surface:10y_treasury,yield_curve,cpi, ...). The FREDmacro_datavendor also exposes ICE BofA credit-OAS aliases for the credit-stress read:hy_oas/hy_spread/high_yield_oas→BAMLH0A0HYM2,ccc_oas/ccc_and_lower_oas→BAMLH0A3HYC,bb_oas→BAMLH0A1HYBB. Massive also drives a deterministicmacro_backdrop(yield-curve inversion / elevated breakevens) that keeps the B1 catalyst overlay de-risking even when the OpenD event calendar is unavailable (seestrategies/catalyst.py+docs/massive_integration.md§3a). Short interest (get_short_interestchain, FINRA 2-week cadence) and aget_short_volumetool (daily short-sale ratio, market analyst) come from the/stocks/v1/*endpoints. Form-4 insider activity is aget_form4_insidertool bound to the fundamentals analyst. (13-F is not wired: Massive's/stocks/filings/vX/13-Fhas no security filter — onlyfiler_cik/filing_date— so a per-ticker aggregate would be misleading; moomooget_institution_holdingsstays the source for that signal.) NOI (massive_noi.py, WebSocket monitor) and Flat Files (massive_flat.py, bulk OHLCV loader for the screener/backtests) are standalone monitored utilities, not graph@tools — seedocs/massive_integration.md§3e. Row 5 (entitled on the current plan):get_company_peersgains amassiveoption (related-companies) to the fundamentals analyst;get_corporate_actionsand a dedicatedget_dividendsbind Massive dividends/splits to the fundamentals analyst;get_ipos(IPO reference) binds to the news analyst. Plan-dependent recency/entitlements; FMV/Greeks are Business-only (unavailable, never invented). US-only — supplements, not replaces, moomoo/yfinance non-US coverage. Seedocs/massive_integration.md.
➠ get_market_snapshot / get_top_movers also bound to the market analyst
(plan-gated, see §6.4), and get_ratios to the fundamentals analyst; a
pipeline.py --universe top-movers-massive option supplies a Massive mover
universe (plan-gated). These degrade to "upgrade at massive.com/pricing"
on the free plan and activate when the account's plan includes them.
EODHD fallback: when Massive 403s (free plan), get_market_snapshot
falls back to eodhd.get_market_snapshot_eodhd (/api/real-time/{ticker},
live 15-20 min delayed OHLCV + change, works on the EOD plan) and
get_top_movers falls back to eodhd.get_top_movers_eodhd
(/api/real-time/{ticker}?ex=US, one call returns ~18k US stocks sorted by
change_p — a movers + universe replacement).
dataflows/symbol_utils.py::normalize_symbol() maps broker symbols to Yahoo
gold XAUUSD -> GC=F, forex EURUSD -> EURUSD=X, crypto
BTCUSD -> BTC-USD, indices SPX500 -> ^GSPC; moomoo code map
(_moomoo_code(): US., HK. pad, JP., SH., SZ., AU., CA., SG., MY., CC.USD).
US share classes are quoted with a hyphen on Yahoo (BRK-B, BF-A), but
moomoo emits dotted (BRK.B, MOG.A, MOG.B, PBR.A), which Yahoo cannot
resolve. normalize_symbol converts a dotted single-letter share-class suffix
(.A/.B/.C/.K...) to the hyphen form (BRK.B -> BRK-B), while leaving
London's single-letter .L exchange and all multi-letter exchange suffixes
(.SA Brazil, .TO, .AX, .HK, .NS, .BO, ...) untouched. Moomoo's
_moomoo_code deliberately does NOT use this - it keeps the raw dotted form
the movers rank returns - so both vendors resolve the same symbol.
Market-analyst house tools (bound only to the market node, not part of the
vendor category system - see 6.1): get_verified_market_snapshot
(deterministic OHLCV/indicator verification snapshot, the market analyst's
source of truth), get_momentum_scan (5-pillar day-trade pre-filter +
first-pullback + intraday confirmation), get_market_snapshot_alpaca (live
1-minute Alpaca L1 price/VWAP/volume when configured). News/earnings house
tools bound to the news node: get_catalyst_scale, get_earnings_event_read.
The analyst tool loops bind deterministic calculators from tradingagents/strategies/*
so the LLM reasons over computed numbers rather than re-deriving them:
Surface labels in the "Bound to" column are machine-checked
(tests/test_doc_binding_claims.py): market / news / fundamentals /
risk debators / trader name the toolset a tool is callable from, and
declared means no agent surface can call it — a library/CLI read whose
reason lives in tests/test_calc_agent_wiring.py::TOOL_LEGACY_BINDING.
| Tool | Wraps | Bound to | Returns |
|---|---|---|---|
get_market_technicals(ticker) |
domain_bundles.get_market_technicals |
market | ONE-pass composite (W4-1): regime + swing structure + momentum detail, with the bundle's data_quality and missing_fields |
get_fundamental_profile(ticker) |
domain_bundles.get_fundamental_profile |
fundamentals | ONE-pass composite (W4-1): value floors + valuation z + FCF yield + margin of safety (PIT-gated), with data_quality / missing_fields |
get_sentiment_flow_feed(ticker) |
domain_bundles.get_sentiment_flow_feed |
news | ONE-pass composite (W4-1): news sentiment series + GDELT tone + order imbalance, with data_quality / missing_fields |
get_portfolio_risk_envelope(ticker, basket?) |
domain_bundles.get_portfolio_risk_envelope |
risk debators | ONE-pass composite (W4-1): the name's tail risk + liquidity read + book tail risk, with data_quality / missing_fields |
get_swing_set(ticker) |
swing.swing_report |
market | trend stack, RSI band, 1-ATR stop, 2R/3R targets, VCP, trail |
get_swing_exits(ticker) |
swing.chandelier_exit + trail_ema + targets_rr |
market | chandelier trailing stop (3x ATR(14, simple mean of TR) below the 22-bar high) + 20-day EMA trail + 2R/3R targets measured to the chandelier stop (named in the line; get_swing_set's 2R/3R use its structure stop) |
get_dip_technical(ticker) |
swing.rsi + technical_factors (KST/MFI/Stoch) + value_dip.bollinger_pct_b |
market | RSI/%b + Stochastic + MFI + KST dip-timing read (OVERSOLD / not-oversold) |
get_mean_reversion_tech(ticker) |
technical_factors (StochRSI/RSI2/W%R/Keltner/Donchian/OBV/PSAR/Elder) |
market | mean-reversion dip-timing + exit technicals |
get_opening_range(ticker) |
market_session.opening_range |
market | ORB breakout + 2R stop/target |
get_option_breakeven(long_strike, long_premium, short_strike?, spot?, short_ttm_days?, delta?, days_to_earnings?, days_to_ex_div?) |
strategies.options_breakeven.pmcc_read |
market | option-position breakeven (strike + premium) + PMCC short-call floor, long-leg intrinsic/extrinsic split, delta band, 30-45d theta window, earnings/ex-div assignment risk — advisory, None-safe (n/a, never fabricated) |
get_vol_surface_shape(ticker) |
strategies.options_surface.surface_shape + term_structure_slope |
market | 25-delta risk reversal (skew sign), 25-delta butterfly (smile curvature), term-structure slope |
get_parity_screen(ticker, cost_bps=5) |
strategies.options_surface.parity_violation |
market | put-call parity / conversion-reversal screen (a screen, not a trade) |
get_shift_detection(ticker, kind=mean) |
strategies.regime.cusum + ewma_control + strategies.complexity |
market | CUSUM/EWMA online shift detection + LZ complexity beside its same-length i.i.d. null band (complements the batch HMM) |
get_taylor_read(policy_rate, inflation, output_gap?) |
strategies.cycle_tilt.taylor_rule |
news | Taylor-rule implied policy rate + actual-vs-rule deviation (tight/easy/neutral stance) |
get_lottery_factors(ticker) |
strategies.lottery |
market | MAX (largest single-day return in the month) + IVOL (idiosyncratic vol) lottery-tilt screen — high = expected underperformance (quality penalty) |
get_execution_schedule(notional, intervals, volatility?, temp_impact?, risk_aversion?, method=...) |
strategies.execution_schedule |
market | Almgren-Chriss optimal trajectory + TWAP/VWAP/POV benchmarks (E[IS]/var(IS), per-interval trades) |
get_spread_estimate(ticker, current_date?) |
strategies.liquidity_risk.corwin_schultz + abdi_ranaldo + spread_estimate |
market | quote-free high-low spread FLOOR (median of both estimators) for names with no quoted spread; None when the two-day correction is negative |
get_return_decomposition(ticker, current_date?) |
strategies.market_session.decompose_returns |
market | overnight vs intraday log-return legs + intraday share of variance (which leg carried the move, never why) |
get_quality_factors(ticker, current_date?) |
dataflows.quantitative_scores.gross_profitability + net_operating_assets (+ round-3 S1/S2/S10) |
fundamentals | GP/A (Novy-Marx) + NOA (Hirshleifer); unavailable when COGS or the prior-year balance sheet is missing. Gated additive rows: the Altman Z'/Z''/Z''-EM variant + distress zone with the X4 basis (enable_altman_variants), the F-Score's paper band + recorded deviations (enable_f_score_detail), the G/C scores with each median leg's exclusion printed when no peer panel is supplied (enable_growth_scores) — also renders the §55 SBC-adjusted free-cash-flow read (reported FCF beside economic FCF = FCF minus us-gaap:ShareBasedCompensation, plus SBC/revenue) from the SEC XBRL series it already fetches, and prints the producer's refusal reason when the filer files no such concept |
get_valuation_band(ticker, point_value?, current_date?, alpha?) |
strategies.conformal.rolling_band |
fundamentals | conformal band around a model value with the REALIZED coverage printed beside the nominal level; needs >= conformal_min_pairs model-vs-realized pairs |
get_analyst_revision_index(ticker, current_date) |
strategies.analyst_revisions.revision_index |
fundamentals | weighted, coverage-guarded analyst revision ratio (MSCI weights 3/2/1 over the periods supplied; denominator up+down printed as a deviation from analyst coverage; <2 actions -> unavailable; the estimate-change leg states why it is unavailable) - gated by enable_analyst_revision_index |
get_disclosure_tone(ticker, current_date?) |
strategies.text_factors.lm_tone + readability + divergence |
news | Loughran-McDonald tone counts + readability + filing-vs-news tone/complexity gaps; a zero-hit read is NO SIGNAL, not neutral |
get_book_risk_budget(current_date?) |
strategies.book_risk.min_cvar_weights + copula_scenarios |
risk debators | minimum-CVaR weights at the family's 95% tail (tail_probability=0.05), with the solved book CVaR and its tail_count printed beside the configured budget (a comparison, not a solver constraint — the LP binds on the cap / move limit); advisory sizing only, never an order |
get_risk_overlay(portfolio_value, floor?, expected_vol?, target_vol?, multiplier?) |
strategies.portfolio.cppi_exposure + size.volatility_target_scale |
market | CPPI floor-protected risky exposure + vol-targeting scale (advisory overlay) |
get_earnings_transcript(ticker) |
FMP Earnings Transcript API (free tier) | fundamentals | Latest earnings-call transcript: date/quarter + (when served) excerpt — quote only from returned text |
get_congress_trades(ticker) |
House/Senate Stock Watcher mirrors (keyless) | fundamentals | Congressional open-market stock trades: net buys/sells + samples per chamber |
get_financial_history(ticker, years=?) |
SEC EDGAR XBRL companyconcept (keyless) | fundamentals | Annual 10-K financial history (~10-15y; pre-XBRL years n/a) |
get_tga_balance(days=?) |
US Treasury Fiscal Data API (keyless) | news | Daily Treasury General Account operating-cash balance + net draw/build (reserve injection/drain) read |
get_fx_snapshot() |
yfinance (delayed) + FRED DTWEXBGS |
news | DXY + major FX pairs with 1d/5d changes, plus RISK-20's per-unit 1-day 95% FX move (daily sigma of the broad dollar index × z=1.645) — multiply by the notional you hold; advisory |
get_macro_indicators aliases extended |
fred.MACRO_SERIES |
news | liquidity (tga/reverse_repo/repo/fed_balance_sheet/effr/sofr), commodities (wti/gold/natgas/copper), global rates (ecb_rate/boj_rate) |
get_dark_pool_flow(ticker, weeks=?) |
FINRA ATS weekly (OTC Transparency, keyless) | market | Weekly off-exchange share/trade/notional flow per issue (as-of + staleness-gated) |
get_short_sale_volume(ticker, days=?) |
FINRA Reg SHO daily (keyless) | market | Daily short-sale volume (% short-sale of volume), keyless variant of get_short_volume |
get_edgar_fulltext_search(query, forms?, date_range?) |
SEC EDGAR full-text search (efts.sec.gov, keyless) | fundamentals | Full-text filing search (10-K 'major customer' footnote, peer mentions, thematic scans) |
get_patent_activity(ticker) |
USPTO PatentsView (free key) | fundamentals | Annual granted patents + recent titles (name-based assignee) — innovation/moat gauge |
get_gap_type(ticker) |
market_session.gap_type |
market | common/breakaway/runaway/exhaustion + fill stats |
get_order_imbalance(ticker) |
market_session.order_imbalance |
market | buy/sell-heavy from flow nets |
get_premarket_liquidity(ticker) |
market_session.premarket_liquidity |
market | thin-book warning |
get_post_close_confirmation(ticker) |
market_session.post_close_confirmation |
market | stopped-out / target-hit / holding |
get_gdelt_sentiment(ticker, look_back_days?) |
gdelt.get_gdelt_tone_series |
news | UNAVAILABLE, with the reason: GDELT's DOC 2.0 article-list response carries no per-article tone (a per-day series needs mode=timelineTone, unwired, verified against GDELT's API docs 2026-09-27) |
get_technical_factors(ticker) |
technical_factors (ADX/pivots R1-R3/S1-S3 + ATR-normalised pivot distance/Aroon/Fisher/Chaikin/Elder-Ray/Supertrend/volume-profile) |
market | extended technicals in one call (shares the run-level OHLCV cache) |
get_extended_indicators(ticker) |
strategies.extended_indicators (Ichimoku/CCI/ROC/momentum/TRIX/Force/A-D/VPT/CMF/anchored VWAP/golden-death) |
market | the standard trend/momentum/volume group plus cloud + VWAP cost basis, one call (shares the OHLCV cache) |
get_candlestick_patterns(ticker) |
strategies.extended_indicators.scan_candlesticks |
market | latest-bar doji/hammer/shooting-star/engulfing/morning+evening star scan |
get_book_tail_risk(ticker, weights?) |
book_risk.portfolio_cvar + book_correlated_stress + drawdown_gate + book_context resolver |
market/trader | book-level portfolio CVaR + correlated -10% stress + the measured book drawdown, each labelled with the mix it came from (configured basket vs the analyzed name alone - a name-level drawdown is never printed as "the book") |
get_covariance_read(tickers) |
covariance_models.ledoit_wolf_shrink + ewma_covariance |
risk debators | Ledoit-Wolf shrunk covariance (delta = (b2/t)/d2) + EWMA vol for a name list |
get_concentration_read(weights, benchmark_weights?) |
portfolio.active_share + weight_hhi + effective_holdings + weight_entropy |
risk debators | how concentrated a proposed book is (active share vs benchmark, HHI, entropy) |
get_tail_extreme_var(ticker, alpha?) |
book_risk.extreme_quantile_var |
risk debators | EVT/GPD extreme-quantile VaR/ES (extrapolates beyond the observed worst day) |
get_kyle_lambda(ticker) |
liquidity_risk.kyle_lambda |
declared | daily-bar price-impact slope (cross-sectional liquidity proxy) |
get_kelly_alloc(expected_excess_returns) |
portfolio.kelly_weights |
declared | multi-asset fractional Kelly alloc (w = f·Σ⁻¹μ, long-only) |
get_trade_outcome_metrics(ticker, entry, stop?, target?, direction?) |
prediction_ledger.outcome_metrics |
risk debators | MAE/MFE + stop/target hits over the trailing closes (W1-3) |
get_prediction_ledger_score(results_dir?, auto_invalidate?) |
prediction_ledger.score_all |
declared | calibration read: scores every logged prediction vs realized closes (W1-1) |
get_stress_grid_read(base_value, revenue_shifts?, discount_shifts?) |
regime_performance.stress_grid |
declared | DCF-style scenario grid (W2-11) |
get_macro_regime_read(rate_change?, curve?, spread?, usd?, vol?) |
regime_performance.macro_regime |
news | cross-asset Risk-On / Liquidity-Contraction / Stagflation (W4-6) |
get_liquidation_days(ticker, shares_to_liquidate?) |
liquidity_risk.days_to_absorb |
market | days for the market to absorb a block at a 15% participation cap |
get_premarket_review(ticker, prior_close?, open_price?, prior_stop?, entry_price?) |
pre_market.review_decision |
market | deterministic CONFIRM / REVISE / REJECT arbiter from measured deltas |
get_relative_strength(ticker) |
relative_strength.relative_strength_report + ::relative_strength_vs_sector |
market | leading/uptrend/lagging/diverging/unknown vs SPY, plus the sector-relative excess over the ticker's SPDR ETF (TECH-19; the benchmark is reported beside it, never blended) |
get_earnings_event_read(ticker, date) |
events.post_earnings_play + catalyst.last_earnings_surprise |
news | surprise %, drift side, print-day move, PEAD setup |
get_catalyst_scale(ticker, date) |
catalyst.build_catalyst_snapshot |
news | 0..1 scale + verdict + reasons |
get_position_sizing(confidence, stop_dist_pct, ...) |
size.kelly + risk-budget |
market | min(kelly_quarter, risk/stop, cap). Every argument is a FRACTION of capital (0.01 = 1%); a percent number (stop_dist_pct=8.371, risk_per_trade=1.5) is refused with the corrected form instead of being read as a size |
get_risk_gate(size_pct, ...) |
risk_governor.govern |
market/trader | PASS / WARN / REJECT for the trade-level limits. size_pct and every other *_pct argument is a FRACTION: a percent number is refused before the governor runs (size_pct=1.0 used to be read as 100% and REJECT a 1% proposal - NVDA 2026-09-15). The measured book drawdown is NOT a caller input: a drawdown_pct passed here is reported as a hypothetical and never moves the verdict - the portfolio drawdown gate is get_composed_risk_gate (one resolver: strategies/book_context.py) |
get_regime_read(ticker) |
overlays.build_strategy_overlays |
market | regime label + position scale + momentum/52w |
get_volatility_contraction(ticker) |
swing.vcp_setup |
market | VCP base depths + contraction + near-breakout |
get_orderflow_read(ticker) |
orderflow.summarize (+ guarded fetch) |
market | inst/retail net, distribution, divergence, alignment |
get_financial_trends(ticker, curr_date, items?, periods?) |
agents/utils/financial_trends.py |
fundamentals | labelled quarter-series table: | Item | <date (FYq)> | … | YoY | QoQ |, fiscal labels derived from the vendor's own annual period ends, every delta naming the two periods it compared (+105.9% (Q2 FY26 -> Q2 FY27)), n/a/unavailable with a reason rather than a guess - the producer the 2026-09-12 NVDA review found missing when a +47% two-quarter move was reported as YoY |
get_analyst_verdict(ticker, date) |
screener screen_ticker + normalized.trap_verdict |
fundamentals | EY, EV/EBIT, Tobin's Q, F/M/Z, Net-Net, trap risk, ROE, YoY |
get_earnings_surprise(ticker, date) |
events.surprise_score + catalyst.last_earnings_surprise |
fundamentals | surprise % + side |
get_portfolio_weights(scores, sector_map, ...) |
portfolio.value_ratio_weights + adjust_for_caps |
fundamentals | cap-respecting value weights |
get_basic_financials(ticker) |
finnhub.get_basic_financials_finnhub |
fundamentals | EPS/revenue YoY, ROE/ROA, margins, payout (free tier) |
get_insider_activity(ticker) |
finnhub.get_insider_activity_finnhub |
fundamentals | 12m net insider change + mspr + trend |
get_company_peers(ticker) |
finnhub.get_company_peers_finnhub |
fundamentals | comparable peer group |
get_form4_insider(ticker, start, end) |
massive.get_form4_insider_massive |
fundamentals | net open-market Form 4 buys - sells (excl. A/M) |
get_filing_sections(ticker, section?) |
massive.get_filing_sections_massive |
fundamentals | 10-K item text (risk_factors, business) as bounded excerpts |
get_risk_factors(ticker, include_taxonomy?) |
massive.get_risk_factors_massive |
fundamentals | disclosed risks grouped by vendor category + supporting text |
get_8k_filings(ticker) |
massive.get_8k_filings_massive |
fundamentals | recent 8-K current reports with their item text |
get_ratios(ticker, date?) |
strategies.ratios.compute_ratios (local derivation; Massive plan-gated cross-check via get_fundamentals) |
fundamentals | computed EV/EBITDA, P/E, P/B, P/S, P/CF, P/FCF, ROE, ROA, D/E, Current, Quick, cash ratio, dividend yield, FCF, market cap (free, no paid plan; missing inputs n/a). The returned dict also carries the §55 SBC keys (sbc, sbc_to_revenue, sbc_adjusted_fcf, sbc_to_fcf), which are engine-only and NOT rendered: on this vendor-only path the flat sbc key has no source — fetch_ticker runs here without with_sec_series (the SEC 10-K series is what fills the key) and the vendor cash-flow row is labelled Stock Based Compensation, which no canonical alias maps (live MSFT 2026-09-27; INCY 2026-09-29, whose EDGAR sbc series does resolve) — so the read renders through get_quality_factors instead, which fetches the series |
get_exit_check(entry, close?, atr?, ..., ticker?) |
strategies.exits.exit_check |
market | stop-to-breakeven, ATR target, holding action. Pass ticker and the close + 14d ATR are MEASURED from the run's series (a conflicting caller value is reported as ignored); without one the output labels its levels CALLER-SUPPLIED, so an invented ATR (NVDA 2026-09-15 passed atr=7.0) is never read as computed |
get_allocation(scores, sector_map?, returns_by_name?) |
strategies.portfolio.adjust_for_caps (+ correlation_penalty when enable_correlation_penalty is on and return series are provided) |
fundamentals | cap-respecting book allocation; optionally correlation-penalized (down-weights names whose avg pairwise correlation with the book exceeds the threshold) |
get_regime_components(ticker) |
strategies.regime |
market | vol_pct / trend / chop / regime label breakdown, plus the SPY/QQQ/IWM index reads (REG-4), the market-depth block and the regime state block: HMM transition probability + duration (REG-16) and the entropy/confidence/stability/surprise metadata (REG-17) |
get_consensus(ratings) |
strategies.consensus |
declared | numeric agreement -> high/low consensus |
get_momentum_detail(ticker) |
strategies.momentum |
market | pillars, rvol (session-adjusted on a forming bar), vwap, ema9, first-pullback |
get_beat_miss_sizing(side, catalyst) |
strategies.events.position_mult_by_side |
news | post-earnings key multiplier |
get_dcf_valuation(ticker, date, growth?, erp?) |
strategies.dcf.compute_dcf |
fundamentals | provider-sourced DCF fair value + WACC / EV breakdown; Gordon terminal value anchored to the last projected year's FCF (F_0·(1+g)^years, discounted back from year N) — audit-fixed 2026-09-06 |
get_sector_rotation_screen(enable_breadth?, top_n?, constituent_universe?, eodhd_cap?) |
strategies.sector_screener |
market | Sector-first rotation screen: regime cap + multi-factor SPDR rank + RRG quadrant + pullback-divergence leaders + dispersion trend (+ breadth on the full S&P-500 universe n<20-gated, EW/CW vs the real RSP* index/own-50d-SMA, multi-timeframe 20/50/200 matrix + McClellan Osc/MSI + Setup A-B) — advisory |
get_sector_rank(ticker) |
strategies.sector_rank.rank_sectors + sector_standing (+ gated rank_sectors_multifactor / rank_industry_group / constituent_breadth / leadership_ratio) |
market | 11-SPDR 1m/3m momentum ranking + the ticker's sector standing; when enable_sector_multifactor (P1) /enable_sector_industry (P2) /enable_sector_breadth (P3) are on, advisory score / industry / breadth / leadership lines append (default off → legacy output unchanged). The P2 industry layer uses the sector_rank.INDUSTRY_ETFS universe — extended to the IT subsector set (SOXX/IGV/CIBR/SKYY/AIQ/BOTZ/DTCR/NXTG/IYW/FINX/XSD parented XLK; VGT = benchmark, excluded from the pool) with dual-benchmark RS: on the XLK pool each row also gets rs2 (percentile vs VGT), so "strongest IT subsector" (vs VGT) and "beats the market" (vs SPY RS) are answered from one pass (top-pick line appends rs2_vs_vgt=…). |
get_dupont_read(net_margin, asset_turnover, equity_multiplier, tax_burden?, interest_burden?) |
strategies.dupont |
fundamentals | DuPont ROE decomposition + driver verdict (margin/turnover/leverage/mixed-led via log attribution) |
get_scenario_dcf(fcf, wacc, shares?, cash?, debt?, g_base?, g_bear?, g_bull?, margin_shock_bear?, margin_shock_bull?, market_price?) |
strategies.scenario_dcf |
fundamentals | bear/base/bull DCF value range + market band (below bear/bear-base/base-bull/above bull) + margin of safety |
get_earnings_quality_verdict(net_income, ocf, total_assets, fcf?, capex?, eps_growth?, fcf_growth?) |
strategies.earnings_quality |
fundamentals | earnings-quality CONCERN verdict (LOW/MEDIUM/HIGH = concern; cash conversion, accruals, FCF-vs-EPS divergence) |
get_strategy_quality(ticker, returns?) |
strategies.evaluate + statistical.omega |
market | net CAGR / annualized vol / Sharpe / Sortino / PSR / max drawdown / Omega over a return series |
get_downside_read(ticker, target?) |
strategies.rate_utils.downside_measures |
market | semi-deviation / downside deviation / shortfall probability / average shortfall vs a target (MAR) |
get_horizon_var(ticker, horizon_days?, confidence_level?) |
strategies.book_risk.var_cvar_horizon |
market | empirical + parametric VaR/CVaR at a multi-day horizon, with the sqrt(T) i.i.d. scaling gate. The argument is a confidence LEVEL (0.95), the opposite convention from the daily-tail cvar reads' tail probability (0.05) — the parameter was alpha until 2026-10-12 |
get_trailing_exit(ticker, entry, peak, current, trail_pct?) |
strategies.exits.trailing_stop_exit |
market | peak-trailing / give-back stop verdict + exit price |
get_exit_engine(ticker, entry_bars?, entry_date?, entry_price?, risk_gate?) |
strategies.exit_engine.evaluate_long_exit |
market | the five-mechanism exit decision for a position that is ALREADY OPEN — 2.5×ATR initial stop, 3×ATR chandelier ratcheted from the high-water mark since entry, a confirmed MA50 breakdown (two consecutive closes), momentum deterioration as persistence, and the opportunity-cost stall — published as HOLD / REDUCE / EXIT / RISK_EXIT / UNAVAILABLE with the rule that decided. Point-in-time: the stop tested on a bar never uses that bar's own high, a close-evaluated signal fills at the next tradable price and an intraday stop that gapped fills at the open. Pass entry_bars or entry_date; any rule it cannot measure is named rather than assumed safe. Gated by enable_exit_engine |
get_exit_plan(entry, atr?, current?, peak?, stop?, giveback_pct?, ticker?) |
strategies.exits.breakeven_after_confirmation + max_giveback_exit |
market | structure/R breakeven trigger + margin-giveback stop in one exit-management read. Same measured-level contract as get_exit_check: pass ticker for a MEASURED close + ATR, otherwise the output labels its levels caller-supplied |
get_scaleout_plan(entry, stop, t1_fraction?) |
strategies.swing.scaleout_plan |
market | tiered partial-profit plan (sell T1 fraction -> break-even -> trail) |
get_payoff_asymmetry(ticker, returns?, threshold?) |
strategies.statistical.omega |
market | Omega ratio (gains/losses payoff asymmetry) about a threshold |
get_book_correlation(returns_by_name, method?) |
strategies.statistical.correlation_matrix |
market | full pairwise correlation (avg + max pair) over a book |
get_risk_parity_alloc(ticker, returns_by_name) |
strategies.portfolio_optimizer (risk_parity + min_variance + max_diversification + risk_contribution) |
market | risk-parity weights (equal marginal risk contribution via the converging w ← b⊘Σw fixed point — audit-fixed 2026-09-06; degrades to equal-weight with a note on non-convergence), min-variance weights, max-diversification weights (Choueifaty Σ⁻¹σ) and per-name risk contributions from a real covariance matrix |
get_hrp_alloc(ticker, returns_by_name) |
strategies.hierarchical_risk_parity |
market | Hierarchical Risk Parity book weights (single-linkage HRP; robust under noisy covariance, no Σ inversion) + cluster order |
get_momentum_12_1(ticker) |
strategies.momentum.momentum_12_1 |
market | canonical 12-1 momentum (skips the last month's short-term reversal; needs ~274 bars) |
get_margin_of_safety(ticker, intrinsic) |
strategies.normalized.margin_of_safety |
fundamentals | (intrinsic - price)/intrinsic safety band (wide/modest/negative) |
get_cross_section_momentum(ticker, names?) |
strategies.cross_section.momentum_book (+ centered_rank, quantile_split, neutralize_book, book_risk.net_beta) |
market / fundamentals | advisory cross-sectional momentum book over a caller-supplied panel: ranks by risk-adjusted momentum (factors.vol_adjusted_momentum, lookback 126 / vol window 21), equal-weights the top and bottom quintiles, neutralizes dollar + beta (and sector when the caller supplies sector_map), and reports net beta before → after; None under 8 scored names — a named gap, never a fabricated long/short. The panel is the names you pass (or the ticker's vendor peers), so its size travels with the read and it is not market breadth |
get_composite_rank(ticker, factors?, current_date?) |
strategies.factors.composite_score (+ round-3 S3 under enable_quality_composite) |
fundamentals | cross-sectional value+momentum composite percentile vs industry peers (the peers come from finnhub.peer_symbols, which PARSES the vendor's rendered "Peers: A, B, ..." string - iterating it made the peer set the sentence's characters); gated additively the 0-100 composite quality score over the same peer set with its metric set, coverage, peer count and quality band |
get_tail_risk(ticker, alpha?) |
strategies.book_risk.cvar / simple_var / stress_loss + cdar |
market | historical VaR / CVaR tail budget + CDaR/DVaR drawdown-tail + -10% uniform stress loss. The appended one-line family reads the same return series — p_loss (RISK-16's sibling §34), sigma_ratio/sigma_expansion, sterling, reversal, the loss-frequency family, and RISK-16's stop_hit_1m + stop_dist (the share of trailing month-long paths that touched the repo's own 2xATR stop) — each printing n/a when unmeasurable |
get_credit_spread_read(date) |
strategies.credit_spread.credit_stress_level |
market | FRED ICE BofA HY/CCC/BB OAS + deterministic credit-cycle band (low/mod/high/severe) + de-risk scale |
get_session_discipline(ticker, peak_pnl?, current_pnl?) |
strategies.momentum.session_flags + psych_level + past_optimal_window |
market | intraday walk-away rules (giveback, max-daily-loss, past 10:00 ET optimal) + nearest psych levels |
get_earnings_quality(ticker, date) |
strategies.earnings_quality + strategies.normalized.trap_verdict |
fundamentals | provider-fed earnings-quality read: consensus CONCERN verdict (cash conversion, accrual ratio, FCF=OCF- |
get_bollinger_pct_b(ticker) |
strategies.value_dip.bollinger_pct_b |
market | Bollinger %b (price position inside the 20-day 2-sigma population-SD band; a vendor sample-SD band is 1.026x wider); %b <= 0 at/piercing the lower band, <= 0.10 the mean-reversion entry zone |
get_tranche_plan(ticker, weights?, risk_pct?, account?) |
strategies.value_dip.tranche_plan |
market | 3-tranche scale-in plan (P1/P2/P3 at 1.0/2.0 ATR, the EXECUTED share-weighted avg entry with the nominal sum(w_i*P_i) beside it, composite stop P3-1.5ATR, capital-at-risk check, 1.8R/3.0R targets off the executed R + blended R:R + breakeven win rate) |
get_trade_expectancy(p_win, avg_win, avg_loss, rr?) |
strategies.value_dip.expectancy + breakeven_win_rate |
market | per-trade expectancy E = p*W - (1-p)*L and breakeven win rate 1/(1+R:R) |
get_fcf_yield(ticker, date) |
strategies.value_dip.fcf_yield |
fundamentals | FCF / market cap (>= 6% is the value-dip value-floor row) |
get_capex_quality(ticker, date) |
strategies.capex_quality.capex_quality_read |
fundamentals | Capital-allocation read (advisory, deterministic): CapEx intensity z vs the name's own history, funding cover OCF/CapEx, CapEx-vs-revenue 5y CAGR elasticity, incremental ROIC (3y lag, dNOPAT/dInvestedCapital) + economic spread vs WACC, CapEx ROI 3y, payback, FCF-recovery gap to a 3% target yield, a 5-regime label (HARVEST / PRODUCTIVE / PRODUCTIVE_INVESTMENT / INVESTMENT_WATCH / OVERINVESTMENT / DISTRESS) and a 0-100 quality score + advisory valuation penalty. NEVER a hard gate - distinguishes negative-FCF reinvestment (PRODUCTIVE_INVESTMENT) from value destruction (OVERINVESTMENT / DISTRESS) |
get_valuation_z_score(ticker, date, multiple?) |
strategies.value_dip.valuation_z_read |
fundamentals | historical valuation Z (current vs own trailing P/E, EV/EBITDA or P/FCF; Z <= -1.5 = cheap vs history) |
get_value_dip_setup(ticker, date) |
strategies.value_dip.value_dip_setup |
fundamentals | the hybrid allocation matrix (value floor + technical entry + trade risk + exit target) as one computed candidate verdict |
get_balance_sheet_health(ticker, date) |
strategies.value_dip.balance_sheet_health |
fundamentals | D/E < 1.0 OR current ratio > 1.5 (Step-1 balance-sheet gate) |
get_macd_divergence(ticker) |
strategies.value_dip.macd_divergence |
market | Daily RSI(14) / MACD-histogram momentum divergence (bullish-divergence / higher-low / lower-low-confirmation) |
get_vdu_entry_setup(ticker) |
strategies.value_dip.vdu_entry_setup |
market | Step-2 entry ladder: volume dry-up near support -> divergence/higher-low -> trigger candle (close above prior high, session-adjusted RVOL >= 1.3x), plus the reported volatility-compression reads (compression / range_tight) and, when enable_mechanical_volume_discount is on, the OPEX-week-discounted rvol_gate |
get_support_structure(ticker) |
strategies.value_dip.support_structure |
market | major weekly / multi-month base support + 200-day SMA proximity |
get_decline_driver_check(ticker, date) |
strategies.value_dip.decline_driver_check |
fundamentals | negative-force screen (clean/caution/structural): trap-HIGH, accruals>6%, negative 12-1m momentum, non-positive FCF/ROE, severe EPS decline |
| get_regime_gate_read(ticker, catalyst_window?) | strategies.regime.regime_gate_read | risk debators | mean-reversion knife guard: vol_pct + fast-downtrend + pass/block verdict |
| get_fixed_risk_size(equity, risk_frac, entry, stop_loss, commission_rate?, units?) | strategies.risk_sizing | risk debators / trader | commission-aware, tranche-aware fixed-risk share count (the governor-budget sizer) |
| get_exit_overrides(targets, state_by_name, max_drawdown_pct?, trail_pct?) | strategies.risk_manager.manage_risk + trailing_stop_targets | risk debators | two-pass liquidate/shrink overrides (Lean L1) from persisted entry/peak/current state |
| get_pre_trade_read(symbol, notional, max_notional?, max_rate?, window_secs?) | strategies.risk_checks.pre_trade_check + RateLimiter + notional | risk debators | notional-cap + rolling-rate submission gate (advisory) |
| get_ledger_risk_state(ticker) | memory log + strategies.pre_market.ledger_track_record | risk debators | realized win-rate drift + paper-reviewer track record (daily-loss/HWM inputs) |
| get_trade_plan(ticker, price?) | strategies.trade_plan.build_trade_plan | risk debators / trader | the written plan card as a callable. Carries §103's ENTRY block (entry ceiling, §100 final entry price = the min over the terms present with each term's value, target entry, execution price = §103's liquidity_adjusted_entry_price, risk-adjusted max entry, tranche levels) and its EXIT block (the §101 predicate: every evaluated condition, coverage N/7, the level that fires and its price, and the exits NOT measurable at plan time named absent - never "no exit"). The card is also the string injected into computed_decision_context, so the Trader and the PM read the same rows. The card's own assembly additionally captures the structured §103 object (build_trade_plan(capture=...)): the graph stores it on the entry_exit_block state channel, and trade_plan.render_entry_exit_block renders it into the Trader's plan and the PM's decision — so the entry price and the exit price print in 3_trading/trader.md, 5_portfolio/decision.md and the consolidated report, and travel in research_decision.json (see §"the only input contract" below). One assembly per run: those surfaces read the captured dict, never a second computation and never a regex over this card's markdown. The captured object carries §103's complete member set — all 13 ENTRY and all 16 EXIT names the spec lists — assembled from this card's own primitives plus strategies/entry_exit_families.py, which computes the §8-§42 constructions (the volatility and support entries, the volatility / ATR / support / break-even stops, the volatility / risk-multiple / fair-value targets) by the formula each section states. Each member is the price the run measured or None with the reason it is absent; momentum_entry_price / momentum_target have no score→price map and fair_value_price / fair_value_target need statement data the pre-graph card never fetches, so both pairs are named absent rather than defaulted |
| get_fixed_income_risk(ticker, years?) | strategies.fixed_income | fundamentals | preferred yield + duration/DV01/convexity risk rows (perpetual YTM n/a) |
| get_pair_risk(ticker_x, ticker_y, window?, maxlag?) | strategies.statistical.cointegration_pair + granger_causality (fetches both close series) | market | Engle-Granger cointegration + lag-wise Granger causality |
| get_pair_trade_signal(x, y, entry?, exit_thresh?, stop?) | strategies.statistical.pair_signal (spread_zscore + cointegration + half-life) | market | cookbook recipe-3 pairs signal: entry |z|≥2 / exit ≤0.5 / stop ≥3, dollar-neutral pair_quantities + VECM ecm_loading advisory |
| get_signal_quality(signal, forward_returns, quantile?) | strategies.signal_analysis (rank_ic / icir / long_short_precision) + evaluate.purged_cpcv_splits | market | deterministic validation read for a forecast: Spearman rank IC, ICIR, Qlib long-short precision (top-quantile sign hit rate) + combinatorial-CPCV path count (audit-fixed 2026-09-06) |
| get_bsm_option_quote(spot, strike, t_years, vol, option_type?, r?, q?) | strategies.options_math.bsm_equity_surface | market | direct Black-Scholes-Merton spot-space price + Greeks (incl. charm with dividend term) when the chain does not quote the asked strike/expiry — advisory model quote |
| get_constituent_cap_weights(weights, cap?, ceiling?) | strategies.capital_income.cap_and_redistribute | fundamentals | exact-ceiling constituent capping (excess pro-rata to sub-soft-cap names; flags degenerate fallback when the ceiling is not enforceable) |
| get_ts_momentum_weights(closes_by_name, horizon?, target_vol?, max_leverage?) | strategies.momentum.ts_momentum_weights | market | MOP-style time-series momentum weights: sign(12m log return) / EWMA vol, target-vol normalized, gross-leverage capped |
| get_event_pnl_response(spot, delta, gamma, vega, theta, dS_pct, dSigma?) | strategies.options_math.greek_pnl_response | market | cookbook recipe-5 scenario P&L: Δ·dS + ½Γ·dS² + ν·dσ + Θ·dt per option unit |
| get_merton_distance(equity, debt, equity_vol, r?, t?) | strategies.credit_spread.merton_distance_to_default | market / risk debators | structural distance-to-default (equity-as-a-call fixed-point; DtD = d2 + risk-neutral PD) |
| get_book_depth_read(bid, ask, bid_size, ask_size) | strategies.market_session.book_depth_read | market | microprice (bid·ask_sz + ask·bid_sz)/(bid_sz+ask_sz) + order-book imbalance + thin-side verdict |
| get_vif_read(ticker, factors, window?) | strategies.factor_expressions primitives (rsi/mom/bias/zscore/std/vol/range) + statistical.variance_inflation_factor | market | per-column VIF (collinearity check; > 5 = HIGH) |
| get_vol_cones(ticker) | strategies.rotation.vol_cones | risk debators | multi-horizon realized-vol percentiles (5/10/21/63/126d) |
| get_trade_excursions(trades) | strategies.journal.trade_excursions | declared | MAE / MFE / profit-factor / max intra-trade drawdown (exit quality) |
| get_alpha_scoring(direction, predicted_magnitude?, period_days?, actual_return?, confidence?) | strategies.alpha_eval.alpha_score | fundamentals | direction + magnitude-scored alpha ("said +12%, realized +2%") |
- The 3 risk debators (aggressive/conservative/neutral) now run an in-node
risk-tool loop (
agents/utils/risk_tool_loop.py): the plain LLM is bound to a 23-tool risk set (gate/tail/liquidity/vol/tranche/sizing/exits/credit/ pre-trade/ledger), capped atMAX_TOOL_ROUNDS(8), and degrades to a plain invocation when the provider cannot bind tools. - The Trader runs a 12-tool verification pass after its structured proposal (sizing / exit / tranche / expectancy / plan-card tools).
- Calls a provider writes as TEXT are executed, not echoed. A relay can
answer a tool-bound turn with its own tool-call markup in
contentand an emptytool_callslist;run_tool_loopparses that markup (agents/utils/tool_call_markup.py::parse_text_tool_calls— both wrapper widths, and the spelling that lost itstool_prefix) and dispatches the calls through the sameToolExecutor, then re-asks for prose without tools. Markup never reaches a caller as prose: the loop returns the residual prose or an explicitunavailable:notice, andtrader._verification_is_stubrefuses to append a markup reply under the "Computed verification" heading.report_verifieruses the same vocabulary to flag an already-leaked block in a finished report. - The Research Manager and Bull/Bear researchers receive the computed decision context (regime gate, plan card, risk snapshot, factsheet).
get_risk_gateexposes the trade-level governor surface:book_total_pct,daily_loss_pct(daily-loss budget),hwm_drawdown_pct(soft/hard high-water-mark tiers),sector_pct(sector cap),capital_at_risk_pct/risk_cap_pct(tranche capital-at-risk),liquidity_verdict,halted. Not the realized book drawdown: that is a measured state input owned bystrategies/book_context.py::measured_book_drawdown(the same resolver the governor's gate and the report'sBook drawdownline use), andget_composed_risk_gateis the tool that applies it. Adrawdown_pctpassed toget_risk_gateis echoed as a labelled hypothetical only, so a model can no longer produce a REJECT the composed gate never issued.
Every tool follows the no-fabrication contract: exact computed numbers or an explicit "unavailable" message (both recorded in the agent's tool history for auditability), never an invented value.
The value screens read a canonical line-item dict (the vendor-output ->
canonical layer in tradingagents.dataflows.statement_parsing, re-exported
by scripts/value_screener for the backend CLI). For moomoo markdown the
parser emits {"current": .., "prior": ..} dicts for keys present in two
consecutive periods (statements list newest-first; tables are sorted by period
year). The Beneish M-Score and Piotroski time-components use
current/prior via quantitative_scores' _num()/_prv();
every other read unwraps with statement_parsing._latest(). moomoo
--prefixed sub-item / contra lines (-Accounts Receivable,
-Accumulated Depreciation) are skipped so the aggregate value wins.
Because the M-Score needs both periods, its M column now computes on moomoo
data (previously always n/a), and the latest-value bug (which silently kept
the OLDEST period) is fixed.
- Memory log (
agents/utils/memory.py): append-only markdown; pending entries resolved at the next same-ticker run with realised return/alpha + reflection; track-record stats injected into the PM context; mtime-cached parse. - Checkpoints: per-ticker SQLite under
~/.tradingagents/cache/checkpoints/,--checkpointopt-in,clear_checkpoint/clear_all_checkpoints. - Vendor cache: disk TTL (6h) under
data_cache_dir/vendor_cache/.
reporting.py::write_report_tree(state, ticker, path) writes the per-section
tree plus complete_report.md with H1 report -> H2 team -> H3 role -> H4+
agent content (agent headings demoted 3 levels) and an auto Table of Contents.
When the risk governor ran, the Risk Gate (computed) block also renders the
Analyzed-name CVaR and (with a configured risk basket) the
Portfolio (book) CVaR — this fed the gate lines from risk_context; with the
tranche fold on, it additionally shows Tranche peak-deployed (cap-ok) and
Tranche capital-at-risk from tranche_context.
scripts/rebuild_complete_report.py re-renders folders without a re-run and
preserves Risk Gate (computed) blocks.
reporting.py::write_research_decision(state, ticker, path) writes
research_decision.json next to the run card — the only input contract of
the execution layer, which reads it as a versioned envelope. It declares
schema_version 1.1.0 with an offset-aware expires_at (the close of the
session following effective_date, 20:00 ET), a producer block whose
run_id is the report directory name (stable across a re-emit, so the
executor's inbox key stays stable), a UUIDv4 idempotency_key, and two
self-excluding hashes (artifact_sha256 over the body, decision_hash over
the body minus itself). It sets no executor-owned field: the advisory label
is binding_constraint, never binding_gate/trade_permission (the executor's
gate owns those), and risk_context is an allow-listed advisory passthrough the
executor records but never reads as data. opportunity_score stays null:
this repo has no deterministic producer for it, and the schema prefers an absent
score to a fabricated one. position.entry_price is §100's final entry
price — the §103 block's final_entry_price, falling back to the G1
contract's stop anchor when no final was measurable, and null when neither was.
position.take_profit is the contract's measured target (the contract's
one-line render carries it precisely so this field is recoverable rather than
always null). The whole §103 ENTRY/EXIT object travels verbatim under
entry_exit; it is advisory, and the executor's parser reads the four
position members it knows and ignores the rest. The rules live in one module,
tradingagents/execution_contract.py, shared by the emitter and the report
verifier (verify_flags.json gains an envelope block; legacy 1.0.0 trees are
exempt). Contract note and acceptance tests: docs/execution_v1_emitter_plan.md.
The verification payload is machine-readable as well as readable. Alongside claims and overall
(now PASS | FLAG | NUMERIC_ONLY | UNKNOWN), each stem carries basis: the typed triples the deterministic
layer extracted, as {metric, value, basis, source}. basis is the period token the report prints beside
the value (ttm:ttm, date:2026-06-30, period:annualized) or, when the report states none, the metric's
unit class (level, percent, multiple, ratio) — a bare ratio is deliberately unbounded, because a
ratio above 1 is ordinary here (P/E 135.94). source is evidence when the value resolves to a tool leaf
within the metric's tolerance, else report. NUMERIC_ONLY means the LLM half could not produce a verdict
but the figures were extracted, checked and found consistent; UNKNOWN is reserved for "nothing ran".
The registry is what makes two runs of one ticker comparable without diffing prose — the same
(AMZN, 2026-09-14) calls resolved FY-annual flows at 22:5xZ and TTM quarters at 19:08Z, which no sentence
records.
Every number in these blocks states the period it was built from. get_ratios
leads with - basis: flows TTM (4 quarters ending …); balance sheet … (the
flows come from statement_parsing.trailing_twelve_months(), which reports a
window only with full quarter coverage; compute_ratios labels the fallback
price / reported EPS when it has to use it), get_balance_sheet_health prints
the four canonical rows it divided, get_dupont_read(ticker, curr_date) derives
all three legs from ONE payload and states its basis (the raw-number form labels
the legs caller-supplied unless period= is given), and each Finnhub metric is
tagged with its basis (roaTTM (TTM, Finnhub)) with an advisory note when the
payload's own margin x turnover contradicts its ROA. write_report_tree also runs
the deterministic identity checks at write time and records them in
run_card.json as analyst_consistency ({stem: [{status, claim}]}) - report_verifier._valuation_identity_checks, i.e.
the valuation quartet (DuPont, P/E basis, EV/net-cash, R-multiple) plus the
net-debt sign, current-ratio, ROA, fiscal-quarter-label and date-count
identities (the last recomputes a stated N days out from its own ISO date and
the run date read off the report directory, and stays silent without an anchor),
so a tree carries its own consistency record instead of depending on the opt-in
scripts/report_verify.py pass. Advisory: it never blocks or edits a report.
| Command | Purpose |
|---|---|
python *.py ETF engine (docs/design_etf_fundamental_valuation.md) |
get_etf_valuation() weighted constituent P/E/fwd-P/E/yields/percentile/vs-SPY-XLK; get_etf_decline_driver() MARKET/SECTOR/ETF_SPECIFIC/CONSTITUENT_DRIVEN/UNKNOWN; get_etf_relative_strength() both legs vs SPY/QQQ/XLK; get_etf_risk() beta/capture/vol%-percentile/ATR%/maxDD; get_etf_mechanics() NAV premium/discount + distributions |
classify_security(ticker) |
security-type gate: ETF (quote_type / universe / fund-issuer name) vs operating_company vs UNKNOWN (no behavior change) |
TRADINGAGENTS_ENABLE_ETF_ENGINE |
default-off gate: fundamentals analyst swaps company statement tools for the ETF toolset when the security classifies as a fund |
tradingagents |
interactive CLI (typer/rich), full flow |
python batch.py --symbols ... |
headless concurrent; --vendor, --workers, --analysts, --depth, --date |
python pipeline.py --universe top-losers --top 5 |
screener + composite rank + batch (B2) |
python scripts/value_screener.py ... |
value screens / scans / composite |
python scripts/exit_engine.py --tickers … --entry-bars N [--variants] |
the five-mechanism exit decision per name, with the per-rule census and the four-variant A/B (report → screener/) |
python scripts/action_report.py ... |
conditional action report (basket vs report verdicts vs live market) |
python scripts/positions_to_basket.py ... |
combine broker position CSVs -> TRADINGAGENTS_RISK_BASKET_* weights (cash in the denominator; --apply rewrites .env) |
python scripts/rebuild_complete_report.py reports/<dir> |
re-render TOC reports |
python scripts/verify_sweep.py ... |
post-verifier adjudication workbench: condense a report tree's verify_flags.json into N confirmed (MISQUOTED/contradicted/internal-conflict) vs M suspect (unsupported) needing review; --json --confirm-only |
python scripts/rule_eval.py --ticker SKHY |
per-rule forward-return table (fwd 1/5/10/20d, hit, MAE/MFE, Sharpe, PF; n>=30 INSUFFICIENT guard); advisory |
python scripts/jev_decide.py [--tree <dir>] [--verdict] |
judge a document with the TypeSafe decisions model (typesafe/jev-1.13) via POST /api/alpha/decisions — not a chat model (its model / endpoint / per-call timeout default from TRADINGAGENTS_JEV_MODEL / TRADINGAGENTS_JEV_ENDPOINT / TRADINGAGENTS_JEV_TIMEOUT in .env, falling back to the literals); --verdict = the analyst reports only, position language neutralised, buy/hold/sell battery; also --state-file / --state / --stems / --all / --neutralize / --rating / --questions; exit 1 on any failed call |
get_composite_sizing(confidence, stop_dist_pct, ...) |
composite recommended position: min(quarter-Kelly, risk/stop, cap) x vol x liquidity x uncertainty, clamped 0 by blocking portfolio action; strategies/size.composite_position_size |
python scripts/smoke_structured_output.py |
smoke structured output |
python main.py |
minimal Python API demo |
- batch.py:
--symbols(required)--date--workers(1-4 default=capped, viabatch.effective_workers;TRADINGAGENTS_MAX_WORKERSraises the cap)--depth(shallow|medium|deep)--analysts(market|social|news|fundamentals)--vendor(default|moomoo|yfinance|eodhd|tiingo)--verify(advisory LLM report-verification pass post-run ->verify_flags.jsonin each report tree). - pipeline.py:
--universe(tickers|top-losers|heat-proxy|top-movers-massive)--file--top--limit--market--movers-count--min-mcap--price-min--pe-max--workers(capped viabatch.effective_workers)--analysts--depth--vendor.-u/--universe(eodhd-us default | tickers | top-losers | heat-proxy | eodhd-losers | moomoo-screen)--market-n/--movers-count--min-mcap--price-min--pe-max--min-avg-vol--min-atr-pct--max-mcap--min-eps-yoy--min-rev-yoy--min-roe--max-chg5d--max-rsi--max-debt-assets--dip-days--pb-min--pb-max--exchanges(NYSE,NASDAQ default, '' = off)--sector-rank--revision--inst-accum--intraday--enrich-sector--enrich-rev--enrich-inst--scan(value|trend-pullback|breakout|momentum|momentum-score|swing|vcp|value-dip|all)--value-dip-loose--knife-z--out-dir--rank--enable-float--journal--alloc. - action_report.py:
--basket(SYM=W,SYM=W override; default configrisk_basket_weights)--reports-dir(defaultreports/)--date--llm(judge UNKNOWN conditions)--json--dry-run--out-dir(defaultaction_reports/, keep-only-newest). - positions_to_basket.py:
--positions DIR(default repopositions/) + explicit CSV files;--apply(backup.env.bak, rewrite the two basket lines);--min-value/--exclude SYM(repeatable);--write-book-json(dollar book topositions/book_value.json, gitignored);--json.
docs/AGENT_ONBOARDING.md- environment runbook (interpreter, quirks, layout)docs/howto_end_to_end.md- screener -> pipeline -> reports walkthroughdocs/EasyManual.md- the same project explained for a teenager (no finance degree needed): web app first, then terminal, reading reports, glossarydocs/developer/- full developer map (topology, graph/workflow, dataflow, strategies, agents/tools, entrypoints, persistence, dev guide, tests layout, Massive)docs/massive_integration.md- the Massive.com add-on plan + entitlement mapdocs/pre_market_review.md- design + implemented (choice (a)): a pre-market overnight reviewer that CONFIRMs / REVISEs / REJECTs a prior close-time decision against measured deltas (gap, catalyst window, re-anchored tranche/contract, governor) —strategies/pre_market.py(deterministic arbiter + paper-book ledger) +scripts/pre_market_review.py(standalone pre-open; real pre-market quote, planned-level re-anchor),scripts/nightly_review.py(batch-summary driver;--mode recentreviews each symbol's newest report folder instead, covering CLI/API runs that never write a batch summary),scripts/decision_history.py(decision series), and the opt-inbatch.pysame-night step (enable_pre_market_review).docs/developer/11-agent-decision-tools.md- the six decision-grounding tools implemented for the analyst LLMsdocs/developer/12-data-providers.md- the 13 data providers/sources and per-category vendor chainsStrategies/*- strategy plans and specs (index:Strategies/index.md)docs/design_multi_agent_debate.md- research + design (no code) for the bull/bear research debate: two-layer judiciary (deterministic L1 gates before a blind dimensioned L2 LLM judge), heterogeneous per-role models + capability matrix, FSM orchestrator + canonical wire schemas (DebaterTurnPayload/L1DeterministicResult/L2JudgeDimensionedRubric) fromStrategies/Multi_Agents_Debate.md, phased P0-P6 allenable_*-flagged OFF.docs/design_quantlib_lean_enhancements.md- research-to-design: deep study of QuantLib (pricing/measurement rigor) + Lean (framework/operational rigor) mapped to concrete deterministicstrategies/*enhancements, phased (highest ROI first) with quick-wins verdict.docs/design_nautilus_trader_enhancements.md- research-to-design: deep study of NautilusTrader (execution/evaluation rigor) mapped to a backtest harness, consistent risk sizing, statistics and config validation - implemented (see CHANGELOG).docs/design_webull_data_provider.md- provider study (2026-09-16, design only): Webull OpenAPI mapped ontodata_vendors- a US-only bars + fundamentals vendor whose statements are natively basis-tagged (fiscal_year/fiscal_period/end_date/publish_date), the entitlement and 2FA-token constraints, and a phased plan.docs/design_longbridge_data_provider.md- provider study (2026-10-08, design only): the Longbridge (Longport) OpenAPI read against the chains in force - US + HK + CN, a near-total US overlap, a unique HK/CN surface with no consumer, a Non-Commercial licence, and the probed finding that the supplied app key + secret cannot authenticate withoutLONGBRIDGE_ACCESS_TOKEN(401001 token empty).docs/design_external_research_leads.md- lead-source register (2026-10-08): a public "awesome-quant"-style list checked against this repo - the two process patterns adopted (the eligibility gate and the anti-doc-sprawl rule, nowdocs/AGENT_ONBOARDING.mdsection 0 rules 11-12, plusscripts/validate_registers.pyand the SSRF-safescripts/url_probe.pybehind a weekly workflow), and every lead's disposition: which are already implemented here, which produced a change (rule_eval's baseline lift), and which are blocked on an owner decision.docs/design_moomoo_unused_api_surface.md- provider study (2026-09-18, design only): the unused moomoo OpenAPI surface enumerated - 103 unused data methods ofOpenQuoteContext's 166 public callables, every one read and ~35 live-probed against OpenD.get_market_snapshotreturns 142 columns for a whole batch in one call (valuation block, pre/after/overnight sessions, short interest, ETF NAV/premium); batch IV rank/percentile/HV plus a 251-row IV-HV series; a valuation percentile and peer distribution; an independent fair value. Records the all-or-nothing batch constraint, the rule-15 overlap to settle before adopting the valuation fields, and a P0-P5 plan behind the newenable_moomoo_snapshot.docs/design_finnhub_yfinance_unused_surface.md- provider study (2026-09-18, design only): the unused finnhub and yfinance surfaces - 108 offinnhub.Client's 117 methods and ~31yfinance.Tickerproperties, both diffed against the tree and live-probed (61 + 32 calls). The headline:Ticker.eps_trendsupplies the five-level estimate series thatanalyst_revisions.estimate_change_indexdocuments itself as never having, and which no production caller ever supplies. Records the finnhub free-tier wall (12 of 61 work, 48 return 403, all listed), the two rule-15 overlaps that block the tempting adoptions, and a P0-P5 plan behind the newenable_analyst_estimates.docs/design_eodhd_unused_surface.md- provider study (2026-09-19, design only): the unused EODHD surface, probed live against the repo's own key across five rounds (~100 URLs). The supplied 68-endpoint catalog is materially wrong about reachability: 8 paths are already called by the tree, 9 are reachable and unused, 20 are plan-gated (403) and ~9 are not found at any probed path. Records the/ust/*Treasury family found by guessing a path the catalog did not list, the 422-vs-403 probe that separated/calendar/dividends(authorized but plan-gated) from a wrong path, and the rule-15 analysis that rejects six of the nine reachable endpoints as second producers. A P0-P3 plan behind the newenable_eodhd_rates; P0 is/ust/real-yield-rates, the one endpoint that changes what an existing number means. P0 is BUILT (2026-09-19):eodhd.real_yield_points_eodhd/::get_real_yield_rates_eodhd(TIPS real yields) and::inflation_expectation_eodhd(the single producer of nominal − real, both legs' dates and the basis printed), withfederal_reserve.treasury_curve_pointsrefactored to a structured read the existing renderer now draws from. P1 and P2 are BUILT (2026-09-27):eodhd.get_bill_auction_rates_eodhd(Treasury bill auction detail — discount/coupon,maturity_date,cusip) andeodhd.map_identifiers_eodhd(FIGI / LEI / CUSIP; the vendor's CIK is returned only as a labelledcik_cross_check, sincesec_edgar._cik_forowns that join). P3 (/eod-bulk-last-day) and P4 (/news-word-weights) are BUILT (2026-10-01) at the owner's instruction: the first as a batched transport wired as a session + coverage cross-check (bulk_last_day_index_eodhd), never a price contributor (rule 15); the second as a news-term reference read (news_word_weights_eodhd, ~40 s/call, feeds no score —/sentimentsowns the news-derived number). The per-symbol price-chain swap is still not built: no measurement shows that path is the bottleneck. All behind the oneenable_eodhd_ratesgate, read byscripts/value_screener.py --rates. Gate off by default. P5 is BUILT (2026-10-06): the refusal typing is corrected (401 names the token, 403 the entitlement — a 403 is a PLAN gate on a valid key, established live on eight datasets), a 402 is raised on the first attempt instead of being retried twice as a rate limit,Retry-Afteris honoured on a 429 and the 5xx path backs off;eodhd.resolve_symbol_eodhdreads the one newly-reachable endpoint,/search, and selects a listing by rule, never row 0 (isPrimaryalone selects nothing — everyTSMrow reports False — and the vendor's spelling is never re-edited:BRK.B→BRK-B.US); and the newdataflows/eodhd_quota.pyreadsGET /user— the one endpoint EODHD does not count against the daily limit — to report the plan's allowance and the run's own weighted spend (5×/10× for the CBOE, market-cap and insider feeds).docs/design_openbb_enhancements.md- research-to-design: deep study of OpenBB (typed provider envelopes, self-describing REST/CLI/MCP surface, quantitative/econometrics/technical toolkit, Tauri desktop + SPA product surfaces) mapped to TradingAgentsdataflows/+strategies/andtrading_web(watchlist/grid/charts/credentials/presets/MCP), phased with a quick-wins verdict.