How a LangGraph state machine orchestrates analysts, debators, and managers to produce a grounded, point-in-time trading rating.
Ask a single LLM to analyze a stock and it will happily invent a price, leak next week’s earnings into a backtest, and hand you one confident opinion with no counterargument. TradingAgents, an actively developed Python framework at github.com/tauricresearch/tradingagents, treats that failure mode as an orchestration problem rather than a prompting problem. It simulates a trading firm—specialist analysts, bull/bear researchers, risk debators, and a portfolio manager—as a LangGraph state machine, with each agent grounded in verified, point-in-time data.
The codebase is substantial: roughly 100k lines across agent factories, data-vendor adapters, and graph orchestration, sitting at v0.4.0 with a CI gate and a large contributor base. APIs still shift between releases, so treat it as a research scaffold, not a stable dependency.
By the end you will understand how the graph routes control flow between analyst tool loops and structured debate rounds, how the data layer prevents look-ahead bias, and where to extend the pipeline with your own LLM providers or data vendors.
What It Is and What It Isn’t

TradingAgents is a Python framework that runs a team of LLM agents—analysts, bull/bear researchers, a trader, risk debators, and a portfolio manager—over a LangGraph state machine to produce a 5-tier trading rating for a given ticker and date. It fetches market, news, and social data through pluggable vendors and persists decisions for later reflection.
It is not a live trading bot or execution engine. The README explicitly describes the simulated exchange as a proposal; the output is a research decision, not an executable order. It is also not a backtesting library with a fixed strategy. Results vary run-to-run because LLM sampling and live data move.
In the software stack, TradingAgents sits above LLM provider SDKs (OpenAI, Anthropic, Google) and data vendors (yfinance, Alpha Vantage, FRED, Polymarket), and below user code that consumes the decision. A caller invokes TradingAgentsGraph.propagate() or the CLI and receives markdown plus a parsed rating.
The project is active at v0.4.0 (Aug 2026) with a CI gate and large contributor base, but remains 0.x so APIs shift between releases. Unlike a single-LLM prompt, it decomposes analysis into specialist roles with tool access and structured debate, trading latency and cost for a more thorough, auditable reasoning trail.
Architecture: A LangGraph State Machine for a Trading Firm




The pipeline is a LangGraph StateGraph that encodes a fixed sequence of specialist roles. Selected analysts (market, sentiment, news, fundamentals) each run an LLM-with-tools loop. Bull and bear researchers then debate, a research manager synthesizes an investment plan, a trader proposes a transaction, three risk debators argue, and a portfolio manager issues a final 5-tier rating.
| |
Control plane. TradingAgentsGraph (in tradingagents/graph/trading_graph.py) is the orchestrator: it builds LLM clients and tool nodes, compiles the graph, and runs propagate(). GraphSetup constructs the StateGraph from analyst execution plans and debate/risk nodes. ConditionalLogic decides analyst tool-loop continuation, debate speaker order, and risk-debate speaker order. Every conditional edge shares a complete path map, so a router fall-through cannot crash the graph mid-run.
Runtime. Agent factories under tradingagents/agents/ return graph node functions that prompt LLMs. The LLM client factory creates provider-specific clients from a declarative registry. The research manager, trader, and portfolio manager use with_structured_output with a free-text fallback on exception.
Data plane. tradingagents/dataflows/interface.py routes tool calls to configured vendors with ordered fallback—no silent fallback to unselected vendors. The yfinance layer fetches OHLCV, indicators, fundamentals, and news with caching and look-ahead prevention. Social sentiment fetchers (Reddit RSS, StockTwits) trim results to the analysis window.
Storage. TradingMemoryLog is an append-only markdown decision log with pending/resolved entries and reflections, using atomic writes via temp-file plus os.replace(). A per-ticker SQLite checkpointer enables crash-resume; its thread ID hashes ticker, date, and graph-shape signature so a resume under different choices starts fresh.
Control flow details. The analyst tool loop continues while the last message has tool_calls. Debate terminates when count reaches 2*max_debate_rounds; risk debate when count reaches 3*max_risk_discuss_rounds. Structured-output calls fall back to free text on any exception. Data flows from instrument identity resolution through analyst data fetching, social sentiment pre-fetching, debate and synthesis, then trade proposal and risk debate, ending with decision logging and reflection.
Key Features: What Problems They Solve

