/**
 * # adapters/broker/ibkr — the IB Gateway PAPER ORDER FACE (kestrel-7o2.8)
 *
 * The first Kestrel module allowed to name `placeOrder`, and it may do so ONLY behind the 7o2.9
 * safety envelope. It is a {@link BrokerAdapter} — therefore a {@link Gate} — so it drops into the
 * ONE execution seam the engine already fires through (`engine/plans.ts`), and `sim | paper | live`
 * stays one Session path where only the gate differs.
 *
 * ## A TRANSMITTER, never a re-pricer
 * The {@link OrderIntent} arrives FULLY RESOLVED: a numeric `px` the engine already earned (with its
 * `sourceAnnotation` receipt), never-naked-checked, directional-guard evidence attached. This adapter
 * therefore **never touches `intent.px`** — it does not round it, does not snap it "to improve", does
 * not substitute a mid (a mid is a HEALTH SIGNAL, never a price and never a value, RUNTIME §4). It
 * transmits that exact number as an IB `LMT` limit price, or it REFUSES. There is no third path.
 *
 * ## PAPER ONLY. `live` is refused at construction.
 * Live routing is kestrel-7o2.10 and lands only behind the human-signed {@link LiveArm}. A `live`
 * config here is a typed {@link IbkrOrderRefused} `mode-gate` — fail-closed, never a port fact.
 *
 * ## The walls, ALL of them BEFORE the wire
 * `submit` runs a guard chain and only then calls `placeOrder`. Any wall ⇒ a typed throw and
 * **`placeOrder` is never called** (the {@link IbOrderClient} spy in the tests is the witness):
 *
 *  0. **KILL-SWITCH** (7o2.9) — tripped (operator STAND_DOWN, or a reconciliation break) ⇒
 *     {@link ClampRefused} `killed`. Consulted first so a halted process refuses uniformly. On the
 *     `makeGate("paper")` path this is the SAME switch the seam's L0 clamp consults (makeGate threads
 *     it in): exactly ONE instance, so a trip in EITHER layer halts the whole path — never two
 *     independent switches that cannot reach each other.
 *  1. **THE INTENT ITSELF** — `qty` a positive whole number, `px` a positive finite price, the `ref`
 *     not already on the ledger. IB is never this system's validator: a SELL of `qty: -3` would
 *     sign-invert its way through the never-naked wall and put `totalQuantity: -3` on the wire.
 *  2. **CONTRACT** — the leg must already be resolved to the gateway's OWN definition (7o2.6). An
 *     unresolved leg is refused; an identity is NEVER guessed on the hot path.
 *  3. **NEVER-NAKED** — the projected position may never go NEGATIVE. It is measured against the
 *     **BROKER's own reported truth MINUS the quantity our WORKING (unfilled) SELLs have already
 *     promised away** — the worst case, once every order in flight has filled. A long option (max loss
 *     = premium) and a long equity are defined risk; an uncovered short is unbounded risk, and no
 *     budget makes it acceptable, so this is a doctrine refusal rather than a ceiling.
 *
 *     A RESTING sell does not move broker truth until it fills. Reading truth alone therefore let a
 *     SECOND sell of the same long clear this wall — and when both filled, this module opened an
 *     uncovered short (kestrel-7o2.8, blocker 1). Hence the RESERVATION: a working sell holds its
 *     quantity against the key from SUBMIT until a terminal outcome releases it.
 *  4. **INTRINSIC FLOOR** — a SELL is floored at intrinsic. Below it ⇒ refused. And because the
 *     adapter may not re-price, an UNKNOWN intrinsic also refuses (the floor is never *assumed*
 *     satisfied — that would be a silent default, and this runtime has none).
 *  5. **L0 CLAMP** (7o2.9 {@link RiskLimits}) — `maxOrderQty`, `maxPositionQty`, `maxNotionalUsd`
 *     (`notional = px x qty x the VENUE's multiplier`). Over ANY ceiling ⇒ {@link ClampRefused}, and
 *     NOTHING is transmitted. This is ONE of TWO INDEPENDENTLY-PROVEN ceilings, defense-in-depth
 *     (ADR-0034 §4) — NOT a subordinate backstop to a single canonical owner. The OTHER is the seam's
 *     `makeLiveClamp` ABOVE the adapter, which on the `makeGate("paper")` path wraps this face, SHARES
 *     its ONE kill-switch, and enforces the SAME limits + the SAME true per-contract multiplier (all
 *     threaded in via {@link IbkrBroker.limits} / {@link IbkrBroker.multiplierOf}). Each layer is proven
 *     load-bearing on its own: this WALL 5 is the ONLY ceiling when the face is reached BARE (unit tests,
 *     the env-gated dry-run/equity-order proof scripts — where no seam wraps it, and fail-closed forbids
 *     a bare venue face with no ceiling), and a mutation test gutting it goes RED; the seam is the only
 *     ceiling over a WALL-5-less adapter, and a mutation gutting the seam goes RED too. Same numbers,
 *     never divergent — two barriers, not one owner with a shadow.
 *  6. **PRICE ANCHOR** ({@link PriceAnchor}) — the walls above bound SIZE; this one bounds the PRICE.
 *     The order's `@fair` must lie INSIDE the venue's OBSERVED two-sided book, and that book must be
 *     LIVE: delayed/frozen data is a HEALTH SIGNAL, never a price anchor. A fair above the ask (or
 *     below the bid) means the ANCHOR ITSELF is lying, and no size ceiling can rescue a bad price — so
 *     no authorizable ticket is produced at all. (Live, a ticket printed CLEARED at `@fair = 0.9229`
 *     against an observed offer of `0.73`: "a BUY never bids above fair" did zero work, because fair
 *     was above the offer. `@fair`'s own derivation is the engine's — kestrel-ku99 — but a consumer
 *     that cannot corroborate its anchor must fail closed.)
 *
 * {@link IbkrBroker.preflight} runs walls 0–6 and returns the resolved {@link OrderTicket} **without
 * transmitting anything** — that is what the dry-run ticket prints, and it is why a ticket can be
 * reviewed by a human before a single byte reaches the venue.
 *
 * ## The inbound pump — IB callbacks → the EXISTING closed ORDER vocabulary
 * The engine already folds `ORDER place | fill | cancel | reject` (bus/types.ts, a CLOSED
 * {@link OrderAction} set). No new event kind is invented:
 *
 *  - `openOrder` / a live `orderStatus` → **`place`** (the venue's acknowledgment), emitted at most
 *    ONCE per ref. IB duplicates `orderStatus` routinely, so every mapping here is idempotent.
 *  - `execDetails` → **`fill`**, one per NEW `execId`. This — not `orderStatus.filled` — is the fill
 *    source of truth: `orderStatus` reports a CUMULATIVE count and repeats itself, while each partial
 *    fill has its own `execId`. So PARTIAL and MULTI-fill both fall out for free, each at the price
 *    the venue actually printed, and a replayed callback can never double-count.
 *  - `orderStatus` `Cancelled`/`ApiCancelled` → **`cancel`**; `Inactive`, or an order-scoped fatal IB
 *    `error`, → **`reject`**. Both terminal, both latched.
 *  - `commissionReport` → the LEDGER only. It is not an ORDER event: the vocabulary is closed, and a
 *    commission is not an order action.
 *
 * Causal order is preserved (a `fill` that somehow beats its acknowledgment still emits `place`
 * first), so a replay of the same callback sequence yields a byte-identical event stream.
 *
 * ## The reconciliation ledger — the BROKER's report is authoritative, and the trip fires AT OBSERVATION
 * {@link IbkrBroker.ledger} holds per-order state. {@link IbkrBroker.positions} is the venue's own
 * signed net position — its `position` push as a BASELINE, plus the executions observed SINCE it —
 * never the engine's expectation. The push is SUBSCRIBED at construction (`reqPositions`): IB does not
 * emit `position` unsolicited, and without the subscription "broker truth" is really just the engine's
 * own mirror wearing that label.
 *
 * The break TRIPS THE {@link KillSwitch} FROM INSIDE THE `execDetails` HANDLER — the moment the venue's
 * report proves an over-fill, a post-terminal fill, or ANY key gone negative. **An invariant is only
 * real if something calls it**: the shipped version could only trip via {@link IbkrBroker.reconcile},
 * which nothing in production ever called, so an over-fill sat at −3 with the switch un-tripped and the
 * next submit still accepted (blocker 3). `reconcile()` survives as an explicit AUDIT — it compares the
 * engine/Blotter EXPECTED snapshot against broker truth — but it is no longer the only thing that can
 * trip. Never a silent divergence, and the engine never "corrects" the broker.
 *
 * ## Determinism at the edge
 * The IB client and the order-id minter are INJECTED, so the unit tests drive an in-memory double
 * with no socket, no timer, and no real order. Nothing here reads a wall clock: ORDER events are
 * stamped with the `now` the driver pins on the gate (RUNTIME §0), exactly as `SimGate` is.
 */
