LIVE
connecting to the live quote stream…
--:--:--UTC
Learn/Bots/Heston SV Pricer (NN)
AI QuantsAI
Ψ

Heston SV Pricer (NN)

Stochastic-vol pricer — vol of vol, mean reversion, leverage.

⟢In plain English

Real markets don't have one fixed volatility — vol moves around too. Heston is the gold-standard model that simulates that, including the negative correlation that makes puts more expensive than calls. We trained an AI to price it instantly.

Real markets don't have a single fixed volatility — vol moves around too. Heston is the gold-standard model that simulates that, including the negative correlation that makes puts more expensive than calls (the famous skew). The bot trained on fypy library ground truth, neural-net distilled.

⟢The math
formula
trained on fypy Heston ground truth
parameters
typeTypechoices: C, Pdefault · C
spotSpot $range — → —default · 100
strikeStrike $range — → —default · 100
daysDays to expiryrange — → —default · 60
rateRate %range — → —default · 4.5
v0v₀ (initial var)range 0.001 → 0.5default · 0.04
kappaκ (mean rev speed)range 0.1 → 10default · 2
thetaθ (long-run var)range 0.001 → 0.5default · 0.04
xiξ (vol of vol)range 0.05 → 2default · 0.5
rhoρ (S↔v corr)range -1 → 1default · -0.7
⟢Live demo

Real Heston SV Pricer (NN) bot, running on real Yahoo data when the symbol is available. Drag the params — the bot re-runs instantly.

symbolloading…
loading AMZN bars…
⟢Source code · public

This is the actual code the bot runs — not a re-explanation, not a simplified version. Whatever ships here is what executes when you press Run All in the workbench. Read it, copy it, fork it, build a better one.

●lib/quant/ai-bots.ts·lines 495–591
TypeScript · MIT-licensed
const hestonSurrogate: BotDef = aiBot<HestonReq, BSRes>(
  {
    id: "ai-heston",
    name: "Heston SV Pricer (NN)",
    category: "ai",
    glyph: "Ψ",
    tagline: "Stochastic-vol pricer — vol of vol, mean reversion, leverage.",
    formula: "trained on fypy Heston ground truth",
    endpoint: "/api/heston",
    module: "ai quants/models/heston/train.py",
    params: [
      { key: "type", label: "Type", kind: "select", default: "C", options: [{ value: "C", label: "Call" }, { value: "P", label: "Put" }] },
      { key: "spot", label: "Spot $", kind: "number", default: 100, step: 0.5 },
      { key: "strike", label: "Strike $", kind: "number", default: 100, step: 0.5 },
      { key: "days", label: "Days to expiry", kind: "number", default: 60, step: 1 },
      { key: "rate", label: "Rate %", kind: "number", default: 4.5, step: 0.1 },
      { key: "v0", label: "v₀ (initial var)", kind: "number", default: 0.04, min: 0.001, max: 0.5, step: 0.005 },
      { key: "kappa", label: "κ (mean rev speed)", kind: "number", default: 2.0, min: 0.1, max: 10, step: 0.1 },
      { key: "theta", label: "θ (long-run var)", kind: "number", default: 0.04, min: 0.001, max: 0.5, step: 0.005 },
      { key: "xi", label: "ξ (vol of vol)", kind: "number", default: 0.5, min: 0.05, max: 2, step: 0.05 },
      { key: "rho", label: "ρ (S↔v corr)", kind: "number", default: -0.7, min: -1, max: 1, step: 0.05 },
    ],
  },
  {
    request: (ctx, p) => {
      const lastPx = ctx.candles[ctx.candles.length - 1]?.c ?? 100;
      const type = (str(p, "type", "C") === "P" ? "p" : "c") as "c" | "p";
      return {
        S: num(p, "spot", lastPx),
        K: num(p, "strike", Math.round(lastPx)),
        T: Math.max(0.0005, num(p, "days", 60) / 365),
        r: num(p, "rate", 4.5) / 100,
        v0: num(p, "v0", 0.04),
        kappa: num(p, "kappa", 2.0),
        theta: num(p, "theta", 0.04),
        xi: num(p, "xi", 0.5),
        rho: num(p, "rho", -0.7),
        flag: type,
      };
    },
    build: (data, ctx, p) => {
      const lastPx = ctx.candles[ctx.candles.length - 1]?.c ?? 100;
      const type = (str(p, "type", "C") === "P" ? "P" : "C") as "C" | "P";
      const spot = num(p, "spot", lastPx);
      const strike = num(p, "strike", Math.round(lastPx));
      const days = num(p, "days", 60);
      const rate = num(p, "rate", 4.5) / 100;
      const rho = num(p, "rho", -0.7);
      const v0 = num(p, "v0", 0.04);
      const t = days / 365;
      const sigEff = Math.sqrt(v0);
      const bs = priceOption(spot, strike, t, rate, sigEff, type).price;
      const skew = data.price - bs;
      return {
        signals: [],
        metrics: [
          { key: "px", label: "Heston $", value: `$${data.price.toFixed(2)}`, tone: "info" },
          { key: "bs", label: "BS comparable", value: `$${bs.toFixed(2)}` },
          { key: "skew", label: "Skew premium", value: `${skew >= 0 ? "+" : ""}$${skew.toFixed(2)}`, tone: skew < 0 ? "bear" : "bull" },
          { key: "vol", label: "v₀ vol-equiv", value: `${(sigEff * 100).toFixed(1)}%` },
        ],
        summary: `Heston says $${data.price.toFixed(2)} — ${skew >= 0 ? "above" : "below"} BS because ρ = ${rho.toFixed(2)} pulls implied skew the way real markets price it.`,
        beginner:
          "Real markets don't have a single fixed volatility. Heston is the gold-standard model that simulates that, including the negative correlation that makes puts more expensive than calls.",
        verdict: { side: "hold", text: `Heston fair value $${data.price.toFixed(2)}.`, confidence: 1 },
      };
    },
    mock: (ctx, p) => {
      const lastPx = ctx.candles[ctx.candles.length - 1]?.c ?? 100;
      const type = (str(p, "type", "C") === "P" ? "P" : "C") as "C" | "P";
      const spot = num(p, "spot", lastPx);
      const strike = num(p, "strike", Math.round(lastPx));
      const days = num(p, "days", 60);
      const rate = num(p, "rate", 4.5) / 100;
      const v0 = num(p, "v0", 0.04);
      const xi = num(p, "xi", 0.5);
      const rho = num(p, "rho", -0.7);
      const t = days / 365;
      const sigEff = Math.sqrt(v0) * (1 + 0.3 * xi * t);
      const bs = priceOption(spot, strike, t, rate, sigEff, type).price;
      const skewBump = rho < 0 && type === "P" ? bs * 0.08 * Math.abs(rho) : rho > 0 && type === "C" ? bs * 0.05 * rho : 0;
      const heston = bs + skewBump;
      return {
        signals: [],
        metrics: [
          { key: "px", label: "Heston $", value: `$${heston.toFixed(2)}`, tone: "info" },
          { key: "bs", label: "BS comparable", value: `$${bs.toFixed(2)}` },
          { key: "skew", label: "Skew premium", value: `+$${skewBump.toFixed(2)}`, tone: rho < 0 ? "bear" : "bull" },
          { key: "vol", label: "Effective vol", value: `${(sigEff * 100).toFixed(1)}%` },
        ],
        summary: `Heston says $${heston.toFixed(2)} (TS approximation).`,
        beginner: "Real markets don't have a single fixed volatility — Heston simulates that.",
        verdict: { side: "hold", text: `Heston fair value $${heston.toFixed(2)}.`, confidence: 1 },
      };
    },
  },
);
what each piece means
  • id — unique key the workbench uses to find the bot.
  • params — the sliders + inputs you see on the cell.
  • run(ctx, p) — the function that gets called with candles + your params and returns the verdict.
  • verdict — the BUY / SELL / HOLD pill at the top of the cell.
  • metrics — the small stat boxes shown in the cell body.
