/**
 * # adapters/broker/ibkr/contract — the CONTRACT-RESOLUTION layer (kestrel-7o2.6)
 *
 * The one place a **Kestrel identity** (an already-resolved {@link OrderIntent}: an instrument, and
 * — for an option — a strike + a right, ADR-0017) is mapped onto the **broker's own definition** of
 * that instrument. It answers exactly one question: *which contract at the venue IS this leg?*
 *
 * ## Two entry points, and the type is what keeps them apart (kestrel-7o2.24)
 * {@link resolveContract} is the ORDER path: it returns an {@link IbkrContract}, the TRADABLE union
 * (`STK` | `OPT`). {@link resolveUnderlier} is the READ path: it returns an
 * {@link IbkrUnderlierContract} (`STK` | `IND`) — what a chain hangs off and what a feed's spot line
 * reads. An INDEX (`IND` — the NDX/XND/SPX class of cash-settled index options) is a number, not an
 * instrument: it lives only in the second union, so the order path cannot be handed one and `tsc`
 * says so. A structural exclusion, not a runtime guard that could go un-wired.
 *
 * ## TRANSMITTER, never a re-pricer (the core doctrine)
 * The engine has already resolved the price before the intent ever reaches a venue face (RUNTIME §4:
 * a silent mid is forbidden; the intent carries its price-resolution receipt). So this layer NEVER
 * reads, derives, rounds, or improves a price — it does not touch `intent.px`, and the
 * {@link IbkrContract} it returns carries **no price field of any kind** (no px / limit / bid / ask /
 * minTick). That is a TYPE-level guarantee, asserted structurally in `tests/ibkr.contract.test.ts`:
 * a contract is an IDENTITY, and a price that rode in on an identity is a price nobody authored.
 *
 * ## FAIL-CLOSED (never a guessed contract)
 * A half-specified leg (strike xor right), an option with no supplied expiry, no matching definition,
 * an incomplete definition, or — the trap this layer exists to catch — MORE THAN ONE matching
 * definition, all raise a LOUD typed {@link IbkrContractError} with a logged reason, which the caller
 * routes to STAND_DOWN. **Never a pick.** Two definitions that both "look right" (an a.m.- vs a
 * p.m.-settled class, two trading classes, two primary exchanges) are two different instruments; a
 * layer that silently chooses one has silently authored a trade the author never wrote. The gateway's
 * definition is the authority — an OCC local symbol, an expiry, an exchange are taken FROM it, never
 * hand-rolled toward it.
 *
 * ## Places NO orders
 * This module issues READ-ONLY definition lookups (`reqContractDetails`, `reqSecDefOptParams`). It
 * contains no order-submission path whatsoever, and the narrow {@link IbContractClient} surface it
 * speaks through declares none — so it cannot submit one even by reaching through the client. Order
 * submission is a later bead (7o2.8 paper / 7o2.10 live), behind the human-signed arm envelope
 * (7o2.9). The tests assert this at the source level and with a runtime spy.
 *
 * ## One socket, determinism at the edge
 * The IB client is INJECTED (as the transport injects it), so unit tests drive an in-memory double
 * with no real socket and no real timer; production passes {@link IbkrTransport}'s ONE shared client
 * via {@link contractClientOf} — never a second connection. The request-id minter and the deadline
 * scheduler are injected for the same reason.
 *
 * ## KNOWN INTERACTION with the transport's fatal-error rule (observed against the LIVE gateway)
 * The transport (7o2.5) degrades the shared session on ANY IB error `isNonFatalError` does not
 * whitelist — including **200 ("no security definition")**, which is REQUEST-scoped and entirely
 * expected on this layer's fail-closed path (a bogus strike is *supposed* to be refused). So an
 * `unresolvable` refusal ALSO degrades the transport, and the next lookup throws
 * {@link IbkrConnectionError} until the session reconnects. The direction is safe — it fails MORE
 * closed, never less, and STAND_DOWN is exactly where both paths route — but a request-scoped error
 * should not fell the socket. Tuning that classification is the transport's call (the IBKR hardening
 * bead), NOT this layer's: nothing here may quietly re-open or re-classify a session the transport
 * has declared dead.
 */