import { EventName, TimeInForce } from "@stoqey/ib";
import type { Contract, Order } from "@stoqey/ib";
import type { BrokerAdapter, KillSwitch, PositionKey, PositionSnapshot, RiskLimits } from "../../broker.ts";
import type { OrderIntent } from "../../../engine/index.ts";
import type { NewBusEvent, Right } from "../../../bus/index.ts";
import type { IbkrConfig } from "./config.ts";
import type { IbkrContract } from "./contract.ts";
import type { IbkrTransport } from "./transport.ts";
/**
 * Why the paper order face REFUSED to transmit (kestrel-7o2.8) — a closed, typed vocabulary. Each of
 * these means the SAME thing about the wire: **nothing was sent**.
 *
 * `naked-short` / `below-intrinsic` / `intrinsic-unknown` are the RISK DOCTRINE walls (never-naked;
 * a SELL floored at intrinsic; a floor never *assumed* satisfied). They are DISTINCT from the 7o2.9
 * {@link ClampRefused} (a bounded-risk CEILING) because they are not ceilings at all — no budget
 * makes an uncovered short acceptable.
 */
export type IbkrOrderFailure = 
/** A `live` config reached the paper face. Live routing is 7o2.10, behind the human-signed arm. */
"mode-gate"
/** The intent itself is MALFORMED (qty ≤ 0 / fractional / non-finite; px ≤ 0 / non-finite; a ref
 * already on the ledger; a re-issued IB order id that would re-point correlation). Refused at wall 1
 * — IB is NEVER this system's validator, and a SELL of `qty: -3` must not be allowed to sign-invert
 * its way through the risk wall. */
 | "invalid-order"
