The CLI validated, normalized, and classified tickers with its own logic that
diverged from the data layer: it rejected '=' symbols like GC=F (#980),
classified BTCUSD as a stock (#981), and accepted unpriceable BTC-USDT (#982).
Route the CLI through normalize_symbol (now mapping USDT/USDC crypto quotes to
Yahoo's -USD pair), so validation, classification, and pricing agree.
resolve_instrument_identity and the reflection return lookup queried Yahoo with
the raw ticker, so broker/forex/commodity symbols (XAUUSD, BTCUSD, EURUSD)
failed identity or could mismatch the priced instrument even though the price
path already normalized them. Route both through normalize_symbol (#983, #984).
GitHub Actions: pytest across Python 3.10-3.13, a clean-install import smoke
that catches undeclared runtime deps, and a strict Ruff gate (standard rule set)
scoped to the files each PR changes. Declares python-dotenv (imported by the CLI
but previously undeclared) and adds a [dev] extra. Recommends Python 3.12 for
setup, verified from a clean isolated install.
Setting the LLM env vars now skips the matching CLI selection step and uses
the value, so OpenAI-compatible endpoints (opencode, LM Studio, etc.) and
unattended runs work without prompting. Unset vars are chosen interactively
as before.
TRADINGAGENTS_LLM_PROVIDER -> skips provider step (still verifies API key)
TRADINGAGENTS_LLM_BACKEND_URL -> custom endpoint (else provider default)
TRADINGAGENTS_DEEP_THINK_LLM / _QUICK_THINK_LLM -> skips model step
TRADINGAGENTS_OUTPUT_LANGUAGE -> skips language step
Builds on the existing TRADINGAGENTS_* config overrides (which already feed
DEFAULT_CONFIG); this wires the CLI to honor them instead of re-prompting.
Analyzing a symbol Yahoo Finance does not recognize (e.g. XAUUSD+) could
produce an invented price instead of an error. The agent now either prices
the correct instrument or clearly reports that data is unavailable.
Ticker support:
- Commodities/forex/crypto resolve to the symbol Yahoo actually serves, so
you can enter the common form and it just works:
XAUUSD / XAUUSD+ / GOLD -> GC=F (gold)
USOIL -> CL=F (WTI crude)
EURUSD -> EURUSD=X
BTCUSD -> BTC-USD
SPX500 / NAS100 -> ^GSPC / ^NDX
Native Yahoo symbols (AAPL, GC=F, ^GSPC) keep working unchanged. New
instruments are added by extending the alias table.
Reliability:
- Unknown or delisted symbols now return a clear "data unavailable" result
the agent reports verbatim, instead of a value the model fills in.
- A failed fetch no longer leaves a broken symbol cached until the cache is
cleared by hand.
A-shares already resolve through the Yahoo Finance vendor (Shanghai .SS,
Shenzhen .SZ) with correct identity and indicators; add the SSE/SZSE
composite benchmarks so their alpha isn't measured against SPY, and
document the exchange-suffix tickers we support (incl. A-shares, crypto).
Adds a cross-provider temperature config (and TRADINGAGENTS_TEMPERATURE),
forwarded to every LLM client when set, so runs can be made less variable
on models that honor it. Adds a README "Reproducibility" section that
separates the sources of run-to-run variation, what users can control
(temperature, non-reasoning model, pinned date), and what is inherent to
LLM-driven analysis, and notes that the identity and verified-data fixes
already removed the "different companies / fabricated prices" variance.
#178#168
The market analyst could confabulate exact figures — citing a Bollinger
band or a "historically validated bounce" the data doesn't support (#830).
Add a deterministic get_verified_market_snapshot tool (latest OHLCV row,
common indicators, recent closes) the analyst must consult and treat as
the source of truth for any exact price/indicator claim, and instruct it
not to assert historical validation or support bounces without tool-backed
dates and prices.
#830
The analyst emitted free-form prose, so its sentiment header varied by
provider and run and downstream consumers needed drifting regex. Extend
the structured-output pattern the trio already uses: a SentimentReport
schema (band + 0-10 score + confidence + narrative) rendered to a
deterministic header, with a free-text fallback for providers that lack
native structured output.
#796
yfinance 1.4.0 regressed the daily-download index to unnamed, so
reset_index() produced an "index" column instead of "Date" and every
stockstats indicator silently failed (no SMA/RSI/MACD/Bollinger/ATR).
Verified across versions: 1.2.0 / 1.3.0 / 1.4.1 name it "Date"; only
1.4.0 is broken. Pin to >=1.4.1 (the upstream fix) and normalize the
date column defensively so a non-"Date" index can't silently drop
indicators on any build.
#890
Reddit blocks the anonymous JSON search endpoint, which silently emptied
the sentiment analyst's Reddit source. Fall back to the public RSS search
feed when JSON fails. RSS lacks score/comment counts, so those posts are
marked "via RSS feed" rather than shown with fake zeros.
#862
Agents had no ground-truth ticker→company mapping, so the market analyst
could pattern-match a price chart to the wrong company (e.g. TOTDY read as
"TotalEnergies"), and every downstream agent inherited the bad framing.
Resolve identity once at run start via a cached, fail-open yfinance lookup
and inject company/sector/exchange into the shared instrument context that
all twelve agents consume, with an explicit do-not-substitute instruction.
Resolution runs on both the propagate() and CLI entry points.
Also replaces the bare "Continue" message-clear placeholder, which some
OpenAI-compatible providers interpreted as the user task, with a
context-anchored placeholder carrying the resolved identity and date.
#814#888
Haiku 4.5 rejects the effort parameter with 400. AnthropicClient.get_llm()
now drops effort when the model isn't in the supported set (Opus 4.5+,
Sonnet 4.5+, mythos-preview). Forward-compat regex catches future
claude-{opus,sonnet}-X-Y releases automatically; Haiku and unknown
models stay excluded conservatively.
14 tests cover Haiku exclusion, current Opus/Sonnet inclusion, future-
version inheritance via pattern, mythos-preview, unknown-default
exclusion, and other passthrough kwargs surviving the effort-skip path.
MinimaxChatOpenAI unconditionally set reasoning_split=True, but the
kwarg is only valid on M2.x reasoning models. The openai SDK's strict
kwarg validation raised TypeError for Coding Plan and any other non-
reasoning MiniMax model.
Adds requires_reasoning_split to ModelCapabilities, gates the payload
injection on it, and only sets True for _MINIMAX_THINKING (M2.x exact
IDs and the ^MiniMax-M\d forward-compat pattern). Same shape as the
existing supports_tool_choice gate.
Regression tests cover both halves: M2.x models still receive the flag,
non-reasoning MiniMax models do not.
- analyst_execution.py: rename "Social Analyst" / "Msg Clear Social"
to "Sentiment Analyst" / "Msg Clear Sentiment" to match v0.2.5.
- conditional_logic.should_continue_social returns the renamed route.
- TradingAgentsGraph.propagate accepts asset_type and threads through
to Propagator.create_initial_state.
- Regression test on the Sentiment Analyst label.
Verified end-to-end (NVDA stock + BTC-USD crypto) on gpt-5.4-mini.
Headline themes in v0.2.5:
- Sentiment Analyst grounded in real data. Renamed from social_media_analyst
and redesigned to pre-fetch Yahoo News, StockTwits, and Reddit before the
LLM is invoked, ending the prior fabrication behavior.
- MiniMax provider with full M2.x catalog and dual-region split. Qwen and
GLM also split into international + China regions with separate API keys
and a clean secondary region prompt in the CLI.
- TRADINGAGENTS_* env-var overlay for DEFAULT_CONFIG with type-aware
coercion; .env loading centralized so every entry point sees the user's
keys. Interactive API-key detection prompts and persists missing keys
to .env on the fly.
- OLLAMA_BASE_URL end-to-end for remote ollama-serve, plus a Custom model
ID option in the Ollama dropdown.
- Configurable news-fetch parameters and configurable alpha benchmark for
non-US tickers (.NS / .T / .HK / .L / .TO / .AX / .BO ship with sensible
regional defaults).
- Multi-language output now propagates to every user-facing agent
(researchers, risk debators, research manager, trader) instead of only
the analysts and portfolio manager.
- Model catalog refresh across all providers (GPT-5.5 frontier, Claude
Opus 4.7, Gemini 3.1 Flash-Lite GA, Grok 4.20, Qwen 3.6 line).
- Capability-dispatch table drives provider-specific structured-output
quirks (DeepSeek V4/reasoner and MiniMax M2.x tool_choice rejection,
MiniMax reasoning_split) so the general client stays clean.
- Fixes: ticker path-traversal validation (security), dotenv loading via
console script, reports save bug, exchange-suffix truncation in the
ticker prompt, Docker permission errors, deepcopy config isolation,
max_recur_limit plumbing, clearer missing-API-key error.
See CHANGELOG.md for the full per-item list with issue/PR references.
SPY was hardcoded as the alpha benchmark in both the return-fetch
path and the reflection label, which produced meaningless alpha for
.NS / .T / .HK / .L / .TO / .AX / .BO listings — FX drift between a
local-currency stock and a USD index dominates the spread.
DEFAULT_CONFIG now exposes benchmark_ticker (explicit override) and
benchmark_map (suffix → regional index, with SPY as the empty-suffix
default). TRADINGAGENTS_BENCHMARK_TICKER joins the env-overlay table.
Trading graph resolves the benchmark once per ticker and threads it
through to both _fetch_returns and reflect_on_final_decision, so the
alpha label reads "Alpha vs ^N225" for Tokyo listings, "Alpha vs ^HSI"
for Hong Kong, etc., instead of the misleading "Alpha vs SPY".
The Required APIs section now mentions the default endpoint,
OLLAMA_BASE_URL for remote ollama-serve, ollama pull, and the
Custom model ID dropdown option, replacing the previous one-liner
that left those details implicit.
Users with other models pulled via `ollama pull` (beyond the three
suggested defaults) can now select "Custom model ID" and type any
model name. Matches the same pattern used for DeepSeek, GLM, Qwen,
and MiniMax — the existing _prompt_custom_model_id flow handles the
"custom" value generically, so this is a one-row catalog addition
plus regression coverage.
OLLAMA_BASE_URL now flows through both the CLI dropdown and the
programmatic client (call-time evaluation so tests behave). After
provider selection, the CLI prints the resolved endpoint and marks
when it came from the env var, plus a soft warning when the URL is
missing a scheme or non-default port. Drops the stale "(local)"
suffix from Ollama model labels since the endpoint is now dynamic.
The agent ingests news, StockTwits, and Reddit, but CLI labels, the
README description, and the legacy shim docstring still framed it as
social-media-only. Updates all user-visible surfaces so the name and
the implementation match.
Adds a canonical PROVIDER_API_KEY_ENV mapping (14 providers including
the three dual-region pairs) and an ensure_api_key() helper. When the
selected provider's key is absent from the environment, the CLI prompts
via questionary.password, writes the value to .env via python-dotenv's
set_key (preserves existing lines), and exports it into os.environ so
the run continues without restart. Wired into cli/main.py right after
the region prompts so qwen-cn, glm-cn, and minimax-cn each check their
own region-specific key. openai_client refactored to consult the same
mapping, eliminating its private duplicate of provider→env-var data.
Adds a single _ENV_OVERRIDES table in default_config.py with type-aware
coercion (str/int/bool), so users can switch llm_provider, deep/quick
models, backend URL, output language, debate rounds, and the checkpoint
flag purely via .env. Centralizes load_dotenv in the package __init__
so the overlay applies for every entry point (CLI, main.py, programmatic).
Drops the hardcoded model assignments and duplicate dotenv loads in
main.py and cli/main.py. Verified live with OpenAI and Gemini.
#602
output_language config now propagates to every user-facing agent.
Previously only the four analysts and portfolio manager respected
the setting, producing partial-localization reports with English
debate text interleaved with non-English analyst sections. Verified
live: 7 agents produce Chinese output when config is set to Chinese.
#575
Per-ticker article limit, global article limit, global lookback
window, and macro query list are now read from get_config()
instead of being hardcoded. Tool wrapper get_global_news passes
None defaults so config overrides flow through the LLM-tool path
too. Macro query defaults broadened from 4 US-centric strings to
5 covering Fed, S&P 500, geopolitics, ECB/BOJ/BOE, commodities.
#606#558#562
Pre-fetches news + StockTwits + Reddit via no-auth public endpoints
and injects structured data blocks into the prompt with professional
analysis instructions. Replaces the prompt-vs-tool mismatch that
caused fabricated social-platform content. Backward-compat alias +
"social" CLI key preserved.
#557#607
Zhipu serves GLM under two brands with separate accounts (Z.AI
international vs BigModel China); the CLI URL pointed at one while
the openai_client default pointed at the other. Split into glm +
glm-cn with secondary region prompt (same UX as Qwen + MiniMax).
Catalog adds glm-5-turbo and glm-4.5-air per docs.z.ai.
Qwen and MiniMax each had two main-dropdown entries (intl + CN);
consolidate to one entry per provider and prompt for region as a
secondary step. Internal provider keys (qwen-cn, minimax-cn) and
endpoint routing unchanged. Add qwen3.6-flash to the Qwen catalog
and drop the version-less aliases (qwen-flash, qwen-plus) that
auto-shift their backing model per Alibaba's docs.
#758
xAI's official docs lead with grok-4.20-reasoning and
grok-4.20-non-reasoning across all SDK examples. Replace the prior
grok-4-1-fast-* entries (hyphens where docs use dots, no literal
code example) with the verified grok-4.20 family. Keep grok-4-0709
and grok-4-fast variants that are still referenced.
gemini-3.1-flash-lite is now GA per ai.google.dev. Use the stable
version (fewer rate limits, stronger compat guarantees) instead of
the -preview suffix. Labels mark preview vs GA explicitly.
Opus 4.7 is the current frontier per platform.claude.com (frontier
category, listed first). Demote Opus 4.6 to second deep-tier slot.
Polish quick-tier labels to match official wording; effort docstring
includes 4.7.
GPT-5.5 (Apr 2026, 1M ctx, $5/$30 per 1M) replaces GPT-5.4 as the
catalog flagship. GPT-5.5 Pro replaces 5.4 Pro in the most-capable
slot. GPT-5.4 demotes to previous-gen cost-effective option.
M2.x tool_choice is enum-only (none/auto), so route through the
no-tool_choice dispatch. MinimaxChatOpenAI injects reasoning_split
so <think> blocks stay out of content. Catalog rounded out to the
full official M2.x lineup plus forward-compat regex.
- dataflows/config: deepcopy + one-level dict merge so a partial
set_config doesn't clobber sibling defaults
- graph: thread max_recur_limit from config to Propagator
- openai_client: name the missing env var in the API-key error
#788#764#680
Two regional endpoints (global api.minimax.io, China api.minimaxi.com)
with separate API keys. Models M2.7 / M2.5 plus -highspeed variants,
204K context. Follows the existing provider-preset pattern.
#789#609#577#546#395#378
useradd --create-home creates /home/appuser but not the
.tradingagents subdir, so cache writes fail with PermissionError
when docker-compose mounts a named volume there (the volume
inherits image-dir ownership on first init).
#627#672#771#690#714#723#780#633#773#631
DeepSeek V4 and reasoner reject tool_choice but accept tools.
Route via a per-model capability table that suppresses tool_choice
for thinking-mode models.
#678#689
langgraph-checkpoint 4.0.3 calls Reviver() at module load without
allowed_objects, printing a pending-deprecation warning at every
CLI start. The upstream patch is merged
(langchain-ai/langgraph#7743) but not released; no app-side seam
fixes it. Install a surgical filter in package init (message regex
+ PendingDeprecationWarning category). Remove when we bump past
langgraph-checkpoint 4.0.3.
The typer.prompt-based input could lose .SH/.SZ/.SS/.HK suffixes on
some shells, so exchange-qualified tickers like 000404.SH arrived
truncated to 000404 and failed downstream lookups. Switch to
questionary.text which reads the raw line; keep SPY-on-empty
behavior and validate the allowed character set (alnum, ._-^) up
to 32 chars.
#770
graph.stream() yields per-node deltas, not the full state. Taking
trace[-1] only captured the last node's contribution, so reports
saved to disk were missing every section except the final decision.
Merge all chunks in both the CLI path and trading_graph._run_graph's
debug branch.
#719#736
load_dotenv() with no arguments walks up from site-packages instead
of the user's CWD, so the installed tradingagents console script
silently misses the project's .env. Pass find_dotenv(usecwd=True)
so the search starts from CWD; same treatment for .env.enterprise.
#726#755#612#747#743#753#729#728#751