import { EventName } from "@stoqey/ib";
import type { Contract } from "@stoqey/ib";
import type { IbkrTransport, Scheduler } from "./transport.ts";
import type { OrderIntent } from "../../../engine/index.ts";
import type { Right } from "../../../bus/index.ts";
/** IB's "No security definition has been found for the request" (error 200) — the venue's own way of
 * saying the identity does not exist. `@stoqey/ib`'s {@link import("@stoqey/ib").ErrorCode} enum has
 * no member for it, so it is named here rather than left as a bare literal at the branch. */
export declare const IB_NO_SECURITY_DEFINITION = 200;
/** Publication-safe routing defaults (ARCHITECTURE §7: nothing here reveals an application). SMART
 * is IB's own documented default router and USD the listing currency of the generic US tickers the
 * package ships fixtures for; both are always overridable per-call. Never a guessed EXPIRY, though —
 * an expiry has no safe default, so it is required from the caller (see {@link IbkrContractDeps}). */
export declare const IBKR_DEFAULT_EXCHANGE = "SMART";
export declare const IBKR_DEFAULT_CURRENCY = "USD";
/** The fields every resolved contract carries, whatever the asset class. Note what is ABSENT and
 * always will be: any price. The contract layer is a transmitter, never a re-pricer. */
interface IbkrContractBase {
    /** IB's unique contract id — the authoritative handle a later order face transmits against. */
    readonly conId: number;
    readonly symbol: string;
    /** The destination exchange the gateway's definition names (e.g. `SMART`). */
    readonly exchange: string;
    readonly currency: string;
    /** The gateway's OWN local symbol (for an option, the OCC symbol) — never hand-rolled. */
    readonly localSymbol: string;
    /** The gateway's own trading class, when it named one (e.g. `SPY`) — the disambiguator. */
    readonly tradingClass: string | undefined;
}
/** A resolved SPOT/EQUITY contract (ADR-0017: strike AND right both absent on the leg). */
export interface IbkrEquityContract extends IbkrContractBase {
    readonly kind: "equity";
    readonly secType: "STK";
    /** Shares are 1:1 — the multiplier is carried for a uniform sizing surface, not derived. */
    readonly multiplier: 1;
    /** The listing exchange the gateway names for a SMART-routed equity (e.g. `ARCA`), when known. */
    readonly primaryExchange: string | undefined;
}
/**
 * A resolved INDEX contract (`secType: IND`) — the underlier of a **cash-settled index option**
 * (NDX/XND/SPX class), and the thing a chain hangs off.
 *
 * ## It is NOT a member of {@link IbkrContract}, and that is the whole point
 * An index is a NUMBER, not an instrument: it cannot be bought, sold, held, or delivered. So it is
 * deliberately kept OUT of the tradable {@link IbkrContract} union rather than admitted and then
 * guarded. {@link resolveContract} — the ORDER path's entry point — can never return one, and the
 * order face's {@link IbkrContract}-typed surfaces (the contract book, `submit`, `preflight`) cannot
 * be handed one: `tsc` refuses. That is a STRUCTURAL guarantee, not a runtime check that could be
 * forgotten, mis-wired, or deleted. A feed subscribes to an index (that is where the spot the strike
 * window centres on comes from — see {@link IbkrUnderlierContract}); an order path never touches one.
 *
 * Note what is ABSENT: no `multiplier`, no `primaryExchange`. An index has no contract multiplier
 * (there is no contract), so the field that would let it be SIZED does not exist to be read.
 */
export interface IbkrIndexContract extends IbkrContractBase {
    readonly kind: "index";
    readonly secType: "IND";
}
/**
 * What an option chain can hang off, and what a feed's SPOT line can be: an equity/ETF underlier
 * (`STK`) or an index underlier (`IND`). Which one a symbol IS is never inferred from the symbol —
 * `XND` could be anything — it is stated by the caller ({@link resolveUnderlier}'s `secType`).
 */
