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.

117 lines 7.48 kB
/** * # engine/orgfacts — the book ledger + the OrgFacts backing (RUNTIME §2, §5) * * The engine's realization of the {@link OrgFacts} seam from `src/series`: the path-scoped * **org facts** that exist only in an acting session (`pnl`, `fills.avg_px`, `fills.count`, * `plan(x).state`, `plan(x).rungs`, `plan(x).fills`). `src/series` resolves market facts from * canonical state and delegates *org* facts to this backing; the trigger evaluator therefore * reads a plan-state chain (`plan(chase).state eq fired`) or a book P&L guard * (`pnl lt -200`) through here. * * The single mutable substrate is the {@link BookLedger}: the engine folds every observed * `ORDER fill` into it and stamps each plan's lifecycle state onto it, and {@link EngineOrgFacts} * is a **pure read view** over that ledger (it never mutates, never throws — an unresolvable * path reads {@link UNKNOWN}, RUNTIME §8). Because the evaluator's provider closes over the * same ledger instance, an org fact is always current as of the event being swept. * * ## P&L is mark-to-intrinsic (the honest daily fact) * `pnl` = realized cash-flow (`+` on sells, `−` on buys, per-contract × multiplier) **plus** * the residual open position marked at its **intrinsic** at the current spot — the same * `intrinsic(spot, strike, right)` the fill engine settles against (RUNTIME §6). Before any * spot is known the open mark contributes `0`; with no fills at all `pnl` is `0` (a flat book, * not UNKNOWN — a resolved fact). */ import type { PathSegment } from "../lang/index.ts"; import type { Right } from "../bus/index.ts"; import { UNKNOWN, type Resolved, type Unknown, type OrgFacts } from "../series/index.ts"; /** One observed fill folded into the ledger — a single leg that actually traded. * `strike`/`right` are BOTH present for an option leg and BOTH absent for a spot/equity leg * (ADR-0017 — a spot instrument has neither, and a fictional strike is never written). */ export interface LedgerFill { readonly plan?: string; readonly ref: string; readonly instrument: string; readonly side: "buy" | "sell"; readonly qty: number; readonly px: number; readonly strike?: number; readonly right?: Right; } /** The per-instrument contract multiplier (dollars per point). Injected so the ledger scales * intrinsic marks and cash flows to dollars exactly as the fill engine does. */ export type MultiplierOf = (instrument: string) => number; /** * The single mutable org substrate. The engine folds fills and stamps plan states; the read * view ({@link EngineOrgFacts}) and the engine both hold the same instance so facts are always * current. Deterministic: fills are appended in observation order and every derived number is a * pure function of that ordered log + the current spot. */ export declare class BookLedger { #private; constructor(multiplierOf: MultiplierOf); /** Fold one observed fill. Increments the owning plan's fill count. */ recordFill(fill: LedgerFill): void; /** Stamp a plan's current lifecycle state (read by `plan(x).state`). */ setPlanState(plan: string, state: string): void; /** Record that a plan placed another reload rung (read by `plan(x).rungs`). */ setPlanRungs(plan: string, rungs: number): void; /** Update the last observed spot for an instrument (drives the open mark). */ setSpot(instrument: string, px: number): void; /** The last observed spot for an instrument, or `undefined`. */ spotOf(instrument: string): number | undefined; /** Book realized cash-flow (`+` sell, `−` buy) + residual open position marked at intrinsic, * per-contract × multiplier (see file header). A flat/empty book is `0`, never UNKNOWN. */ pnl(): number; /** Quantity-weighted average fill price across every observed fill, or UNKNOWN with no fills. */ avgFillPx(): number | typeof UNKNOWN; /** Total number of observed fills (lots). */ fillCount(): number; /** Publish (upsert) a child scope's org aggregate — the write side of `children(any).<fact>`. * The parent Pod node receives each child's own aggregate and folds it here; deterministic, a * pure upsert keyed by `(child, fact)`. */ setChildFact(child: string, fact: string, value: number): void; /** A **monotone display summary** of a child-scoped fact across every child that has published it: * `any` reads the worst (**max**), `all` reads the best (**min**). UNKNOWN when no child has * published the fact. This is a comparator-blind scalar and is therefore used ONLY for display / * inspection panes — a *guard* comparison (`children(any|all).<fact> OP …`) must NOT fold to this * scalar, because the ∃/∀ meaning depends on the comparator direction (a `<` guard inverts which * child is the deciding one). The trigger evaluator resolves guards through {@link childValues} * and folds the comparator per member instead. Order-independent (max/min commute) ⇒ deterministic. */ childFact(fact: string, quant: "any" | "all"): number | Unknown; /** The per-member value vector for a child-scoped fact — every child that has published `fact`, * in deterministic (child-name-sorted) order. The comparator-honest input to the ∃/∀ fold: the * trigger applies the authored comparator to each member, then combines by quantifier (`any` = ∃, * `all` = ∀), so a `<`/`<=` guard can never invert (the fail-open {@link childFact} would hide). * Empty when no child has published the fact — the caller reads that as a genuinely unresolvable * quantified path and de-arms with a logged reason (RUNTIME §8), never a silent pass. */ childValues(fact: string): readonly number[]; planState(plan: string): string | undefined; planRungs(plan: string): number; planFills(plan: string): number; /** A deterministic, serializable snapshot for the determinism certification (RUNTIME §7). */ dump(): { fills: readonly LedgerFill[]; planState: readonly (readonly [string, string])[]; planRungs: readonly (readonly [string, number])[]; planFills: readonly (readonly [string, number])[]; spot: readonly (readonly [string, number])[]; childFacts: readonly (readonly [string, readonly (readonly [string, number])[]])[]; }; } /** * The pure read view over a {@link BookLedger} that satisfies the `src/series` {@link OrgFacts} * seam. Resolves the v1 org vocabulary and reads UNKNOWN for anything else (fail-closed, * absent-with-reason — the referencing statement de-arms, it never crashes, RUNTIME §8). */ export declare class EngineOrgFacts implements OrgFacts { #private; constructor(ledger: BookLedger, onUnresolved?: (reason: string) => void); resolve(segments: readonly PathSegment[]): Resolved; /** Resolve a **quantified** org operand (`children(any|all).<fact>`) to the vector of per-member * values so the trigger folds the comparator per member (∃/∀), honoring the comparator direction. * UNKNOWN (with the same loud, path-naming de-arm as {@link resolve}) when the path is genuinely * unresolvable — an unknown quantified head, a missing field, or a pod where no member has * published the fact (fail-closed, never a silent pass — RUNTIME §8). */ quantify(segments: readonly PathSegment[]): readonly number[] | Unknown; } //# sourceMappingURL=orgfacts.d.ts.map