/** The leg has no pre-resolved gateway contract — an identity is never guessed on the hot path. */
 | "unresolved-contract"
/** The resulting position has an UNBOUNDED (non-computable / infinite) max_loss — a naked short CALL
 * or a short EQUITY. Refused by the BOUNDED-RISK platform INVARIANT itself, REGARDLESS of policy
 * (kestrel-buos): no budget can satisfy `size × max_loss ≤ budget` when max_loss is infinite. */
 | "unbounded-risk"
/** The resulting position's max_loss is FINITE but exceeds the risk budget (`size × max_loss >
 * budget`). Bounded risk is the invariant; a budget that cannot cover the worst case refuses. */
 | "over-budget"
/** The order would open (or deepen) an UNCOVERED SHORT whose max_loss is finite and budgeted (a
 * cash-secured / naked PUT) while the no-uncovered-short POLICY is ON (the default — the 0DTE book
 * keeps it hard). An OVERRIDABLE policy, not the platform invariant (kestrel-buos): turn it off to
 * sell puts. The UNBOUNDED cases above are refused regardless of it. */
 | "naked-short"
/** A SELL priced BELOW the leg's intrinsic value. */
 | "below-intrinsic"
/** A SELL whose intrinsic is UNKNOWN — the floor cannot be proven, so it fails closed. Applies to
 * OPTIONS only: an equity has no optionality and its floor is 0 (kestrel-buos §e). */
 | "intrinsic-unknown"
/** No OBSERVED two-sided book for the leg (unquoted, dark, one-sided, crossed), or no `@fair` at
 * all. A price we cannot audit against the venue's own book is not authorizable. */
 | "anchor-unresolvable"
/** The book on offer is DELAYED/FROZEN. That is a HEALTH SIGNAL, never a PRICE ANCHOR (7o2.8). */
 | "anchor-stale"
/** The `@fair` RECEIPT cannot vouch for the price — a stale index, frozen input, or no ATM coverage
 * (kestrel-ltrf). `@fair` is UNDERLYING-anchored, not book-anchored, so it may LEGITIMATELY sit
 * away from a lagging/wide index-option quote; the wall gates on an UNTRUSTED receipt, NOT on a naive
 * fair-outside-[bid,ask] clamp (which defeats the whole point of a fresh-index fair). */
 | "fair-untrusted"
/** The venue's `placeOrder` threw — surfaced LOUD, never swallowed. */
 | "transmit-failed";
/**
 * The paper order face refused to transmit (kestrel-7o2.8) — a LOUD, typed, fail-closed refusal that
 * routes to STAND_DOWN. A DISTINCT class (not a bare `Error`, not {@link ClampRefused}) so a caller
 * can catch a DOCTRINE refusal specifically and can never mistake it for a bounded-risk ceiling.
 * NEVER carries a credential/account (the config is described through the redacting formatter).
 */
export declare class IbkrOrderRefused extends Error {
    readonly name = "IbkrOrderRefused";
    readonly failure: IbkrOrderFailure;
    /** The refused order's engine ref, when the refusal belongs to one. */
    readonly ref: string | undefined;
    constructor(failure: IbkrOrderFailure, reason: string, options?: {
        ref?: string | undefined;
        cause?: unknown;
    });
}
/** A listener over the IB event bus (the transport's convention — concrete closures are wrapped at
 * the registration site, so the `never[]` never escapes into a handler body). */
