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.

148 lines 10.2 kB
/** * # fill/guarded-intent — the **GuardedIntent** owner (kestrel-vav2, arch-review C1b) * * ONE interface for the directional far-OTM guard, so the BOUNDED-RISK / never-naked property is * testable THROUGH the seam that enforces it rather than across three functions in two modules. * A {@link GuardedIntent} is a leg's guard verdict: which wing it sits in, whether it is a covering * wing, and whether the classification had to fail closed. It concentrates three decisions that * previously sat apart (the engine classified, the engine resolved spot, the fill model capped): * * 1. **Classification** — the coarse {@link Moneyness} bucket (strike vs the decision-time * underlier), and the ADR-0017 spot-leg case (no strike/right ⇒ no wing; the far-OTM guard is an * option concept with no spot analogue). * 2. **Decision-time spot resolution** — the underlier is pulled through the caller's * {@link GuardIntentInput.resolveSpot} thunk **at the moment of the decision**, and ONLY when an * option leg actually needs classifying. The owner decides *when* spot is needed; the caller only * says *how* to get it (the engine's `#spotFor` path). No clock, no RNG — pure given the thunk. * 3. **The far-OTM cap decision** — {@link guardedPfill}, the ONE place a symmetric maker `p_fill` * is bent by side × wing. {@link MakerFairV1} (and the calibrated sibling) consume a GuardedIntent * through this function on both of their hazard paths. * * The fail-closed clause lives here too and cannot be bypassed by a caller: a **SELL** whose spot is * unresolvable is classified `deep_otm` + standalone (the conservative cap) and flagged * {@link GuardedIntent.unresolved} so the caller LOGS the fork — never a silent default, never a * silent bypass (RUNTIME §8). A **BUY** carries no bounded-risk leak (the guard only ever UNLOCKS a * far-OTM bid), so an unresolvable BUY takes the symmetric path unflagged. * * Everything here is pure (no wall clock, no RNG — RUNTIME §0). */ import type { Right } from "../bus/index.ts"; /** Coarse moneyness of the resting leg vs the decision-time underlier — the axis the * DIRECTIONAL fill guard keys on. * `deep_otm` is the far wing where a resting OFFER has no lifter (fantasy) while a resting BID is * a real crossing-print fill. Caller-supplied; **absent ⇒ the symmetric legacy path** (the guard * cannot fire on an unclassified leg). */ export type Moneyness = "itm" | "atm" | "otm" | "deep_otm"; /** * The coarse **moneyness bucket boundaries** — GENERIC market-convention cutoffs (NOT fitted * calibration constants; they carry no strategy edge). A leg within {@link MONEYNESS_ATM_BAND} of * spot is at-the-money; a leg at least {@link MONEYNESS_DEEP_OTM_FRAC} out of the money is the * `deep_otm` far wing the directional guard keys on. These are the ONLY end-to-end definition of * "far-OTM" (a genuine fork, kestrel-9gu.6): the conservative default is a generous wing so a * standalone far-OTM short is caught by the cap rather than slipping through. Tuning them is a * data-versioned change, but they are deliberately round and public — a moneyness bucket boundary * is a market convention, not a private threshold. */ export declare const MONEYNESS_ATM_BAND = 0.02; /** @see MONEYNESS_ATM_BAND — the far-OTM wing boundary (fraction of spot out of the money). */ export declare const MONEYNESS_DEEP_OTM_FRAC = 0.15; /** * Classify a leg's coarse {@link Moneyness} from the decision-time underlier — strike vs spot, by * right. Pure. Returns `undefined` when spot is unresolvable (`undefined` / non-positive): the * caller must NOT read that as near-the-money (which would silently bypass the directional guard) — * it fails closed to the conservative wing (see {@link guardIntent}, kestrel-9gu.6). * * The OTM fraction is signed toward out-of-the-money: `(strike − spot)/spot` for a CALL, its mirror * for a PUT. `≥ MONEYNESS_DEEP_OTM_FRAC` ⇒ `deep_otm`; `> MONEYNESS_ATM_BAND` ⇒ `otm`; within the * band ⇒ `atm`; below ⇒ `itm`. */ export declare function classifyMoneyness(spot: number | undefined, strike: number, right: Right): Moneyness | undefined; /** * A leg's directional-guard verdict — the ONE value the engine authors and the fill model consumes. * Produced by {@link guardIntent} at submission (from the authoring side) or read back off a resting * order by {@link guardedIntentOf} (from the fill side); applied by {@link guardedPfill}. * * `moneyness` absent ⇒ the leg is UNCLASSIFIED and takes the symmetric path (a spot leg, or a BUY * whose spot was unresolvable) — the guard cannot fire. `covered` is meaningful only on a SELL. * `unresolved` is `true` ONLY on a SELL that had to fail closed to the conservative wing, so the * caller can log the fork. */ export interface GuardedIntent { readonly side: "buy" | "sell"; readonly moneyness?: Moneyness; readonly covered?: boolean; readonly unresolved: boolean; } /** What {@link guardIntent} needs to reach a verdict: the leg, and HOW to resolve the underlier. * `strike`/`right` are BOTH present for an option leg and BOTH absent for a spot leg (ADR-0017). */ export interface GuardIntentInput { readonly side: "buy" | "sell"; /** The option strike; ABSENT for a spot/equity leg (ADR-0017). */ readonly strike?: number; /** The option right; ABSENT for a spot/equity leg (ADR-0017). */ readonly right?: Right; /** The decision-time underlier resolver — called by the owner at most once, and ONLY when an * option leg needs classifying (a spot leg never resolves spot). `undefined` ⇒ unresolvable. */ readonly resolveSpot: () => number | undefined; } /** * Author a {@link GuardedIntent} for a leg at the moment of the decision (kestrel-9gu.6 / * kestrel-vav2) — the ONE place a leg's wing, covered-state and fail-closed fork are decided, so the * far-OTM standalone SELL cap, the covered-wing exemption, and the BUY crossing unlock all run END * TO END through the fill model. Pure given `resolveSpot` (no clock/RNG). * * - **Spot leg** (no strike/right, ADR-0017) — no wing: the far-OTM guard is an option concept with * no spot analogue, so no classification is attempted and spot is never resolved. * - **BUY** — the guard only ever UNLOCKS a far-OTM bid (crossing-print), never a bounded-risk leak, * so an unresolvable spot is safe: classify when we can, else take the symmetric path (no * moneyness). `covered` is not meaningful for a buy. * - **SELL** — the engine's never-naked boundary authors only single-leg closes of a held long, which * are STANDALONE offers in the fill-model sense (`covered: false`) — a genuine multi-leg * combo-anchored short is not authored here (a FORK; if one is ever authored it must set * `covered: true` explicitly). If spot is unresolvable we cannot classify the wing, so we **fail * closed** to the conservative far-OTM cap (`deep_otm`, standalone) and flag it — NEVER a silent * default and never a silent bypass (BOUNDED-RISK / never-naked, RUNTIME §8). */ export declare function guardIntent(input: GuardIntentInput): GuardedIntent; /** Read the {@link GuardedIntent} back off a resting order — the fill side of the seam. The verdict * was reached at submission ({@link guardIntent}) and threaded onto the order; the fill model never * re-classifies (it has no decision-time underlier, and a second classification is a second truth). * An order carrying no `moneyness` is UNCLASSIFIED ⇒ the symmetric path. `unresolved` is the * author's log-worthy fork and is not re-litigated here, so it reads `false`: the fail-closed * verdict is already baked into `moneyness`/`covered`. */ export declare function guardedIntentOf(order: { readonly side: "buy" | "sell"; readonly moneyness?: Moneyness | undefined; readonly covered?: boolean | undefined; }): GuardedIntent; /** * The far-OTM SELL cap. Resting an OFFER to SELL far-OTM premium is * **fantasy** — nobody lifts the worthless wing on a thin book, so the honest hazard is ~no-fill. * We **do not hard-zero it** (a probability of exactly 0 is brittle and un-gradable, and a rare * lift is not literally impossible); we **floor it** to this small positive value — so far below a * typical fill threshold that any standalone far-OTM short DROPS the package under complete-fill-only * (fail-closed in practice), yet the cell stays gradable. */ export declare const SELL_FAR_OTM_CAP = 0.01; /** * The far-OTM BUY crossing-print multiplier. Resting a penny BID to * BUY cheap far-OTM is **real**: holders of near-worthless OTM options DUMP to any bid into expiry, * and their market SELL crosses our resting bid at our price. The dump flow concentrates at the bid, * so the buy-side far-OTM leg fills MORE than the symmetric touch implied — a modest (>1) unlock, * clamped to 1. NOT invented high — calibrated up from the live crossing-print ledger. */ export declare const BUY_FAR_OTM_CROSSING = 1.5; /** * Apply a {@link GuardedIntent}'s directional asymmetry to a **symmetric** maker hazard `pSym` — * the ONE far-OTM cap decision, and the seam the never-naked property is enforced at. The guard * fires ONLY in the far-OTM wing; near-the-money (itm/atm/otm) and an **unclassified** leg * (`moneyness` absent) are returned UNCHANGED — the bounded short-premium that SELLS near strikes is * preserved, and callers that want the guard MUST author a classified GuardedIntent. * * - BUY far-OTM → `× BUY_FAR_OTM_CROSSING`, clamped to 1 (the real crossing-print dump-flow fill). * - SELL far-OTM **standalone** → floored/capped to `SELL_FAR_OTM_CAP` ≈ no-fill (the no-lifter fantasy). * - SELL far-OTM **covered** (covering wing of a defined-risk package) → UNCHANGED (fills as a combo, * anchored by its near leg; penalizing it would silently kill every defined-risk spread). * * Pure. `covered` defaults to standalone. Never hard-zeros: the cap is a positive floor. */ export declare function guardedPfill(pSym: number, guarded: GuardedIntent): number; //# sourceMappingURL=guarded-intent.d.ts.map