# Defined-Risk Equity-VRP Modeled Sleeve (SPY put-credit-spreads)

**Date:** 2026-07-11
**Status:** SUPERSEDED (2026-07-12) by `2026-07-12-ibkr-consolidation-xsp-vrp-ucits-design.md` — the
flat-VIX Black–Scholes synthetic below was a biased, un-validatable placeholder; VRP is rebuilt on **real
OPRA option prices** (XSP put-credit-spreads on IBKR, not synthetic SPY). Kept for history only.
**Depends on:** the forward engine (A), the reconciliation gate (B1), the CVaR-aware allocation engine (merged `edeb40b`), the multistrat forward-track pattern. Real-money-adjacent (it becomes a sized, gated sleeve).

## Problem / Goal

The defined-risk equity-VRP sleeve is the validated cross-asset diversifier (existence-test PASSED this session: `^PUT` proxy, defined-risk Sharpe ~0.8–1.0, diversifies *idiosyncratic* crypto crashes but co-crashes systemic risk-off). Get it into the book as a **recomputable, forward-tracked, CVaR-sized, gated strategy** — the same first-class treatment as the crypto edges and the multistrat book — **without** the Alpaca-options execution leg, which is blocked on the 401 paper keys and is a large untestable API surface.

**Goal:** a `vrp` strategy that produces a recomputable daily return series (a synthetic *defined-risk* SPY put-credit-spread ladder priced from public SPY + VIX data), wired into the nightly forward engine + reconciliation gate + CVaR-aware allocation, and surfaced in the cockpit. The Alpaca-options execution twin is explicitly deferred.

## Non-Goals / Hard Constraints

- **No Alpaca-options execution leg now** (blocked on 401 keys; a later `vrp_exec` twin reconciling executed-vs-modeled, mirroring C1/C2).
- **Defined-risk ONLY** — the modeled series is a *put-credit-spread* with a bounded max loss (width − credit), never a naked put-write. This is the load-bearing safety property: std under-sizes short-vol's fat tail, so the sleeve is safe only because (a) the CVaR floor de-levers it and (b) defined-risk bounds its max loss.
- **Real money errs false-WAIT**: recompute-from-frozen-PIT (stable record); reconciliation gate before any capital; sized small (10–20% risk) via the existing engine; **it is NOT a systemic hedge** (co-crashes risk-off).
- Verify the cockpit **locally** (read the NAV/gate) per `feedback_verify_cockpit_locally_not_prod`.
- Faithful-but-approximate: the BS+VIX synthetic ignores skew and real slippage — the *deferred* exec-twin will measure that divergence; the modeled track is the honest baseline, not a claim of live fills.

## Design

### Component 1 — the synthetic VRP strategy (`src/fxhnt/application/vrp_book.py`)

Pure, testable Black–Scholes helpers + a `VrpStrategy` implementing the `ForwardStrategy` protocol
(`advance(last_date, extra) -> (list[(iso_date, fractional_return)], extra)`), recomputing the full daily
NAV each call (recompute mode).

**Rules (approved params):** weekly entry, **~30 DTE**, short-leg **~20-delta** put, fixed **$5 SPY width**,
**manage at 50% profit or close 1 trading day before expiry**, held as a **ladder** (~4–5 concurrent spreads
so the book is always-on → a smooth daily series).

**Pricing (Black–Scholes, VIX as IV, r=0):** for spot `S`, IV `σ = VIX/100`, `τ = DTE/365`:
- Short strike from the ~20-delta target: put `|Δ| = N(-d1) = 0.20` ⇒ `d1 = Φ⁻¹(0.80) = 0.8416` ⇒
  `K_short = S·exp(σ²τ/2 − 0.8416·σ√τ)` (rounded to the nearest $1 SPY strike).