type IbListener = (...args: never[]) => void;
/**
 * The NARROW ORDER surface of the shared IB client. This is the ONE place in the package that names
 * `placeOrder`, and it exists only behind the guard chain above.
 *
 * Note the deliberate asymmetry with 7o2.5/7o2.6: the transport's {@link import("./transport.ts").IbClient}
 * and the contract layer's `IbContractClient` EXCLUDE every order method, so neither of them can
 * transmit even by reaching through the client. THIS view widens toward exactly two order calls (plus
 * the read-only position pull), and it is reached only via {@link orderClientOf} — a single, named,
 * auditable door. A test injects a double implementing exactly this; production passes the
 * transport's ONE shared client, so the feed face and the order face are two faces of ONE socket,
 * never a second connection.
 */
export interface IbOrderClient {
    /** Transmit an order. The `id` is the API client's order id (see {@link IbkrBrokerDeps.nextOrderId}). */
    placeOrder(id: number, contract: Contract, order: Order): unknown;
    /** Pull a resting order by its IB order id. */
    cancelOrder(orderId: number): unknown;
    /** Solicit the next valid order id (`nextValidId`) — the id sequence's origin. */
    reqIds(numIds?: number): unknown;
    /** Subscribe to the account's AUTHORITATIVE position push (`position` / `positionEnd`). READ-ONLY. */
    reqPositions(): unknown;
    on(event: EventName, listener: IbListener): unknown;
    removeListener(event: EventName, listener: IbListener): unknown;
}
/**
 * The ONE shared IB session, viewed through the ORDER surface (kestrel-7o2.8). The transport hands
 * out its single guarded client — throwing the typed connection error if the session is not connected
 * or has gone degraded, so an order can never be transmitted over a dead socket. This re-views it as
 * an {@link IbOrderClient}. The cast is the SINGLE, NAMED door through which order authority reaches
 * the socket; the real `IBApi` is a structural superset. There is never a second socket.
 */
export declare function orderClientOf(transport: IbkrTransport): IbOrderClient;
/**
 * The pre-resolved contract book — a SYNCHRONOUS leg → contract lookup.
 *
 * Why it exists: the {@link Gate} seam is synchronous (`submit(intent): string`), but the 7o2.6
 * resolution is a network round-trip. Resolving on the hot path would put a socket between the tick
 * and the order — so contracts are resolved AHEAD of it (at arm time) and looked up here. That is the
 * fire-then-inform discipline applied to identity: nothing slow, and nothing guessed, at the tick.
 * A leg that is not in the book is REFUSED (`unresolved-contract`), never resolved by hand.
 */
export interface ContractBook {
    get(leg: {
        readonly instrument: string;
        readonly strike?: number;
        readonly right?: Right;
    }): IbkrContract | undefined;
}
/** Build a {@link ContractBook} from contracts already resolved through 7o2.6's `resolveContract`.
 * Keyed by {@link positionKeyOf}, so the book, the never-naked wall, the L0 position ceiling and the
 * reconciliation ledger all key IDENTICALLY (no skew). */
export declare function contractBook(contracts: readonly IbkrContract[]): ContractBook;
/**
 * How the VENUE said it sourced a quote (IB's `marketDataType`: 1 live · 2 frozen · 3 delayed ·
 * 4 delayed-frozen). **Only `live` may ANCHOR a price.** Delayed/frozen data is an honest HEALTH
 * SIGNAL — "is this thing tradeable?" — and it is never a price and never a value (RUNTIME §4). A
 * quote whose provenance we cannot establish is `unknown`, which is likewise refused: fail-closed.
 */
export type QuoteFreshness = "live" | "frozen" | "delayed" | "delayed-frozen" | "unknown";
/**
 * The PRICE ANCHOR for one leg (kestrel-7o2.8): the venue's OBSERVED two-sided book, and the `@fair`
 * the engine anchored `intent.px` on — the two numbers whose disagreement the live proof exposed.
 *
 * The 2026-07-14 dry run printed a ticket carrying `@fair = 0.9229` against an observed offer of
 * `0.73` — 28% ABOVE the ask on a liquid ATM 0DTE leg — and every wall still printed CLEARED, because
 * the walls bound SIZE, not PRICE. "A BUY never bids above fair" did zero work because FAIR ITSELF was
 * above the offer. So the book is no longer merely printed beside the price: it BOUNDS it, at the one
 * seam every order path crosses ({@link IbkrBroker.preflight}).
 */
