A lightweight Quant AI Agent that diagnoses why a specific option's price changed — not by predicting prices, but by combining deterministic quant metrics (Delta/Vega/Theta PnL attribution) with recent news, synthesized by an LLM into a human-readable Markdown report.
A unified LangGraph product graph (book-first parent + leg subgraph). Live compiled diagrams: notebooks/langgraph_architecture.ipynb.
flowchart LR
A[require_openai] --> B[Send leg_branch]
B --> C[aggregate_book]
subgraph D[leg_branch subgraph]
D1[fetch_market] --> D2[quant] --> D3[blotter] --> D4[diagnostic_pass]
D4 --> D5[residual_gate loop]
D5 --> D6[plan_search] --> D7[search] --> D8[synthesize]
D8 --> D9[verify loop] --> D10[finalize_leg_report]
end
The PnL of a 1-day move is decomposed as:
.
├── src/ # Diagnoses option moves
│ ├── data_loader.py # yfinance: stock, option IV, news, vol surface
│ ├── data/synthetic.py # Fixture loader (primary eval path)
│ ├── pricing/ # QuantLib FDM official PnL + LSM/Merton analysis API
│ ├── agent_graph.py # fetch_market → quant → blotter → plan_search → search → diagnose
│ ├── graph/ # GraphDeps ports (market / PnL)
│ ├── intel/ # SearchPlan + IntelSource registry
│ └── report_generator.py
├── tests/ci/ # Parts check (engine/graph/report + fixtures/golden)
├── tests/live_book/ # Recent live-book evaluation
├── tests/historical/ # Historical-event evaluation
├── tests/README.md # Test layout
├── notebooks/ # LangGraph topology notebook (PNG / x-ray)
├── app.py # Streamlit + CLI entrypoint
├── requirements.txt
├── README.md
├── ACKNOWLEDGMENTS.md # Third-party licenses
├── docs/ # Notes + curated official links
│ ├── references/
│ └── knowledge/
└── scripts/ # run-tests.sh, smoke-offline.sh
Doc map: docs/README.md.
Case study (historical anomaly): docs/case-studies/vow_float_squeeze_2008.md.
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
# All offline tests
./scripts/run-tests.shSet an OpenAI API key (required by default in the unified runtime):
export OPENAI_API_KEY="sk-..."
# default (cheapest common OpenAI chat model): gpt-4o-mini
# export EMO_LLM_MODEL="gpt-4o-mini"You can also put these in a .env file (loaded automatically). Optional Tavily
Search for vega / event queries: paste TAVILY_API_KEY= there (see .env.example).
Never commit the key. Without it, Yahoo titles still run and Tavily is skipped.
CLI:
python app.py --ticker AAPL --type call
python app.py --ticker TSLA --type put --strike 250 --expiry 2026-01-16
python app.py --fixture vol_crushBook input on the same product entrypoint:
python app.py --book tests/ci/fixtures/books/showcase_book.jsonEval reports belong under tests/live_book/output/ and tests/historical/output/<case>/ (gitignored), not the repo root. See tests/README.md.
Streamlit UI:
streamlit run app.py- Default US equity official pricing is American FDM (QuantLib
FdBlackScholesVanillaEngine). Local vol is used when today's implied-vol grid passes a Dupire probe; otherwise the contract IV is treated as flat. CRR remains selectable viaEMO_PRICING_ENGINE=crr. - Heston, when a vol grid is available, is diagnostic only.
- LSM-BS and LSM-Merton are an analysis API (
src/pricing/analysis_api.py) for later LLM tools — they are not thequantnode. - yfinance has no historical option chain. Live
iv_prevuses a local SQLite t-1 snapshot when present, otherwise a 20-day realized-vol (HV20) proxy. - Risk-free rate comes from
^IRXwhen available (else 4.5%).
- Wire
compare_to_officialas bounded diagnose-time tools - LangSmith golden dataset + numeric evaluators
- Extra search sources on the existing
searchnode — not a dedicated filings graph. Tavily (TAVILY_API_KEY) and SEC 8-K (EMO_SEC_USER_AGENT, free) are optional env toggles.