use this code yourself
  1. Copy the whole block above.
  2. On /quant, click + Import your bot in the bot library.
  3. Paste, hit save. It hot-loads into your workspace.
  4. Edit any param defaults or logic to your taste — it's now yours.
⟢Specialty · when it shines, when it fails
✓ Shines when
  • ·Modelling skew. BS assumes flat IV across strikes; reality has a smile and a smirk. Heston captures it.
  • ·Long-dated options where stochastic vol matters most.
  • ·Calibrating to a vol surface. Run Heston across the chain, fit (κ, θ, ξ, ρ, v₀) to observed prices.
✗ Fails when
  • ·Inputs outside training distribution. Heston's parameter space is wide; the surrogate only saw a slice.
  • ·Short-dated, near-the-money options. BS is essentially correct there; Heston's edge is at the extremes.
  • ·Calibration is hard. The bot prices Heston given parameters — it doesn't fit parameters to a market chain. That's a separate optimisation.
⟢How to read its verdict

HOLD always. The 'skew premium' (Heston − BS) is the interesting output. Negative ρ + put = bigger Heston premium = real market behaviour. Tells you how much skew is worth in dollar terms for these inputs.

⟢Python service

This bot tries to call the FastAPI service first. When it's up, you get real model output. When it's down, the bot transparently falls back to a deterministic TS surrogate.

srcai quants/models/heston/train.pyapi/api/heston
FastAPI··/api/hestonCHECKING…
How the request flows+
01
BotCell.run()
User clicks Run on this bot in /quant
02
callApi()
POST to localhost:8000/api/heston
03
load_surrogate()
ai quants/models/heston/train.py
04
predict()
Forward pass on the inputs you provided
05
BotResult
JSON returned, card flips green Source: Python NN
spin it upcd "ai quants" && uvicorn serve:app --reload --port 8000
⟢FAQ
What do κ, θ, ξ, ρ, v₀ mean?+
v₀: current variance. θ: long-run variance the system reverts to. κ: speed of reversion. ξ: vol of vol (how jumpy the variance itself is). ρ: correlation between stock and variance — typically negative because stocks and vol move opposite.
How do I calibrate Heston for a specific stock?+
Outside the bot's scope. Standard practice: take observed option prices across strikes/expiries, run a least-squares fit on the 5 Heston parameters, plug calibrated params back into the bot for any new strike/expiry. We don't ship the calibration step yet.