export interface PriceAnchor {
    /** The venue's observed BEST BID. */
    readonly bid: number;
    /** The venue's observed BEST OFFER. */
    readonly ask: number;
    /** The `@fair` the ENGINE resolved for this leg — the anchor `intent.px` was derived from. It is
     * UNDERLYING-anchored (the fresh index spot), NOT book-anchored: for a lagging/wide index-option
     * quote it may LEGITIMATELY sit away from the posted `[bid, ask]`, so it is NOT clamped to the book
     * (kestrel-ltrf). Non-finite ⇒ UNKNOWN ⇒ refused (never assumed — a silent default is never a
     * default here). */
    readonly fair: number;
    /** Whether the `@fair` RECEIPT vouches for the price (kestrel-ltrf). The ENGINE owns `@fair`'s
     * derivation and hands the adapter its verdict: `true` iff the receipt is live-anchored, ATM-covered,
     * and not stale/tainted. `false` ⇒ the anchor is UNTRUSTED ⇒ refused (`fair-untrusted`). This is
     * what REPLACES the round-1 naive fair-outside-[bid,ask] clamp — the wall gates on trust, not on
     * where fair happens to sit relative to a book that legitimately lags a fast index. */
    readonly fairTrusted: boolean;
    /** The provenance of `bid`/`ask`. Anything but `live` refuses: a price anchor never accepts
     * delayed/frozen input. */
    readonly freshness: QuoteFreshness;
}
/** Construction deps for {@link ibkrBroker}. Every non-determinism source (the socket, the order-id
 * minter) is injected, so the unit tests run with none of them. */
export interface IbkrBrokerDeps {
    /** The shared IB client's ORDER view — the transport's ONE session in production ({@link orderClientOf}). */
    readonly client: IbOrderClient;
    /** Contracts resolved AHEAD of the hot path (7o2.6). A leg absent from it is refused. */
    readonly contracts: ContractBook;
    /** Surface this adapter's fresh ORDER events onto the emitted bus — the SimGate `drain()` analogue.
     * Called after every `submit`/`cancel` AND after every inbound pump batch. */
    readonly drain: () => void;
    /** The L0 pre-transmit ceilings (7o2.9). Over ANY of them ⇒ {@link ClampRefused}, nothing transmitted. */
    readonly limits: RiskLimits;
    /**
     * The BOUNDED-RISK budget in dollars (kestrel-buos): a risk-INCREASING order is refused unless
     * `size × max_loss ≤ budget`. Absent ⇒ {@link RiskLimits.maxNotionalUsd} (a long's max_loss IS its
     * notional, so the two coincide there; the budget is what bounds a naked PUT, whose max_loss is
     * `(strike − premium) × multiplier` rather than the notional). Bounded risk is the PLATFORM
     * INVARIANT — an UNBOUNDED max_loss is refused at any budget.
     */
    readonly budgetUsd?: number;
    /**
     * The no-uncovered-short POLICY (kestrel-buos) — an OVERRIDABLE default, ON when absent. When ON (the
     * 0DTE prop book keeps it hard) an order that would open UNCOVERED short exposure is refused even
     * when its max_loss is finite and budgeted (i.e. the naked PUT is refused). When OFF, a budgeted
     * naked put is ALLOWED. The UNBOUNDED cases (naked call / short equity) are refused REGARDLESS — they
     * fail the platform invariant, not merely the policy. A default is not an invariant.
     */
    readonly noUncoveredShort?: boolean;
    /** The kill-switch to consult/trip. Absent ⇒ a fresh one ({@link IbkrBroker.killSwitch}). Share it
     * so an operator halt, a transport degradation, and a reconciliation break hit the SAME switch. */
    readonly killSwitch?: KillSwitch;
    /**
     * The leg's per-contract INTRINSIC value in dollars-per-point (the engine computes it from
     * canonical state — this adapter never derives a price). `undefined` ⇒ UNKNOWN, and a SELL then
     * fails closed rather than assuming its floor is satisfied.
     */
    readonly intrinsicOf: (leg: {
        readonly instrument: string;
        readonly strike?: number;
        readonly right?: Right;
    }) => number | undefined;
    /**
     * The leg's PRICE ANCHOR: the venue's OBSERVED two-sided book plus the `@fair` the engine priced
     * against (kestrel-7o2.8). REQUIRED — every order path crosses this wall, and there is no opt-out:
     * `undefined` (no observed book) REFUSES the order rather than transmitting against a price nothing
     * corroborates. Delayed/frozen input refuses too; a fair outside the book refuses outright.
     */
    readonly priceAnchorOf: (leg: {
        readonly instrument: string;
        readonly strike?: number;
        readonly right?: Right;
    }) => PriceAnchor | undefined;
    /** The engine/Blotter EXPECTED signed net positions — the side {@link IbkrBroker.reconcile} compares
     * AGAINST the broker's authoritative truth. Never used as a risk input (broker truth is). */
    readonly expectedPositions: () => PositionSnapshot;
    /**
     * The SETTLEMENT ORACLE (kestrel-7o2.24, ADR-0034 q3): the VENUE's own statement that this key's
     * expired position was cash-settled — position removed, cash posted. It is the ONLY thing that can
     * absolve a position's disappearance in {@link IbkrBroker.reconcile}, and it is supplied by the
     * VENUE composition (which reads the account's cash/expiry reports), never by this face.
     *
     * ABSENT ⇒ NOTHING IS EVER ABSOLVED — every disappearance trips, exactly as it did before this dep
     * existed. That is the fail-closed default and the reason the carve-out cannot leak into a caller
     * that did not ask for it.
     */
    readonly settlementOf?: (key: PositionKey) => SettlementReceipt | undefined;
    /** Mint the next IB API order id. A COUNTER seeded from the gateway's `nextValidId` — never an RNG,
     * even here at the edge. */
    readonly nextOrderId: () => number;
    /** The venue's positions as ALREADY KNOWN at construction (a resumed session's opening book). The
     * broker's `position` push and its own executions fold on top. Absent ⇒ flat. */
    readonly seedPositions?: PositionSnapshot;
    /** Reconciliation tolerance (absolute qty per key). Absent ⇒ `0` (exact match required). */
    readonly tolerance?: number;
    /** Time-in-force for transmitted orders. Defaults to `DAY` — an order that outlives the session is
     * an order nobody is watching. */
    readonly tif?: TimeInForce;
    /** Redacted-diagnostic sink (default no-op). Receives only already-redacted strings. */
    readonly log?: (line: string) => void;
}
/** Where one order stands. `working` is the venue's acknowledgment; the three terminals are latched
 * (a terminal order never re-emits, so a replayed callback cannot perturb the stream). */
