UNPKG

kestrel.markets

Version:

A typed, token-efficient language + runtime for agentic trading: agents author bounded plans, the runtime fires them at the tick. CLI + typed library + MCP server.

159 lines (135 loc) 11.6 kB
# OSS-ADR-0037 — `@fair` is underlying-anchored; the book is a freshness-conditional diagnostic, never a clamp Status: accepted (2026-07-14, owner ruling on `kestrel-ltrf`) ## Context Live paper-gateway testing (2026-07-14, `kestrel-7o2.8` dry-run against real IB data) produced an `@fair` **28% ABOVE the observed ask** on a liquid, two-cent-wide ATM SPY leg (752C bid 0.71 / ask 0.72 → `@fair` 0.92). A subsequent read-only tape scan using the REAL `buildSurface` / `impliedVol` / `black76` / `executionFair` (reimplementing nothing) found the resolved `@fair` landing **outside the leg's own bid/ask on 91.8% of real-OPRA legs** and 35.4% of real-IB legs — worst exactly at the money, where authors actually trade. Seeing that, the owner ruled: *"then fair isn't being calculated at all correctly."* That is the correct conclusion, and it is architectural, not merely a bug in one function. `ARCHITECTURE` defines `ExecutionFair` as *"the honest price at which a market maker will fill you — underlying-anchored intrinsic + a vol read backed out of the liquid quotes, floored at intrinsic … it carries whether it can be trusted."* Two readings of that definition were in tension, and the incident forced a decision between them: - **"The book bounds fair."** A price at which a market maker will fill you cannot be above the ask (you can buy at the ask right now) nor below the bid. So — the reasoning went — clamp `@fair` into `[bid, ask]`. - **"Fair is anchored to the underlier, not the option quote."** `@fair` was **built for XND** (Nasdaq-100 micro-index options) and instruments like it, where the option spread is routinely 0.01–1.00 wide **and the posted option quote lags the continuously-ticking index**. There, the mid — indeed the whole posted quote — has no reliable relation to fair value from the index spot. Clamping `@fair` to the option's own `[bid, ask]` would defeat exactly what the surface exists to do. The first reading is wrong for the instrument `@fair` was designed to serve. The owner's original framing ("THE BOOK BOUNDS FAIR") is **superseded** by this ADR. The root of the SPY incident was **not** "fair legitimately differing from a real book." It was a **corrupted vol read**: the forward was never derived (`forward = underlyingSpot`, `kestrel-ukwz`), so the per-leg IVs disagreed and got averaged across a broken put-call parity (`kestrel-ku99`); tau was independently wrong on multi-day tapes (`kestrel-wcnd`). With the correct **parity-implied forward** (`F* = K + (Cmid − Pmid)` at the ATM strike), the single-leg call/put IVs agree to bisection precision and `@fair` on the SPY-752 leg lands ~0.72 — **inside the fresh, tight SPY book, with no clamp.** The fix is to repair the inputs, not to clamp the output. The code already applies no-arb discipline on the way **IN**`realSides()` demotes a bid below intrinsic to dark and caps a sentinel ask — but it applied nothing to its own **output**. The receipt was worse than silent: it was **anti-diagnostic** (median `nLiquid` 5 over all priced legs but 31 when `@fair` was out of the book — more strikes backing the surface made it *more* likely to be wrong), and it carried IVs of 166%–1418% ungated. ## Decision 1. **`@fair` is authoritative from the fresh underlying spot (via the model), floored at intrinsic.** It is anchored to the underlier; the liquid option quotes inform the **vol read only**, never the price **level**. For index options especially, `@fair` is **not** bounded by the option book, because the option book lags and/or is wide and is frequently not a reliable transactable reference. The intrinsic floor stays **spot-anchored** — intrinsic is settlement truth, a fact about spot, not about the forward. 2. **The forward is derived from the option market, not assumed.** `@fair` prices against the **parity-implied forward** `F* = K* + (Cmid − Pmid)` read at the ATM-most shared-liquid strike, threaded into **both** the IV inversion and the Black-76 pricing. This handles dividends, cost-of-carry, and spot/quote clock-desync for free, and makes the call and put IV agree so the averaging becomes a near-no-op. Where no strike qualifies, fail closed to the **known-degraded** semantics (`forward = spot`, `source = "spot"`) with a machine-readable taint — never null (that would kill `@fair` on ~92% of real strikes), never a novel guess. (Implemented per `kestrel-ukwz`.) 3. **The book is a FRESHNESS-CONDITIONAL DIAGNOSTIC, NEVER A CLAMP.** Its authority depends on the option quote being **fresh** (updating with the underlier, not lagging) **AND tight** (spread small relative to the option value): - **When the book is real, fresh, AND tight** (a liquid equity option like SPY that keeps up), a large fair-vs-book divergence is the loudest available evidence that the model **inputs** are broken. **Fail closed:** `@fair` goes UNKNOWN / falls to an annotated fallback and **taints the receipt**, de-arming dependents, with a logged machine-readable reason. **Never** clamp to the book, never silently override reality with a model. - **When the book is wide, stale, dark, one-sided, or index-lagging** (the XND fast-index regime `@fair` exists for), the book does **not** bound `@fair`; `@fair`-from-fresh-spot is authority and the quote is only a health signal. 4. **The discriminator is FRESHNESS + TIGHTNESS, not two-sided-or-not.** A fast-index quote can be tight and two-sided yet **stale**. The existing `QuoteHealth` vocabulary (`twoSided` / `dark` / `spread` / `spreadRegime`) plus a staleness/watermark check of the option quote against the underlier is the right test. Freshness is the **engine's** job (`src/engine/pricing.ts` injects a `bookFresh` flag); `src/fair` stays pure and clockless. 5. **A violent disagreement is a SIGNAL, not a PRICE — refuse, never project.** When the model lands outside a real+fresh book, or the derived forward violates wide no-arb bounds, **reject** it entirely and fall to the annotated fallback carrying the reason. Do **not** project the value onto the sanity band — a clamp manufactures a plausible-looking number and hides the break. This dissolves the "mid is never a price" tension rather than violating it: that doctrine exists to stop an author anchoring on a **fictional** book (thin / dark / placeholder). A tight, deep, **fresh** two-sided market is real and bounds you; a dark, one-sided, or stale book is not, and that is precisely where the surface earns its keep. 6. **The receipt must carry whether `@fair` can be trusted.** `FairReceipt` reports the derived **forward**, its **source** (parity-implied vs spot-fallback), its **taint**, the **strike** the forward was read at, the **parity residual**, and the **distance from a fresh, tight book** — and refuses to vouch when the model contradicts a liquid, fresh market. `nLiquid` is **not** a trust signal (it is anti-diagnostic — negatively correlated with correctness). 7. **The live-broker safety wall gates on an UNTRUSTED / TAINTED / STALE receipt**, not on a naive `@fair`-outside-`[bid, ask]` clamp (which would wrongly refuse legitimate XND fairs). A refusal to transmit is triggered by a receipt the model cannot vouch for. A delayed / frozen quote may never be a price-anchor input. ## Consequences - **What the surface is FOR is now explicit:** to price a leg whose book is **not** real (dark / one-sided / absurdly wide / stale / a placeholder ask) from the fresh underlier and a vol read backed out of the legs that *are* real. For a tight, deep, fresh, two-sided market the surface has nothing to add and no standing to overrule it — `@fair` and mid must **agree** there (else taint / alarm). - **This invariant would have caught all four input bugs at the output boundary.** `kestrel-ku99` (parity averaging), `kestrel-ukwz` (underived forward), `kestrel-wcnd` (tau) corrupt the inputs; `kestrel-wyay` (two fair paths reading different inputs) must be unified to one canonical `@fair` per `(tape, instant, leg)`. Fixing the inputs is **necessary but not sufficient** — a perfect surface still must not be trusted over a real, fresh book, and a broken one must fail closed. - **BENCHMARK-AFFECTING.** Deriving the forward and adding the taint changes `@fair`, hence fills, hence every graded result on a real, non-synthetic option tape. This decision and its implementation (`kestrel-ukwz` + `kestrel-ku99`) are **coordinate-gated** with the benchmark fleet line (`m9i` / `y77` / `pqv`); prior graded rows are immutable and get a dated restatement annotation, and re-graded rows carry new provenance. No published number is silently edited. - **Fixtures ride the REAL driver.** Per AGENTS.md, every guard ships with a fixture that fails on purpose through the production path (`runSimulateSession``PlanEngine``resolveFair``executionFair`): the SPY-752 reconstruction (`@fair` 0.90 → 0.72), a fresh/stale sibling pair that proves the taint fires only on a fresh book (and a mutation that turns the taint into a clamp reddens **both** siblings — the stale sibling is what stops a clamp masquerading as the fix), plus byte-determinism and leg-order-permutation-invariance fixtures. ## Alternatives considered - **Clamp `@fair` into the option's own `[bid, ask]` ("the book bounds fair").** Rejected: it defeats the instrument `@fair` was built for (XND / fast-index options, where the posted quote lags the index and is often wide), and a clamp manufactures a plausible number that hides a broken vol read instead of surfacing it. Superseded framing. - **Bound `@fair` only when the book is two-sided.** Rejected: insufficient — a fast-index quote can be tight and two-sided yet stale. The discriminator must be freshness + tightness, not merely two-sidedness. - **Fix the inputs (forward / parity / tau) and stop there.** Necessary but not sufficient: a perfect surface still must not override a real, fresh market, and a corrupted one must fail closed at the output boundary. This ADR is the output-side invariant that complements the input fixes. - **Keep `nLiquid` as the fit-quality signal.** Rejected: it is anti-diagnostic (`nLiquid` was *higher* precisely when `@fair` was out of the book). The receipt must carry forward-source, taint, parity residual, and distance-from-fresh-book instead. ## References - `kestrel-ltrf` — the owner ruling this ADR records (2026-07-14). - `kestrel-hf0v` — the ExecutionFair correctness epic (one canonical `@fair`, parity guard, tau fix). - `kestrel-ukwz` — derive the forward from put-call parity (the dominant root cause; the code fix cites this ADR as its governing doctrine). - `kestrel-ku99` — stop averaging call/put IV across an unchecked parity assumption; add the freshness-conditional fair-vs-book taint. - `kestrel-wcnd` — tau from real expiry (independent input bug). - `kestrel-wyay` — collapse the two fair paths to one canonical number per caller. - `ARCHITECTURE` — the `ExecutionFair` definition ("underlying-anchored intrinsic + a vol read backed out of the liquid quotes, floored at intrinsic … carries whether it can be trusted"), refined here. - `src/fair/surface.ts` (`impliedForward`, `buildSurface`, `realSides`), `src/fair/index.ts` (`executionFair`, `FairReceipt`), `src/engine/pricing.ts` (`bookFresh` injection).