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
Markdown
# 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).