export type OrderPhase = "pending" | "working" | "filled" | "cancelled" | "rejected";
/**
 * One order's line in the RECONCILIATION LEDGER (kestrel-7o2.8): what the ENGINE submitted beside
 * what the BROKER actually reported. The broker's side is AUTHORITATIVE — it is never overwritten by
 * an engine expectation, and a divergence between the two is a break, not a correction.
 */
export interface IbkrOrderRecord {
    /** The engine's ref (`intent.ref`) — the correlation key on every ORDER event. */
    readonly ref: string;
    /** The IB API order id this ref was transmitted under. */
    readonly ibOrderId: number;
    /** The resolved intent, verbatim — the ENGINE's side of the ledger (including its untouched `px`). */
    readonly intent: OrderIntent;
    /** The gateway's own contract definition this was transmitted against. */
    readonly contract: IbkrContract;
    /** ENGINE truth: what we asked for. */
    readonly submittedQty: number;
    /** BROKER truth: cumulative qty the venue reported EXECUTING (summed over distinct `execId`s). */
    readonly filledQty: number;
    /** BROKER truth: size-weighted average of the prices the venue actually printed. */
    readonly avgFillPx: number | undefined;
    /** BROKER truth: commission the venue reported (per `execId`, deduped). */
    readonly commissionUsd: number;
    /** BROKER truth: the venue's own last-reported remaining quantity. */
    readonly remainingQty: number | undefined;
    readonly phase: OrderPhase;
}
/**
 * A fully-guarded, NOT-YET-TRANSMITTED order (kestrel-7o2.8) — what {@link IbkrBroker.preflight}
 * returns and what the DRY-RUN prints for a human to review. It has cleared all four walls; it simply
 * has not been sent. Every number on it is READ, never invented: `limitPx` is the engine's own `px`
 * verbatim, `multiplier` is the VENUE's.
 */
