A production-grade multi-agent investment research platform built with LangGraph and OpenAI. Input a ticker, get a structured buy/hold/sell investment memo generated by 10 specialized AI agents running in parallel against live market data — with a human-in-the-loop compliance gate before any trade is executed.
⚠️ Not investment advice. This is a research and educational system using paper trading only. See Limitations & Compliance below.
🟢 Live Demo → financeagent-langgraph-production.up.railway.app
Enter any equity or crypto ticker. The system automatically detects the asset class, deploys a team of specialist agents in parallel, and produces a structured investment memo covering macro regime, fundamentals, technical analysis, bull/bear theses, valuation, risk, and sentiment — all from live data.
The full pipeline including the Supervisor runs on /analyze. The result is shown immediately. The HITL gate pauses before trade execution — a human must explicitly approve before any paper order is placed.
Equity (AAPL, MSFT, TSLA...) — fundamentals, technical analysis, valuation, bull/bear thesis, risk, sentiment, macro context
Crypto (BTC-USD, ETH-USD, SOL-USD...) — on-chain metrics, sentiment, risk, macro context
Step 1 — Analyze (full pipeline including supervisor):
curl -X POST http://localhost:8000/analyze \
-H "Content-Type: application/json" \
-d '{"ticker": "AAPL", "timeframe": "3mo", "thread_id": "aapl-run-1"}'{
"status": "pending_approval",
"ticker": "AAPL",
"asset_class": "equity",
"supervisor_report": {
"recommendation": "Sell",
"confidence": "High",
"summary": "..."
},
"intermediate": {
"macro": {"regime_label": "Risk-On Tightening"},
"technical": {"signal": "Bearish", "trend": "Sideways", "momentum": "Strong"},
"fundamentals": {"PE_ratio": 35.18, "EPS": 8.26},
"valuation": {"valuation_label": "Overvalued"},
"sentiment": {"sentiment_label": "bearish", "sentiment_score": -0.65}
}
}Step 2 — Approve (executes paper trade if High confidence equity):
curl -X POST http://localhost:8000/approve/aapl-run-1{
"status": "complete",
"supervisor_report": {"recommendation": "Sell", "confidence": "High"},
"trade": {"traded": false, "skipped_reason": "no_position_to_sell"}
}POST /analyze (ticker, timeframe, thread_id)
│
▼
┌─────────────────┐
│ DataFetchAgent │ yfinance + CoinGecko + FRED + RSS
│ │ auto-detects asset class from quoteType
└────────┬────────┘
│
▼
┌─────────────────────┐
│ MacroRegimeAgent │ FRED → Fed funds, CPI, yield curve, unemployment
│ (always runs) │ → macro regime classification
└──────────┬──────────┘
│
┌─────┴──────┐
▼ ▼
EQUITY CRYPTO
┌──────────┐ ┌──────────┐
│Fundament.│ │OnChain │ ┐
│Sentiment │ │Sentiment │ │ parallel
│Risk │ │Risk │ │
│Technical │ └────┬─────┘ ┘
└────┬─────┘ │
│ (equity only)
┌────▼─────┐
│Bull │ ┐
│Bear │ │ parallel
│Valuation │ ┘
└────┬─────┘
│
▼
┌─────────────────────┐
│ SupervisorAgent │ GPT-4.1-mini — synthesizes all outputs
│ (Portfolio Mgr) │ → final buy/hold/sell memo
└─────────────────────┘
│
▼ ⏸ trade_gate (HITL — human approves trade execution)
POST /approve/{thread_id}
│
▼
Paper Trade (Alpaca) + Signal recorded for 30-day eval
Agent count: 10 specialist agents (MacroRegime, Fundamentals, Technical, Sentiment, Risk, Bull, Bear, Valuation, OnChain, Supervisor) + DataFetch as infrastructure node + trade_gate as HITL checkpoint.
| Source | Used For | Asset Class |
|---|---|---|
| yfinance | Price history, fundamentals, news, analyst consensus | Equity |
| CoinGecko | Market cap, volume, developer activity, community score | Crypto |
| FRED API | Fed funds rate, CPI, yield curve spread, unemployment | Both |
| RSS (Reuters, WSJ, Yahoo, SeekingAlpha, MarketWatch) | News headlines for sentiment | Both |
Asset class is detected automatically from yfinance quoteType — no user input required.
Benchmarked across 5 S&P 500 tickers using per-agent instrumented timing (benchmark.py). Production-traced via LangSmith across 65 runs.
| Metric | Value |
|---|---|
| Avg end-to-end latency | 17.60s |
| p50 latency | 16.4s |
| p99 latency | 25.3s |
| Cost p50 | $0.00180 per run |
| Cost p99 | $0.00197 per run |
Per-agent averages:
| Agent | Avg Latency |
|---|---|
| Data Fetch | 3.36s |
| Macro Regime | 0.80s |
| Fundamentals / Sentiment / Risk / Technical (parallel) | 1.42s wall clock |
| Bull / Bear / Valuation (parallel) | 2.63s wall clock |
| Supervisor | 8.93s |
SupervisorAgent accounts for ~51% of total latency — the irreducible sequential bottleneck by design.
Note: benchmark numbers were measured before TechnicalAnalyst was added to the Wave 1 parallel group. Re-run
python benchmark.pyfor updated timings.
| Layer | Technology |
|---|---|
| Agent orchestration | LangGraph (StateGraph, SqliteSaver) |
| LLM — analysis agents | GPT-4.1-nano (9 agents) |
| LLM — supervisor | GPT-4.1-mini (1 agent) |
| Market data | yfinance |
| Crypto data | CoinGecko API |
| Macro data | FRED API (fredapi) |
| Technical indicators | ta (RSI, MACD, MA, ATR, volume) |
| Paper trading | Alpaca Markets API |
| Structured outputs | Pydantic v2 |
| API framework | FastAPI |
| Frontend | React + Vite + Recharts |
| Observability | LangSmith + Python logging |
| Deployment | Docker + Railway |
| Package manager | uv |
src/
├── states/
│ └── financestate.py # FinanceState TypedDict + all Pydantic output models
├── nodes/
│ ├── data_fetch.py # DataFetchAgent — yfinance, CoinGecko, FRED, RSS
│ ├── macro_regime_agent.py # MacroRegimeAgent — FRED macro classification
│ ├── fundamentals_agent.py # FundamentalsAgent — P/E, EPS, revenue growth, D/E
│ ├── technical_analyst.py # TechnicalAnalyst — RSI, MACD, MA crossover, ATR, volume
│ ├── sentiment_agent.py # SentimentAgent — equity headlines + crypto signals
│ ├── risk_agent.py # RiskDataAgent — volatility (std × √252), beta, risk flags
│ ├── bull_analyst.py # BullAnalyst — upside thesis, catalysts (qualitative only)
│ ├── bear_analyst.py # BearAnalyst — downside thesis, risks (qualitative only)
│ ├── valuation_analyst.py # ValuationAnalyst — P/E vs sector, intrinsic value
│ ├── onchain_analyst.py # OnChainAnalyst — CoinGecko metrics for crypto
│ └── supervisor_agent.py # SupervisorAgent — final synthesis, buy/hold/sell
├── graphs/
│ └── graph_builder.py # GraphBuilder — parallel graph + conditional routing
├── llms/
│ └── llm_client.py # Tiered LLM client — nano (fast) + mini (smart)
└── exceptions.py # Custom exception hierarchy
app.py # FastAPI — /analyze, /approve, /portfolio, /orders
alpaca_broker/
├── client.py # Alpaca SDK wrapper — orders, positions, portfolio history
└── trade_executor.py # Trade rules — High confidence equity only, stop-loss
evaluation/
├── signal_store.py # Records approved signals with price for 30-day eval
├── eval_job.py # 30-day maturation — hit rate vs SPY
└── db_eval.py # Postgres schema for eval tables
ingestion/
├── scheduler.py # 6 nightly APScheduler jobs
├── db.py # Postgres schema — 8 ingestion tables
├── universe.py # S&P 500 + NASDAQ 100 from FMP
├── screener.py # Stage 1 quantitative screener → top 50
├── rss_client.py # RSS feeds → ticker-mapped headlines
└── crypto_signals.py # Fear & Greed, BTC dominance, GitHub momentum
frontend-react/ # React + Vite frontend
benchmark.py # Per-agent Gantt-style latency benchmark
Dockerfile
git clone https://github.com/aakarsh31/FinanceAgent-LangGraph.git
cd FinanceAgent-LangGraph
uv sync
cp .env.example .envAdd to .env:
OPENAI_API_KEY=...
LANGCHAIN_API_KEY=...
FRED_API_KEY=... # free at fred.stlouisfed.org
FMP_API_KEY=... # free tier — constituent lists only
APCA_API_KEY_ID=... # Alpaca paper trading
APCA_API_SECRET_KEY=...
uv run uvicorn app:app --reloadFrontend (dev):
cd frontend-react && npm install && npm run devSupervisor pattern over shared state — each specialist agent has a distinct role and writes one output to state. The SupervisorAgent reads all outputs and makes the final call — it never touches raw data directly, mirroring how a portfolio manager reads analyst memos rather than Bloomberg terminals.
HITL at the trade gate, not the supervisor — the full pipeline including the Supervisor runs on /analyze so the user sees the recommendation before deciding. The interrupt fires at trade_gate after synthesis — the human reviews the complete memo and approves trade execution, not blind agent outputs. This is the architecturally correct HITL placement for a compliance gate. A /reject endpoint is also available — a gate with only "yes" is a confirmation dialog, not a gate.
Qualitative data isolation for bull/bear analysts — raw P/E, EPS, and D/E ratios are converted to qualitative buckets in Python before reaching the LLM. This eliminates hallucinated sector comparisons (e.g. "sector median = 2.47x") caused by LLM pattern-matching on numeric inputs from training data.
MacroRegimeAgent always runs — macro context isn't an asset class to analyze, it's the backdrop for every analysis. Fed funds rate, CPI, and yield curve shape color every bull/bear thesis downstream.
Tiered LLM cost optimization — GPT-4.1-nano for 9 analysis agents (fast, cheap), GPT-4.1-mini reserved for SupervisorAgent where synthesis quality matters. Keeps cost under $0.002 per full run.
SqliteSaver for checkpoint persistence — HITL requires persisting suspended state between two separate HTTP requests (/analyze → /approve). InMemorySaver cannot survive across requests. SqliteSaver gives per-thread state persistence with zero infrastructure overhead for single-instance deployment.
Volatility computed deterministically — std × √252 in pandas. LLM never touches numerical calculations.
This system is for research and educational purposes only. It is not a registered investment adviser and does not provide investment advice.
- Paper trading only — all trades execute against Alpaca's paper trading environment with simulated capital. No real money is involved.
- Not registered — this system is not registered under the Investment Advisers Act of 1940 or any equivalent regulation. Outputs should not be construed as investment advice.
- Evaluation limitations — signal hit rate is computed over a small sample (n < 50 at launch). Results are directionally suggestive but not statistically significant. Confidence intervals are not yet reported; treat point estimates with appropriate skepticism.
- Data quality — fundamentals and news are sourced primarily from yfinance (an unofficial scraper) and RSS feeds. Data may be stale, incomplete, or incorrect. The system includes cache-miss logging and data provenance annotations but cannot guarantee data accuracy.
- Survivorship bias — the screener universe is constrained to S&P 500 + NASDAQ 100 constituents. Delisted or recently added tickers may produce unexpected behavior.
- Prompt injection surface — RSS headlines (untrusted internet text) flow into agent prompts. Headlines are included in structured prompt sections but are not formally sandboxed. Adversarial inputs have not been comprehensively tested.
- LLM non-determinism — all models run at temperature=0 for reproducibility, but structured output parsing and model behavior may vary across API versions.
- No stop-loss persistence — stop-loss orders use
TimeInForce.DAYdue to Alpaca paper trading constraints on fractional shares. Stops expire at market close and are not re-placed automatically.
For production use in a regulated context, this system would require: investment adviser registration, formal model risk management, input sanitization for prompt injection, persistent HITL audit logging, and migration from yfinance to a licensed data provider.