This release bundles substantial work since v0.2.3:
- Structured-output Research Manager, Trader, and Portfolio Manager
(canonical with_structured_output pattern, single LLM call per agent,
rendered markdown preserves the existing report shape).
- LangGraph checkpoint resume for crash recovery (--checkpoint flag).
- Persistent decision log replacing the per-agent BM25 memory, with
deferred reflection driven by yfinance returns + alpha vs SPY.
- DeepSeek, Qwen, GLM, and Azure OpenAI provider support; dynamic
OpenRouter model selection.
- Docker support; cache and logs moved to ~/.tradingagents/ to fix
Docker permission issues.
- Windows UTF-8 encoding fix on every file I/O site.
- 5-tier rating consistency (Buy / Overweight / Hold / Underweight / Sell)
across Research Manager, Portfolio Manager, signal processor, memory log.
Plus the small quality items in this commit:
1. Suppress noisy Pydantic serializer warnings from OpenAI Responses-API
parse path by defaulting structured-output to method="function_calling"
(root-cause fix, not a warnings filter — same typed result, no warnings).
2. Ship scripts/smoke_structured_output.py so contributors can verify
their provider's structured-output path with one command.
3. Add opt-in memory_log_max_entries config — when set, oldest resolved
memory log entries are pruned once the cap is exceeded; pending
entries (unresolved) are never pruned.
4. backend_url default changed from the OpenAI URL to None so the
per-provider client falls back to its native endpoint instead of
leaking OpenAI's URL into Gemini / other clients.
CHANGELOG.md added with the full v0.2.4 entry. 92 tests pass without API keys.
Extends the canonical structured-output pattern from the Portfolio Manager
to the other two decision-making agents. Each of the three agents now
returns a typed Pydantic instance via llm.with_structured_output() in a
single primary call, and a render helper turns the result into the same
markdown shape downstream agents and saved reports already consume.
- ResearchPlan: 5-tier recommendation, conversational rationale, concrete
strategic actions for the trader.
- TraderProposal: 3-tier action (transaction direction is naturally Buy /
Hold / Sell — position sizing happens later at the Portfolio Manager),
reasoning, and optional entry_price / stop_loss / position_sizing.
Rendered output preserves the trailing "FINAL TRANSACTION PROPOSAL:
**BUY/HOLD/SELL**" line for backward compatibility with the analyst
stop-signal text.
- PortfolioDecision: 5-tier rating, executive summary, investment thesis,
optional price_target / time_horizon (unchanged).
The shared try-structured-then-fallback pattern is extracted into
tradingagents/agents/utils/structured.py (bind_structured +
invoke_structured_or_freetext) so all three agents go through the same
code path and log the same warning when a provider lacks structured
output and the agent falls back to free-text generation.
Net effect for users: every saved markdown report (research/manager.md,
trading/trader.md, portfolio/decision.md) now has consistent section
headers across runs and providers, easier to scan.
Net effect for the runtime: the rating extraction round-trip is gone —
the rating comes from the structured response itself, not a second
LLM call. SignalProcessor was already simplified to a heuristic adapter
in the previous commit.
11 new tests in tests/test_structured_agents.py cover the Trader and
Research Manager render functions, structured-output happy paths, and
free-text fallback. Full suite: 88 tests pass in ~2s without API keys.
Three related changes that take the rating pipeline from heuristic-only
to type-safe at the source.
1) Research Manager prompt now uses the same 5-tier scale (Buy /
Overweight / Hold / Underweight / Sell) as the Portfolio Manager,
signal_processing, and the memory log. The prior 3-tier wording
(Buy / Sell / Hold) was the only remaining inconsistency in the
pipeline.
2) Centralise the 5-tier vocabulary and the heuristic prose-rating
parser into tradingagents/agents/utils/rating.py. Both the memory
log and the signal processor now share the same parser instead of
duplicating regex and word-walker logic.
3) Make structured output a first-class part of the Portfolio Manager's
primary call. The PM uses llm.with_structured_output(PortfolioDecision)
so each provider's native structured-output mode (json_schema for
OpenAI/xAI, response_schema for Gemini, tool-use for Anthropic,
function_calling for OpenAI-compatible providers) yields a typed
Pydantic instance directly. A render helper turns that instance back
into the same markdown shape downstream consumers (memory log, CLI
display, saved reports) already expect, so no other code has to know
the PM now produces structured output. Providers without structured
support fall back gracefully to free-text + the deterministic
heuristic.
The previous SignalProcessor had been making a second LLM call to
re-extract the rating from the PM's prose; that round-trip is now
eliminated. SignalProcessor is a thin adapter over parse_rating(),
makes zero LLM calls, and stays for backwards compatibility with
process_signal() callers.
Schema (PortfolioDecision) captures rating + executive_summary +
investment_thesis + optional price_target + time_horizon, with field
descriptions doubling as output instructions. Agent prose remains the
primary artifact; structured output is layered onto the PM only because
it is the one agent whose output has machine-readable downstream
consumers.
15 new tests cover the heuristic parser (markdown-bold edge cases that
had no coverage before), the structured PM happy path, the free-text
fallback path, and that SignalProcessor never invokes the LLM. Full
suite: 77 tests pass in ~2s without API keys.
The previous per-agent BM25 memory was effectively dead code — its only
caller was a commented-out line in main.py. Replace it with a single
append-only markdown decision log driven by the propagate() lifecycle.
Lifecycle:
- store_decision() appends a pending entry at the end of every run
- _resolve_pending_entries() runs at the start of the next same-ticker
run, fetches yfinance returns + alpha vs SPY, and writes one LLM
reflection per resolved entry through an atomic temp-file rename
- Portfolio Manager consumes state["past_context"] (5 most recent
same-ticker entries plus 3 cross-ticker reflection-only excerpts)
Storage at ~/.tradingagents/memory/trading_memory.md
(override: TRADINGAGENTS_MEMORY_LOG_PATH).
Tag schema:
- Pending: [YYYY-MM-DD | TICKER | Rating | pending]
- Resolved: [YYYY-MM-DD | TICKER | Rating | +X.X% | +Y.Y% | Nd]
Removes rank-bm25 dependency and the legacy reflect_and_remember()
plumbing across reflection.py, trading_graph.py, and the agent factories.
49 new tests in tests/test_memory_log.py cover the storage, deferred
reflection, prompt injection, and legacy-removal paths. Full suite
(58 tests) passes in under 2 seconds without API keys.
LLMs (especially smaller models) sometimes pass multiple indicator
names as a single comma-separated string instead of making separate
tool calls. Split and process each individually at the tool boundary.
- Replace FinnHub with Alpha Vantage API in README documentation
- Implement comprehensive Alpha Vantage modules:
- Stock data (daily OHLCV with date filtering)
- Technical indicators (SMA, EMA, MACD, RSI, Bollinger Bands, ATR)
- Fundamental data (overview, balance sheet, cashflow, income statement)
- News and sentiment data with insider transactions
- Update news analyst tools to use ticker-based news search
- Integrate Alpha Vantage vendor methods into interface routing
- Maintain backward compatibility with existing vendor system
🤖 Generated with [Claude Code](https://claude.ai/code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Added support for running CLI and Ollama server via Docker
- Introduced tests for local embeddings model and standalone Docker setup
- Enabled conditional Ollama server launch via LLM_PROVIDER