export interface OrderTicket {
    readonly ref: string;
    /** The gateway's own resolved contract (7o2.6) — conId, OCC localSymbol, multiplier, expiry. */
    readonly contract: IbkrContract;
    /** The exact IB `Contract` that would go on the wire. */
    readonly ibContract: Contract;
    /** The exact IB `Order` that would go on the wire, minus the id (minted at transmit). */
    readonly ibOrder: Order;
    readonly side: "buy" | "sell";
    readonly qty: number;
    /** The ENGINE's price, verbatim. Never rounded, never snapped, never a mid. */
    readonly limitPx: number;
    /** The VENUE's contract multiplier (dollars per point). Read from the definition, never assumed. */
    readonly multiplier: number;
    /** `limitPx x qty x multiplier` — what the L0 notional ceiling bounds. */
    readonly notionalUsd: number;
    /**
     * The DEFINED-RISK max loss in dollars. For a BUY of a long option this IS the premium (the whole
     * never-naked claim: you cannot lose more than you paid). For a BUY of equity it is bounded by the
     * price paid. A SELL that closes a held long is RISK-REDUCING — its max loss is already borne, so
     * it adds `0`.
     */
    readonly maxLossUsd: number;
    /** The venue's signed net position in this leg BEFORE the order (broker truth). */
    readonly heldBefore: number;
    /** Quantity of this leg ALREADY PROMISED AWAY by SELLs that are working (transmitted, acknowledged,
     * NOT yet filled). A resting sell does not move broker truth until it fills, so it is RESERVED here
     * — otherwise a second sell of the same long reads the same `heldBefore` and clears too, and both
     * fill (kestrel-7o2.8 blocker 1: the uncovered short this module actually opened). */
    readonly workingSellQty: number;
    /** The WORST-CASE signed net position: `heldBefore - workingSellQty + signed(qty)` — what we hold
     * once this order AND every already-working SELL has filled. Never negative — never-naked. */
    readonly heldAfter: number;
    /** The intrinsic floor that applied, when one did (a SELL). */
    readonly intrinsicFloor: number | undefined;
    /** The PRICE ANCHOR this ticket cleared: the observed LIVE two-sided book, and the `@fair` inside
     * it. A ticket can only exist if its anchor was trustworthy, so this is a RECEIPT, not a hope. */
    readonly anchor: PriceAnchor;
    /** The L0 verdict. `preflight` only ever RETURNS on `cleared` — anything else THREW. */
    readonly clamp: "cleared";
    /** The engine's price-resolution receipt, carried onto the ticket so `@fair` is auditable. */
    readonly sourceAnnotation: string;
}
/**
 * The VENUE's own statement that a specific EXPIRED position was CASH-SETTLED — the position removed,
 * the cash posted. This is the ONE piece of evidence that can absolve a position's disappearance from
 * {@link IbkrBroker.reconcile}'s trip.
 *
 * It is READ FROM THE VENUE, never derived here. The adapter cannot compute a settlement (it has no
 * settlement price, no account cash, no clearing calendar) and it must never assume one — a position
 * that "should have" settled is not a position that DID. So this arrives through the injected
 * {@link IbkrBrokerDeps.settlementOf} oracle, whose absence means NO disappearance is ever absolved.
 */
export interface SettlementReceipt {
    /** The SIGNED net quantity the venue settled away. Must account for the ENTIRE expected position —
     * a receipt that explains only part of a disappearance explains none of it. */
    readonly qty: number;
    /** The settlement cash the venue posted, in dollars. `0` is LEGITIMATE (an option that expired
     * worthless — the common 0DTE end); the RECEIPT's existence is the evidence, not its size. A long
     * can only ever RECEIVE at settlement, so a negative value is refused. */
    readonly cashUsd: number;
    /** When the venue settled it (the venue's own timestamp, ms). Must not predate the leg's expiry. */
    readonly settledAt: number;
}
/**
 * One recorded SETTLEMENT — {@link IbkrBroker.reconcile}'s answer to a legitimately-expired position
 * that the venue removed and paid out. **This is a RECORD, not a correction**: the position is not
 * "reconciled away" silently; the disappearance is accounted for, on the ledger, with the receipt and
 * the expiry that justified it.
 *
 * It is deliberately NOT a Bus ORDER event. The {@link OrderAction} vocabulary is CLOSED
 * (`place | fill | cancel | reject`, bus/types.ts) — a settlement is not an order action, exactly as a
 * `commissionReport` is not (which likewise folds onto the LEDGER only). Promoting settlement to a
 * first-class Bus event would change `serialize(project(bus))` bytes repo-wide and needs its own ADR;
 * see the ADR-0034 q3 amendment.
 */
export interface SettlementRecord {
    readonly key: PositionKey;
    /** The engine-expected quantity that settled away (the receipt accounted for exactly this). */
    readonly qty: number;
    /** The venue's posted settlement cash. */
    readonly cashUsd: number;
    /** The leg's own expiry (`YYYYMMDD`), read from the venue's contract definition — never inferred. */
    readonly expiry: string;
    /** The venue's settlement timestamp, verbatim from the receipt. */
    readonly settledAt: number;
    /** The `now` the DRIVER had pinned when the settlement was recorded (RUNTIME §0 — never a wall clock). */
    readonly observedAt: number;
}
/** The IBKR PAPER {@link BrokerAdapter} (kestrel-7o2.8) — a Gate, plus the ledger/reconciliation
 * surface the safety envelope reads. */