export type IbkrUnderlierContract = IbkrEquityContract | IbkrIndexContract;
/** Anything the FEED may subscribe to for a quote — an underlier (equity or index) or an option leg.
 * Strictly wider than {@link IbkrContract} (the TRADABLE union): a quote is not a trade. */
export type IbkrQuotedContract = IbkrUnderlierContract | IbkrOptionContract;
/** Which asset class an underlier IS. Never guessed from the symbol: stated by the caller. */
export type UnderlierSecType = "STK" | "IND";
/** A resolved OPTION contract (ADR-0017: strike AND right both present on the leg). Every field is
 * the GATEWAY's, taken from its definition — the expiry, the multiplier, the OCC local symbol. */
export interface IbkrOptionContract extends IbkrContractBase {
    readonly kind: "option";
    readonly secType: "OPT";
    /** Contract multiplier as the venue defines it (dollars per point). Read, never assumed. */
    readonly multiplier: number;
    /** The venue's last-trading-day, `YYYYMMDD`. Never guessed — echoed from the definition. */
    readonly expiry: string;
    readonly strike: number;
    readonly right: Right;
}
/**
 * A **TRADABLE** broker contract resolved from a Kestrel identity (kestrel-7o2.6) — the discriminated
 * union a venue face transmits against. It carries NO price: not `px`, not a limit, not a quote, not
 * a tick size. The price on the {@link OrderIntent} is the engine's, already resolved with a receipt;
 * this layer never reads it and never lets a venue-side number impersonate one.
 *
 * {@link IbkrIndexContract} is NOT a member: an index is not tradable, so the order path's own type
 * excludes it and no guard is needed to keep it out (see that type's docblock). Widen to
 * {@link IbkrQuotedContract} for the read-only feed side, never here.
 */
export type IbkrContract = IbkrEquityContract | IbkrOptionContract;
/** The gateway's option chain for one underlier + trading class — the definition source a caller
 * picks a strike/expiry FROM (it does not pick for them). Carries no price. */
export interface IbkrOptionChain {
    readonly symbol: string;
    readonly exchange: string;
    readonly tradingClass: string;
    readonly multiplier: number;
    /** Sorted ascending — the same gateway reply always yields the same order (determinism at the edge). */
    readonly expirations: readonly string[];
    /** Sorted ascending. */
    readonly strikes: readonly number[];
}
/** 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 structural surface of the shared IB client this layer speaks through: the two READ-ONLY
 * contract-definition requests and the event bus. It declares NO order-submission method at all — the
 * contract layer cannot transmit an order even by reaching through the client (order submission is
 * 7o2.8/7o2.10, behind the 7o2.9 arm envelope). A test injects a double implementing exactly this;
 * production passes the transport's ONE shared client (a structural superset) via
 * {@link contractClientOf} — never a second socket.
 */
export interface IbContractClient {
    /** Ask the gateway for every definition matching a (possibly partial) contract query. */
    reqContractDetails(reqId: number, contract: Contract): unknown;
    /** Ask the gateway for an underlier's option-chain parameters (expirations/strikes/multiplier). */
    reqSecDefOptParams(reqId: number, underlyingSymbol: string, futFopExchange: string, underlyingSecType: string, underlyingConId: number): unknown;
    on(event: EventName, listener: IbListener): unknown;
    removeListener(event: EventName, listener: IbListener): unknown;
}
/** Everything {@link resolveContract} is wired with. Every non-determinism source (the socket, the
 * request-id minter, the deadline timer) is injected, so unit tests run with none of them. */
