Back to Selected Work

    Portfolio Risk Analysis

    Parallel risk and compliance checks, narrated into a report as it streams.

    PythonFastAPIGeminiSSE StreamingView live

    What it does

    Holdings in, a narrated risk report out

    Post tickers, weights, and a risk profile. It computes real portfolio statistics — volatility, Sharpe, VaR, drawdown, a full covariance matrix, benchmark beta — checks the result against profile-driven compliance rules, proposes a rebalance, and streams all of it to the browser as each stage finishes, ending in an AI-narrated summary.

    Architecture

    One FastAPI process, a real parallel fan-out

    Browser

    POST holdings + risk profile, read the response as an SSE stream via fetch() — not EventSource, since this is a POST.

    FastAPI service

    Single process. Validates the request, schedules the pipeline on a worker thread, bridges progress back to the event loop via an asyncio.Queue.

    runs in parallel

    Risk Agent

    Pulls 1y price history via yfinance (behind a 5-min cache), computes volatility, Sharpe, VaR, drawdown, correlation & covariance matrices, benchmark beta.

    Compliance Agent

    Checks weight-sum, concentration, position-size, and sector rules against the active risk-profile preset.

    both results feed

    Rebalancing Engine

    Caps any over-limit position, redistributes freed weight by inverse-volatility score, re-estimates portfolio volatility from the covariance matrix.

    Reporting Agent → Gemini · third-party

    Sends a structured prompt, parses the response as strict JSON. On any failure, timeout, or a too-short reply, falls back to a fully deterministic, metric-driven narrative instead.

    Aggregator

    Merges portfolio, risk, benchmark, performance, compliance, and rebalancing results into one schema — its own SSE stage, emitted after the report.

    Browser

    Aggregated JSON renders as risk cards, a correlation heatmap, and the narrated report.

    Risk and Compliance genuinely run at the same time — a two-worker ThreadPoolExecutor, since both yfinance and the Gemini SDK are synchronous and would otherwise block the event loop.

    Worth knowing

    Built for LangGraph, shipped on a thread pool

    The original design called for LangGraph to own this orchestration. What's actually running today is simpler — a supervisor that fans work out to a thread pool and merges it back, with a deterministic fallback if Gemini errors. It does the job the graph would have done, with fewer moving parts to debug; the LangGraph rewrite is still on the roadmap, not abandoned.