Multi-agent debate pipeline. The framework runs bull/bear researchers and aggressive/conservative/neutral risk debators in structured rounds before a final portfolio decision. This mimics real trading-firm dynamics: the bull case is stress-tested against the bear case, and the risk team argues from three distinct risk appetites. The result is a decision that has survived adversarial scrutiny rather than a single model’s unexamined opinion.
Look-ahead-safe data layer. Every data source is filtered to the analysis date. OHLCV rows after curr_date are dropped, FRED queries pin realtime_start/realtime_end to the as-of date so revisions published later never leak into a historical run, and social posts are trimmed to the analysis window. This prevents backtests from seeing future prices or data revisions—the difference between trustworthy research and quietly invalid results.
Deterministic verification snapshot. The market analyst must call get_verified_market_snapshot before making price or indicator claims. This grounds exact numbers in real fetched data rather than letting the LLM recite a plausible-but-wrong figure from training data. Numeric hallucination is the failure mode most likely to poison a downstream decision, and this mandatory tool call is a direct countermeasure.
Pluggable LLM providers. Fifteen-plus providers—OpenAI, Anthropic, Google, xAI, DeepSeek, Qwen, GLM, MiniMax, OpenRouter, Ollama, Azure, Bedrock, and any OpenAI-compatible endpoint—are registered declaratively with a per-model capability table. Client subclasses consult that table to suppress or add API parameters, so supporting a new provider never requires an if-ladder through the codebase. Users switch models via config or environment variables, not code changes.
Structured output for decision agents. Research Manager, Trader, and Portfolio Manager produce typed Pydantic objects via with_structured_output, with a free-text fallback for providers lacking structured-output support. Downstream consumers—the memory log, reports, rating extraction—read consistent typed fields regardless of which provider ran. The fallback path logs a warning and retries once as free text, so a provider limitation degrades gracefully instead of crashing the pipeline.
Persistent decision memory with reflection. Each decision is appended to a markdown log. On a later same-ticker run, the framework fetches realized returns, writes a reflection on what worked, and injects those past lessons into the Portfolio Manager prompt. The system carries forward experience across runs instead of treating each analysis as amnesia.
Checkpoint resume. LangGraph state is saved after each node to a per-ticker SQLite database. A crashed run resumes from the last completed node rather than restarting a long multi-LLM pipeline from scratch. The thread ID hashes ticker, date, and a graph-shape signature, so changing analyst selection or debate depth invalidates stale checkpoints—preventing a resume under mismatched configuration.
Exact vendor chain. The configured vendor list is the resolution chain. If a vendor lacks a required method, the call raises rather than silently falling back to an unselected vendor.
Use Cases: Where It Shines and Where It Doesn’t
A quant researcher studying how multi-agent LLM debate affects trading decisions on a historical date is the primary fit. The framework pins the analysis date, filters OHLCV and social data to prevent look-ahead, and records point-in-time memory with resolution dates. This makes backtests more trustworthy than ad-hoc single-LLM prompting.
A developer experimenting with different LLM providers or local models for financial analysis will find the provider registry and model catalog convenient. Switching between OpenAI, Anthropic, Gemini, Ollama, or any OpenAI-compatible endpoint happens via config keys or environment variables, without code changes.
Analyzing non-US tickers or crypto assets works through symbol normalization that maps broker symbols to Yahoo conventions. The benchmark map auto-selects regional indices for alpha calculation, so the framework handles international instruments without manual index specification.
The framework is a poor fit for production systems that automatically trade real money. The output is a research decision, not an executable order; the README explicitly disclaims financial advice and notes non-determinism. Similarly, it does not serve deterministic, reproducible backtests of a fixed strategy—LLM sampling and live social/news data make results vary run-to-run.
One caveat applies even to historical analysis: live social and news sources reflect “now,” not the pinned analysis date. Only the price and indicator window is truly historical, so sentiment data in a backtest may include information that would not have been available at the time.
Interface and Usage: Running an Analysis
The primary programmatic entry point is TradingAgentsGraph. Its constructor builds LLM clients, tool nodes, and compiles the LangGraph; propagate() runs the full pipeline and returns a tuple of the final state dict and the parsed decision rating.
| |
A minimal run requires only a ticker and date. The example below creates the graph with default configuration and prints the decision markdown returned by propagate:
| |
Configuration comes from DEFAULT_CONFIG, a dict whose keys include llm_provider, deep_think_llm, quick_think_llm, max_debate_rounds, and checkpoint_enabled. Every key is overridable via a TRADINGAGENTS_* environment variable. To customize models and debate depth, copy the dict and override the relevant keys before constructing the graph:
| |
Checkpoint resume is opt-in via the same config dict. Setting config['checkpoint_enabled'] = True enables per-ticker SQLite checkpoints so a crashed run resumes from the last completed node. The checkpoint thread ID hashes ticker, date, and graph-shape choices, so changing analyst selection or debate rounds invalidates the checkpoint.
The CLI (tradingagents analyze or python -m cli.main) prompts interactively for ticker, date, provider, and analysts, then streams node output. Two additional methods round out the API: save_reports(final_state, ticker, save_path) writes per-section markdown files, and clear_checkpoint_on_success() drops the checkpoint after a clean completion.
One gotcha: a missing API key for a required provider raises at client construction. Key-optional providers such as ollama and openai_compatible send a placeholder key instead.
How It Compares: TradingAgents vs. FinGPT and General Agent Frameworks
The table below positions TradingAgents against the two main alternatives: FinGPT, which targets financial NLP via fine-tuning, and general multi-agent frameworks (AutoGen, CrewAI, LangGraph) that developers adapt to trading. This reflects general knowledge as of the analysis date and may be out of date; verify current capabilities before making architecture decisions.
| Axis | TradingAgents | FinGPT | AutoGen/CrewAI/LangGraph trading examples |
|---|---|---|---|
| Primary use case | Multi-agent LLM trading decisions | LLM financial NLP/fine-tuning | General multi-agent apps |
| Agent architecture | Fixed firm roles + debates | Varies, often single-model | Flexible, user-defined |
| Data vendor abstraction | Configurable chain, no silent fallback | Not core | Not provided |
| Look-ahead prevention | Point-in-time filters everywhere | Not addressed | Not addressed |
| LLM provider support | 15+ providers incl. local | Fine-tune any open model | Depends on framework |
| Persistence / memory | Decision log + checkpoint resume | Model checkpoints | Framework-level state |
| Deployment model | Python lib / CLI / Docker | Python lib / Hugging Face | Python lib / services |
| Maturity | Active, v0.4.0, large contributor base | unknown | unknown |
| Extensibility | Add vendors/providers via tables | Model fine-tuning | General agent logic |
| Performance characteristics | unknown | unknown | unknown |
The key differentiators are domain specificity. TradingAgents ships fixed firm roles, a data-vendor routing layer with no silent fallback, and financial look-ahead controls applied across every data source. FinGPT offers an established research line and model weights, but it is not a multi-agent pipeline and provides no point-in-time handling. General frameworks are flexible for non-trading tasks but require you to build the trading-specific scaffolding—data vendors, debate logic, and memory—yourself.
Performance characteristics are marked unknown for all three because no benchmark data exists in the analysis. Confidence in the FinGPT comparison is low; it is a broad research program rather than a single comparable artifact.
Notable Techniques Worth Stealing
Several implementation patterns in TradingAgents generalize well beyond trading. The first is the per-model capability table in tradingagents/llm_clients/capabilities.py. Rather than scattering model-name conditionals through client code, a frozen dataclass declares which API quirks each model family has:
| |
Exact-ID matches take precedence, then regex patterns, then a default. Adding a new provider quirk means editing the table, not the dispatch logic. The pattern-match fallback (^deepseek-v\d, ^MiniMax-M\d) gives forward compatibility for unlisted model versions.
The message-clear node in tradingagents/agents/utils/agent_utils.py solves a subtle failure mode: after an analyst finishes, the full message history is removed and replaced with a context-anchored placeholder rather than a bare “Continue”:
| |
Some OpenAI-compatible providers interpret “Continue” literally as the user task and produce output about the word “continue.” Anchoring the placeholder to the instrument and date keeps the next agent on-task even when the provider treats it as a standalone request.
Other patterns worth copying: FRED queries pin realtime_start/realtime_end to the as-of date so historical runs see only revisions published by then; a regex over NFKC-normalized text extracts the 5-tier rating, returning a visible REVIEW sentinel on parse failure rather than a silent Hold; and checkpoint thread IDs hash ticker plus date plus graph-shape choices so a resume under different analyst selections cannot reuse a mismatched checkpoint.
What to take away
TradingAgents demonstrates that the hard problems in LLM-driven trading are not about prompt quality but about data discipline and process structure. The most transferable lesson is its point-in-time discipline: filtering every data source to the as-of date, pinning FRED vintages, and trimming social feeds to the analysis window. Any financial LLM pipeline that touches historical data should copy this pattern before worrying about agent roles.
The second lesson is the value of a deterministic verification snapshot. Forcing the market analyst to ground price and indicator claims in a fixed, tool-fetched dataset is a concrete antidote to numeric hallucination. The declarative per-model capability table is also worth stealing — it beats hardcoded if-ladders when supporting many providers with divergent API quirks.
Be honest about the limits. The framework does not execute trades, and its output is non-deterministic even at fixed temperature. Live social and news sources reflect “now,” not the pinned date, so only price data is truly historical. Runtime cost in LLM calls and tokens is unmeasured, and the simulated exchange mentioned in the README may be conceptual only.
The repository is at github.com/TauricResearch/TradingAgents. It is active, at v0.4.0, and the APIs shift between releases — pin your version.
Further diagrams




