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.

103 lines (102 loc) 6.15 kB
/** * # grade/measurement — alpha measurements carry a KIND, and kinds never blend (kestrel-a57.6) * * A measurement of alpha is not one number; it is a number **plus the evidence semantics that produced it**. * The three kinds carry DIFFERENT evidence, so silently blending or ranking one against another is a category * error — the same class of error ADR-0006 forbids across measurement *versions* ("EVs across measurement * versions refuse naive comparison"). This module is the KIND-axis sibling of that rule: **alphas across KINDS * refuse naive comparison**, and the enforcement is the TypeScript compiler. * * 1. RULE ALPHA — a MECHANICAL TEMPLATE run over EVERY eligible opportunity on a tape (systematic, no * discretion). Its evidence is the template's realized measure over the whole universe. * 2. SELECTION ALPHA — POINT-IN-TIME CURATION (the author PICKED which opportunities to act on), measured * VS A FROZEN COUNTERFACTUAL on the SAME FUTURE TAPE (what those same picks, frozen, * would have realized). Its evidence is SELECTION skill: realized minus the frozen leg. * 3. DIAGNOSTIC — exploratory / inspection only. Carries an explicit `non_promotable: true` marker and * has NO ordering surface at all (no `compare`, no `rank`, no `promote`). * * ## The barrier (compile-time) * Each kind is a distinctly **branded** record (a private `unique symbol` phantom brand ⇒ nominal, mutually * non-assignable, even if fields ever coincided). `compare` / `rank` / `promote` are pinned with `NoInfer<M>`: * the measurement type `M` is inferred ONLY from the first parameter, so a second argument of a different kind * has nothing to unify with and fails to type-check. `PromotableAlpha` excludes diagnostics, so the diagnostic * kind has no accepting overload anywhere — its only surface is construction and inspection. * * ## Diagnostics never promote (dual gate — fail-closed) * (compile) `promote<M extends PromotableAlpha>` will not bind a `DiagnosticMeasurement`; (runtime) if a caller * bypasses the types (an `any` / erased boundary), {@link promote} returns a typed refusal — `{ ok: false, * reason, kind }` — never throws, never silently succeeds. This mirrors the module's fail-closed convention * ({@link bankableEv} floors by RETURN; the receipt projector degrades with a logged reason) rather than * inventing a new error style. * * Ranking is on ONE named scalar per kind — never a blended omnibus (honors the m9i no-omnibus rule). Pure and * deterministic: no wall clock, no RNG; off the runtime/record path (no bus/Blotter/grammar/golden bytes move). */ /** Build a {@link RuleAlphaMeasurement}. Pure: stamps the discriminant kind; no clock, no RNG. */ export function ruleAlpha(input) { return { ...input, kind: "rule_alpha" }; } /** Build a {@link SelectionAlphaMeasurement}. Pure: stamps the discriminant kind; no clock, no RNG. */ export function selectionAlpha(input) { return { ...input, kind: "selection_alpha" }; } /** Build a {@link DiagnosticMeasurement}. Pure: stamps the kind + the explicit non-promotable marker. */ export function diagnostic(input) { return { ...input, kind: "diagnostic", non_promotable: true }; } /** * The ONE named scalar a kind is ordered by: * • rule alpha → `realized_r` (the mechanical rule's realized measure over its whole universe); * • selection alpha → `selection_edge` = realized − frozen counterfactual (the marginal value of the curation). * Never a composite — the honesty rule is that ranking is on a single, named measure. */ function scalarOf(m) { if (m.kind === "rule_alpha") { return { name: "realized_r", value: m.realized.realized_r }; } return { name: "selection_edge", value: m.realized.realized_r - m.frozen_counterfactual.realized.realized_r, }; } /** * Compare two measurements OF THE SAME KIND on that kind's one named scalar. `M` is inferred solely from `a`; * `NoInfer<M>` pins `b` to the same kind, so a cross-kind second argument fails to compile. Diagnostics are not * in {@link PromotableAlpha}, so they have no `compare` overload at all. Pure, total, deterministic. */ export function compare(a, b) { const sa = scalarOf(a); const sb = scalarOf(b); const order = sa.value > sb.value ? 1 : sa.value < sb.value ? -1 : 0; return { order, measure: sa.name }; } /** * Rank measurements OF THE SAME KIND, descending by that kind's one named scalar. `M` is inferred solely from * the first element; every remaining element is pinned to the same kind via `NoInfer<M>`, so a mixed-kind list * (or a list containing a diagnostic) fails to compile. Pure: returns a sorted COPY; the input is never mutated. */ export function rank(xs) { return [...xs].sort((a, b) => scalarOf(b).value - scalarOf(a).value); } /** * Promote a promotable alpha to a candidate. At compile time only {@link PromotableAlpha} binds `M`, so a * diagnostic has no accepting path. At RUNTIME this is defense-in-depth for a caller that bypassed the types * (an `any` / erased boundary): it inspects the actual object and FAILS CLOSED — a diagnostic, a non-promotable * marker, or any unrecognized kind returns a typed refusal with a logged reason. It never throws, never silently * succeeds. Pure and deterministic. */ export function promote(candidate) { // Read the runtime shape through a widened view so the guard is NOT narrowed away by the compile-time type — // a types-bypassed caller may hand us an object whose real kind is "diagnostic". const probe = candidate; const kind = typeof probe.kind === "string" ? probe.kind : "unknown"; const isPromotableKind = kind === "rule_alpha" || kind === "selection_alpha"; if (probe.non_promotable === true || !isPromotableKind) { return { ok: false, reason: `diagnostic / non-promotable measurement can never promote (kind=${kind})`, kind, }; } return { ok: true, candidate }; }