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
TypeScript
/**
* # 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