export interface IbkrContractDeps {
    /** The shared IB client — the transport's ONE session in production (see {@link contractClientOf}). */
    readonly client: IbContractClient;
    /** Mints a unique IB request id per lookup. Defaults to a module-local monotone counter (a counter,
     * never an RNG — this is the edge, and it still must not be a source of nondeterminism). */
    readonly nextReqId?: (() => number) | undefined;
    /** The OPTION expiry to resolve (`YYYYMMDD`). REQUIRED for an option leg and REFUSED-if-absent —
     * an expiry is never inferred from a clock or a chain, because "the nearest one" is a trade the
     * author did not write. Ignored (and unnecessary) for a spot/equity leg. */
    readonly expiry?: string | undefined;
    /** Destination exchange for the query (default {@link IBKR_DEFAULT_EXCHANGE}). */
    readonly exchange?: string | undefined;
    /** Trading currency for the query (default {@link IBKR_DEFAULT_CURRENCY}). */
    readonly currency?: string | undefined;
    /** Trading class to pin — the ONLY sanctioned way to disambiguate two classes on one underlier
     * (e.g. an a.m.- vs a p.m.-settled index class). Absent + two matches ⇒ a typed refusal, never a pick. */
    readonly tradingClass?: string | undefined;
    /** Lookup deadline (ms). A lapse is a typed `lookup-failed`, never a hang. */
    readonly timeoutMs?: number | undefined;
    /** Deadline scheduler — injected so tests fire it by hand (no real timer). */
    readonly scheduler?: Scheduler | undefined;
    /** The session account, used ONLY to stamp a REDACTED marker on diagnostics (as the transport
     * redacts it). The full id never reaches a message. */
    readonly account?: string | undefined;
    /** Redacted-diagnostic sink (default no-op). Receives only already-redacted strings. */
    readonly log?: ((line: string) => void) | undefined;
}
/** Which underlier + class an option chain is being pulled for. */
export interface IbkrChainQuery {
    readonly symbol: string;
    /** The underlier's IB conId — resolve the underlier ({@link resolveUnderlier}) first, then hand its
     * conId here. */
    readonly underlyingConId: number;
    /** What the underlier IS. `reqSecDefOptParams` takes the underlier's own secType, and an INDEX
     * underlier asked about as `STK` returns NO chain at all — the NDX/XND blocker. Defaults to `STK`,
     * which is what every equity/ETF caller has always meant; an index caller must say `IND`. Not a
     * guess in either direction: the caller states it (same rule as {@link resolveUnderlier}). */
    readonly underlyingSecType?: UnderlierSecType | undefined;
    /** Pin a trading class; absent + several classes on the exchange ⇒ a typed `ambiguous` refusal. */
    readonly tradingClass?: string | undefined;
}
/**
 * Map a resolved {@link OrderIntent} onto exactly ONE broker contract (kestrel-7o2.6).
 *
 * Routing is ADR-0017's rule and nothing else: **strike AND right both present ⇒ an OPTION; both
 * absent ⇒ a SPOT/EQUITY**. Anything in between is not an instrument identity and is refused before
 * the gateway is even asked (a right is never guessed from a side; a strike is never guessed from a
 * price — indeed no price is ever read).
 *
 * The resolved contract is the GATEWAY's own definition: its conId, its OCC local symbol, its
 * multiplier, its exchange, its currency, its expiry. Zero, several, incomplete, or unanswered ⇒ a
 * LOUD {@link IbkrContractError} the caller logs and STANDs DOWN on.
 *
 * @throws {IbkrContractError} `unsupported` | `unresolvable` | `ambiguous` | `lookup-failed`.
 */