export interface IbkrBroker extends BrokerAdapter {
    now: number;
    submit(intent: OrderIntent): string;
    cancel(ref: string): void;
    readonly events: readonly NewBusEvent[];
    /** The BROKER's AUTHORITATIVE signed net position per {@link PositionKey} (ADR-0034 §4). */
    positions(): PositionSnapshot;
    /** Guard an intent through all four walls and return its {@link OrderTicket} — TRANSMITTING NOTHING. */
    preflight(intent: OrderIntent): OrderTicket;
    /** The per-order reconciliation ledger (engine expectation beside broker truth). */
    ledger(): readonly IbkrOrderRecord[];
    /** Compare the engine/Blotter EXPECTED positions + the ledger against the BROKER's own truth; any
     * break TRIPS the kill-switch, fail-closed. The ONE exception is a legitimately EXPIRED-AND-SETTLED
     * position, which is recorded on {@link IbkrBroker.settlements} instead (kestrel-7o2.24). */
    reconcile(): void;
    /** The SETTLEMENT LEDGER: every disappearance {@link IbkrBroker.reconcile} accounted for as a
     * cash settlement rather than a break, with the receipt and expiry that justified each one. */
    settlements(): readonly SettlementRecord[];
    /** The kill-switch every transmit consults. Trip it to halt all transmission. On the `makeGate("paper")`
     * path this is the SAME instance the seam's {@link import("../../broker.ts").LiveClamp} consults —
     * `makeGate` threads it in, so a trip in EITHER layer (an operator STAND_DOWN on the clamp, or this
     * face's own at-observation never-naked trip) halts the whole path. There is exactly ONE. */
    readonly killSwitch: KillSwitch;
    /** This face's configured L0 ceilings (kestrel-7o2.9). EXPOSED so `makeGate("paper")` can thread them
     * into the seam's {@link import("../../broker.ts").makeLiveClamp} — the SAME limits then back BOTH the
     * seam's L0 clamp and this face's WALL 5 (ADR-0034 §4/§7: L0 is above the adapter): TWO INDEPENDENTLY-
     * PROVEN ceilings over ONE set of numbers, so they can never diverge. NEITHER is subordinate — each is
     * mutation-proven load-bearing on its own. */
    readonly limits: RiskLimits;
    /** The venue's TRUE per-intent contract multiplier — an option `100`, an equity `1` (kestrel-7o2.8).
     * EXPOSED so `makeGate("paper")` threads it into the seam's {@link import("../../broker.ts").makeLiveClamp}
     * L0 clamp, whose notional ceiling is then computed on REAL per-contract notional rather than a flat
     * `1×` that is 100× too loose for a 100× option. Resolves the contract from this face's ContractBook;
     * an UNRESOLVED contract returns the CONSERVATIVE `100` (over-estimate the notional so the seam ceiling
     * fails closed — the adapter's own WALL 2 then refuses the unresolved leg outright). Never transmits. */
    multiplierOf(intent: OrderIntent): number;
    /** Detach every IB listener this face registered (idempotent; safe from a STAND_DOWN path). */
    detach(): void;
}
/** The exact IB {@link Contract} to transmit against — built from the GATEWAY's own definition
 * (7o2.6), not hand-rolled. `conId` is the authoritative handle; the option carries the VENUE's
 * MULTIPLIER (never an assumed 100x) and its own OCC local symbol. NO price rides on it, ever. */
export declare function ibContractOf(c: IbkrContract): Contract;
/**
 * Build the IBKR **PAPER** order face (kestrel-7o2.8). REFUSES `mode: "live"` at construction — live
 * routing is 7o2.10 and requires the human-signed {@link LiveArm}; a config flag can never grant it.
 */
export declare function ibkrBroker(cfg: IbkrConfig, deps: IbkrBrokerDeps): IbkrBroker;
/** The name the IBKR paper face registers itself under in the mode-keyed `makeGate` factory. */
export declare const IBKR_PAPER_VENUE = "ibkr";
/**
 * Register a constructed IBKR paper face as the venue `makeGate("paper", { venue: IBKR_PAPER_VENUE })`
 * serves (kestrel-7o2.8). This is the composition-root wiring, and it lives HERE rather than in
 * `adapters/broker.ts` on purpose: the core seam must never import a socket transport, so the `sim`
 * gate keeps its zero-dependency, byte-identical path. Registration is PAPER-only by construction —
 * `makeGate("live", …)` still refuses with `LiveGateRefused` no matter what is registered.
 */
export declare function installIbkrPaperVenue(broker: IbkrBroker): IbkrBroker;
export {};
//# sourceMappingURL=broker.d.ts.map