- `K_long = K_short − 5`.
- Entry `credit = BS_put(K_short, σ, τ) − BS_put(K_long, σ, τ)` where
  `BS_put(K) = K·N(−d2) − S·N(−d1)` (r=0), `d1 = [ln(S/K)+σ²τ/2]/(σ√τ)`, `d2 = d1 − σ√τ`.
- **Daily mark:** each day re-price the open spread with updated `S`, decremented `τ`, current `σ` ⇒
  `mtm_value`; unrealized P&L = `credit − mtm_value`.
- **Management:** close (realize P&L) when `mtm_value ≤ 0.5·credit` (50% profit) OR at DTE=1; then open a
  fresh 30-DTE spread the next weekly slot.
- **Return basis (defined-risk):** per spread `margin = (5·100) = $500` max loss. The ladder's **daily
  fractional return = Σ(daily MTM change over open spreads) / (N_open · margin)** — return per dollar of
  defined-risk margin. (A fixed nominal capital = N·margin; no leverage inside the sleeve — the allocation
  engine sizes it.)

Same `VrpStrategy` recomputes the **full 2016–2026 history** (for the backtest ref) and the **recent forward
window** — identical to how `MultiStratStrategy` produces both.

### Component 2 — data (SPY + VIX, PIT-frozen)