export declare function resolveContract(intent: OrderIntent, deps: IbkrContractDeps): Promise<IbkrContract>;
/**
 * Resolve the UNDERLIER a chain hangs off / a feed's spot line reads (kestrel-7o2.24) — an equity/ETF
 * (`STK`) or an **index** (`IND`, the NDX/XND/SPX class of cash-settled index options).
 *
 * `secType` is REQUIRED and never inferred. A symbol does not say what it is: `XND` is an index at
 * Nasdaq and could be an ETF's ticker anywhere else, and an adapter that sniffed the class would be
 * guessing an identity — precisely what this layer exists not to do. The caller states it; the
 * gateway's definition confirms it, or the lookup refuses.
 *
 * The result is an {@link IbkrUnderlierContract}, which is NOT an {@link IbkrContract}: an index is
 * not tradable and the type says so, so nothing resolved here can be handed to the order path.
 *
 * ## EXCHANGE: pinned, or left OPEN — never defaulted to SMART for an index
 * An equity is SMART-routed and defaults to {@link IBKR_DEFAULT_EXCHANGE} as it always has. An index
 * is NOT: `SMART` is a router, and indices are published by their listing venue (NDX on `NASDAQ`, SPX
 * on `CBOE`). So for `IND` the query's exchange is the caller's pin if there is one, and otherwise
 * LEFT OPEN — asking the venue "which listings of this index do you have?" rather than asserting a
 * wrong one. Open + exactly one listing ⇒ resolved. Open + several ⇒ a LOUD `ambiguous` refusal that
 * NAMES the exchanges, so the operator learns exactly what to pin. Both are fail-closed; the open
 * query is merely the one that fails INFORMATIVELY instead of as a flat `unresolvable`.
 *
 * @throws {IbkrContractError} `unresolvable` | `ambiguous` | `lookup-failed`.
 */
export declare function resolveUnderlier(query: {
    readonly symbol: string;
    readonly secType: UnderlierSecType;
}, deps: IbkrContractDeps): Promise<IbkrUnderlierContract>;
/**
 * Pull the gateway's option-chain parameters for an underlier (kestrel-7o2.6) — the expirations, the
 * strike grid, and the multiplier IB itself publishes for one exchange + trading class. This is a
 * DEFINITION lookup (`reqSecDefOptParams`), not market data: it carries no quote and no price.
 *
 * A caller uses it to pick a strike/expiry from what the venue actually lists (rather than inventing
 * one) and then hands that identity back to {@link resolveContract}. Fail-closed: an empty chain is
 * `unresolvable`; several trading classes on the requested exchange with none pinned is `ambiguous`
 * (never a pick — two classes are two instruments).
 *
 * @throws {IbkrContractError} `unresolvable` | `ambiguous` | `lookup-failed`.
 */
export declare function resolveOptionChain(query: IbkrChainQuery, deps: IbkrContractDeps): Promise<IbkrOptionChain>;
/**
 * The strikes the gateway ACTUALLY LISTS for one expiry + right (kestrel-7o2.6) — a definition
 * lookup (`reqContractDetails` with the strike left OPEN), not market data: no quote, no price.
 *
 * Why it exists beside {@link resolveOptionChain}: `reqSecDefOptParams` returns the UNION of strikes
 * across every expiry of an underlier, so a strike from that grid may not be listed on the expiry a
 * caller actually wants (the near-dated series is far narrower than the union — verified against the
 * live gateway). Picking from the union would therefore invent an identity the venue does not list.
 * This asks the venue what it lists for THAT expiry, so a caller picks from the truth.
 *
 * @throws {IbkrContractError} `unresolvable` (the venue lists no strike for it) | `lookup-failed`.
 */
export declare function listOptionStrikes(query: {
    symbol: string;
    expiry: string;
    right: Right;
    tradingClass?: string | undefined;
}, deps: IbkrContractDeps): Promise<readonly number[]>;
/**
 * The ONE shared IB session, viewed through the READ-ONLY definition surface (kestrel-7o2.6). The
 * transport hands out its single guarded client (throwing the typed connection error if the session
 * is not connected or has gone degraded, so a contract can never be resolved over a dead socket);
 * this re-views it as an {@link IbContractClient}. The cast NARROWS toward the two definition
 * requests — the real `IBApi` is a structural superset of both views, and neither view can submit an
 * order. There is never a second socket.
 */
export declare function contractClientOf(transport: IbkrTransport): IbContractClient;
export {};
//# sourceMappingURL=contract.d.ts.map