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.
80 lines • 5.46 kB
TypeScript
/**
* # bus/anchor-ledger — the SPOT-anchor UNKNOWN-window ledger + fail-closed under-report validator (ven.4)
*
* The de-arm PRIMITIVE already lives in the series layer: an unresolvable value anchor reads
* UNKNOWN (`CanonicalState.spot: number | Unknown`) and any statement/detector referencing it
* de-arms with a logged reason — it can never fire on a value it does not have (fail-closed,
* RUNTIME §3/§8). What was missing is a **LEDGER** of those value gaps: a past-only record a
* consumer can read so it is never misled into thinking a gapped/dead-feed window carried a real
* value.
*
* ## ONE TRUTH — the ledger records EXACTLY the de-arm's windows (never a forked concept)
* The value anchor is UNKNOWN precisely while no SPOT for the signal instrument has been folded —
* the exact condition `CanonicalState.applyEvent` uses to set `spotVal` (a `TICK`/`SPOT` event for
* the matching instrument; RUNTIME §2). This module re-derives THAT SAME predicate at the bus layer
* — it does not import the series layer (the module spine is `series → bus`, never the reverse), so
* the equivalence is proved event-by-event by the ven.4 suite rather than by a cross-layer call.
* The ledger's covered seqs therefore equal the seqs where `CanonicalState.spot === UNKNOWN`: the
* detectors de-arm across exactly the windows the ledger records. One truth, not two.
*
* ## PAST-ONLY / NO-LOOKAHEAD (RUNTIME §0)
* A window is derived by a single forward replay; every `{start,end,dur}` bound is an
* already-elapsed event. A window that resolves ends at the LAST still-UNKNOWN event — the event
* *before* the resolving SPOT — never the resolver and never a synthetic future sentinel. A window
* still open at end-of-tape ends at the last elapsed event. `dur = end.ts - start.ts`. No wall
* clock, no RNG: same bus ⇒ byte-identical ledger (the determinism invariant).
*
* ## NO UNDER-REPORT — honest BY CONSTRUCTION (kestrel-voy9)
* The ledger can never under-report the gaps, because {@link spotAnchorUnknownWindows} IS the sole
* derivation: it replays the de-arm predicate forward and records EXACTLY the seqs the anchor read
* UNKNOWN (a dropped window, a later start, an earlier end, a shortened `dur` are all unrepresentable
* — the derivation emits the real span or nothing). The ONE-TRUTH property is proved end-to-end in
* the ven.4 suite by driving this real derivation and asserting its covered seqs BYTE-MATCH the
* series-layer `CanonicalState.spot === UNKNOWN` primitive, event-by-event, across a resolving, a
* never-resolving, and a px-blind bus. There is NO standalone under-report validator: a guard that
* only ever re-checks this same constructor's own honest output is a tautology (it can never fire),
* and no production path SUPPLIES a foreign ledger to validate — so the honesty lives in the
* construction, not in an unreachable export.
*/
import type { BusEvent } from "./types.ts";
/** A `(seq, ts)` reading — an already-elapsed bus event, referenced by both the writer's monotonic
* `seq` and its injected `ts` (RUNTIME §0/§1). */
export interface SeqTs {
readonly seq: number;
readonly ts: number;
}
/**
* One window the SPOT/value anchor was UNKNOWN, with PAST-ONLY bounds. `start` is the first event at
* which the anchor read UNKNOWN (session open / META — the LEADING pre-first-spot span); `end` is the
* LAST still-UNKNOWN event (never the resolving SPOT, never a future sentinel); `dur` is the elapsed
* span `end.ts - start.ts`. The value anchor is STICKY (see {@link spotAnchorUnknownWindows}), so a
* well-formed session yields exactly ONE such window — the leading one — never a mid-session re-lapse.
*/
export interface AnchorWindow {
readonly start: SeqTs;
readonly end: SeqTs;
readonly dur: number;
}
/**
* Derive the SPOT-anchor UNKNOWN-window ledger from a bus, purely and deterministically
* (RUNTIME §0). A single forward replay: the anchor is UNKNOWN until the first px-bearing SPOT for
* `instrument` is folded, at which point it resolves.
*
* The value anchor is **STICKY**: `CanonicalState.spotVal` is set once by `onSpot` and, on a
* well-formed tape, never reverts (barring a `CanonicalState` reset, which this replay does not
* model), so the `anchorKnown` latch below likewise never resets. This replay therefore records
* **exactly the LEADING window** — the pre-first-spot gap — and, when no SPOT ever resolves the
* anchor, the whole tape as one window. It does NOT record a mid-session lapse back to UNKNOWN: that
* "gapped/dead-feed mid-session staleness" is a SEPARATE primitive concern (a staleness/heartbeat
* backstop over `spotTs`, not the value-anchor de-arm this ledger mirrors) and is out of scope here —
* mirroring it would be forking a second window concept, which ONE TRUTH forbids. Every bound is
* past-only. The input is never mutated.
*
* @param events the bus, in `seq` order (as the writer emits it).
* @param instrument the signal instrument whose value anchor is tracked (mirrors
* `CanonicalStateConfig.instrument`).
* @returns the leading window the anchor was UNKNOWN (0 or 1 windows), in `seq` order — the same span
* the value-anchor de-arm covers.
*/
export declare function spotAnchorUnknownWindows(events: readonly BusEvent[], instrument: string): readonly AnchorWindow[];
//# sourceMappingURL=anchor-ledger.d.ts.map