Fetch **SPY** and **^VIX** daily closes via the existing Yahoo path (`YahooDataProvider.fetch(Market(symbol=…))`
takes any symbol in the URL — confirmed). Mirror `multistrat_nav`: **snapshot today's closes PIT into the
warehouse** and recompute off the FROZEN PIT series (stable against Yahoo's retroactive adjustments). VIX is
an index (not adjusted), SPY uses adjusted close.

### Component 3 — forward-track + gate wiring

- **`vrp_nav` Dagster asset** (`assets.py`, mirrors `multistrat_nav`): snapshot SPY+VIX PIT →
  `run_track(repo, "vrp", VrpStrategy(...))` → `persist_backtest_ref=True` (publishes the basis-matched ref
  via `_persist_track_backtest_ref(context, "vrp", build_strategy)` = `VrpStrategy().advance(None, {})`).
- **`definitions.py`:** add `vrp_nav` to `combined_book_forward_job.selection` + `Definitions.assets`; add it
  to `cockpit_forward`'s `deps`.
- **`registry.py`:** `"vrp"` entry — `display_name` "Equity VRP (SPY put-credit-spreads, defined-risk)",
  `sleeve` "equity-vol", `venue` "alpaca-options" (reference-only until the exec twin lands), `tier`
  "research", `execution` "paper", `record_mode` "recompute", `gate_spec = {gate_type: "reconciliation",
  min_forward_days: 21}`, `promote_on_pass` False initially (turn on after we've watched the forward
  reconcile — a deliberate human step, given it's real-capital-adjacent).
- **`migration_builders.py`:** a `builders["vrp"] = ("vrp_state", VrpStrategy-builder)` entry (recompute
  track needs a builder so `migrate-forward-anchors` can seed its anchor — the CRITICAL-1 pattern).

### Component 4 — sizing (the end-to-end payoff)

No new sizing code: once `vrp` is a funded deploy/promoted strategy, `record_allocation` →
`compute_allocation` sizes it automatically, and the **CVaR floor we just merged de-levers its fat left tail**
(the synthetic VRP series is fat-left-tailed by construction). Add a unit test asserting a VRP-shaped return
series produces `K_cvar < K_vol` (the two sub-projects connect end-to-end). Defined-risk bounds its
contribution to the aggregate killswitch drawdown.

### Component 5 — cockpit + local verify

`vrp` surfaces as a research forward-tracked sleeve (equity-vol venue). Verify LOCALLY: seed/recompute the
sleeve into the local dev DB, `scripts/dev-cockpit-local.sh`, screenshot `/strategy/vrp` — read the NAV,
gate status (WAIT/building N/21), and the backtest ref.

### Component 6 — DEFERRED (explicitly out of scope now)

The Alpaca-options **execution leg** (`vrp_exec` twin: options-chain fetch, ~20-delta strike selection, atomic
`mleg` put-credit-spread orders, the `execute-vrp` CLI + a `fxhnt-vrp-rebalancer` CronJob, reconcile
executed-vs-modeled slippage). Blocked on valid Alpaca paper keys + a large new options-API surface — a later
sub-project, built the same way as the C1/C2 exec twins once keys land.

## Data Flow

```
combined_book_forward_job (nightly)
  → vrp_nav: snapshot SPY+VIX PIT closes → recompute VrpStrategy (defined-risk SPY put-spread ladder, BS+VIX)
      → run_track(repo, "vrp", VrpStrategy) → forward_summary/nav + persist_backtest_ref
  → cockpit_forward: evaluate the reconciliation gate (forward vs ref), surface in the cockpit
Nightly record_allocation (when vrp promotes to deploy):
  → compute_allocation includes vrp; K = min(k_vol, k_cvar) → CVaR floor de-levers the fat-tail sleeve
```

## Error Handling

- Missing/short SPY or VIX history → `advance` returns `[]` (inception, no rows) — the track warms, never crashes.
- BS numerical guards: `σ>0`, `τ>0`, `S>0`; a degenerate day (σ→0) → skip/flat that day.
- Recompute stability: frozen PIT series → the record doesn't drift on Yahoo re-adjustment (multistrat lesson).
- Gate: no backtest ref → the reconciliation gate degrades to a bare timer (known failure mode) — the
  `persist_backtest_ref=True` in `vrp_nav` prevents it; a test asserts the ref row is written.

## Testing

Unit:
- BS helpers: put price vs a known reference; `d1/d2`; 20-delta strike inversion (`|Δ(K_short)| ≈ 0.20`).
- `VrpStrategy.advance`: on a synthetic SPY+VIX path, produces a plausible defined-risk return series (bounded
  per-spread loss = width; positive credit-decay on calm days; capped loss on a crash day); `advance(None,{})`
  returns the full inception series; recompute is deterministic (same input → same series).
- The fat-left-tail property: the produced series has `cvar_daily/std > 1.755` (fatter than Gaussian) →
  feeding it to `compute_allocation` yields `K_cvar < K_vol` (connects to the CVaR engine).
Integration:
- `vrp_nav` asset recomputes + writes a `forward_summary` + a `backtest_summary` (ref) row; `definitions.py`
  loads with `vrp_nav` in the job + cockpit_forward deps; `migrate-forward-anchors` seeds the `vrp` anchor.
Local cockpit verify (Playwright): `/strategy/vrp` renders NAV + gate + ref.

## File Touchpoints

| File | Change |
|---|---|
| `src/fxhnt/application/vrp_book.py` | **new** — BS helpers + `VrpStrategy` (defined-risk SPY put-spread ladder) |
| `src/fxhnt/adapters/orchestration/assets.py` | **new** `vrp_nav` asset (mirror `multistrat_nav`) + `cockpit_forward` dep |
| `src/fxhnt/adapters/orchestration/definitions.py` | add `vrp_nav` to the job + `Definitions.assets` |
| `src/fxhnt/registry.py` | `"vrp"` entry (recompute, reconciliation gate, promote_on_pass False) |
| `src/fxhnt/adapters/orchestration/migration_builders.py` | `builders["vrp"]` (anchor seeding) |
| tests: `test_vrp_book.py` (unit), `test_vrp_nav_asset.py` / definitions + migration (integration) | BS + strategy + fat-tail→CVaR + wiring |

## Deploy Notes

- No schema change. Next nightly runs `vrp_nav` (recompute + ref); the gate shows building N/21.
- `promote_on_pass` is **off** — `vrp` will NOT auto-fund even on gate PASS; promotion to deploy (and thus
  CVaR-sized capital) is a deliberate human step after watching the forward reconcile. Real money errs
  false-WAIT.
- The Alpaca-options exec twin is a separate future sub-project (needs valid paper keys first).
