import { TraderBase, TraderEvent } from "./generated/trader.gen.js";
import type { CallbackOptions } from "./client.js";
import type { Order, Trade, InputOrder, RspUserLogin } from "./generated/structs.gen.js";
import type { MarketData } from "./market-data.js";
/** Pre-trade risk limits, enforced in C++ before each order is sent. */
export interface RiskConfig {
    /** Max volume per single order. Omit/0 = disabled. */
    maxOrderVolume?: number;
    /** Reject if order price deviates from the reference by > this ratio (e.g.
     *  0.02 = 2%). Feed the reference via trackMarketData() or setRefPrice();
     *  with no reference the check is skipped. */
    maxPriceDeviation?: number;
    /** Max notional (price × volume) per order. Omit/0 = disabled. */
    maxNotional?: number;
    /** Max order sends per second (token bucket). Omit/0 = disabled. */
    maxOrdersPerSec?: number;
    /** Token-bucket burst; defaults to maxOrdersPerSec. */
    orderBurst?: number;
    /** Cap on total open-position cost (Σ open price × volume × multiplier).
     *  0/undefined = disabled. Multipliers and existing positions are picked up
     *  automatically via syncMultipliers() / syncPositions(). */
    maxPositionCost?: number;
}
/** A per-instrument lot cap: a single number caps both sides at that value;
 *  `{ long, short }` caps each side separately (omit a side to leave it open). */
export type LotCap = number | {
    long?: number;
    short?: number;
};
/** Options for the one-call post-connect handshake, {@link Trader.session}. */
export interface SessionOptions {
    brokerId: string;
    userId: string;
    password: string;
    /** Terminal authentication (real accounts require it; SimNow uses
     *  appId "simnow_client_test" / authCode of 16 zeros). Omit both to skip. */
    appId?: string;
    authCode?: string;
    /** Confirm the settlement statement after login — real accounts must do this
     *  before trading (brokers reject orders otherwise). Default true; set false
     *  for environments that don't need it. */
    confirmSettlement?: boolean;
    /** Which risk inputs to fetch after login (default: all). `multipliers` may
     *  be `true` (all instruments) or a list of symbols. */
    sync?: {
        multipliers?: boolean | string[];
        positions?: boolean;
        orders?: boolean;
    };
}
/** A latency-critical armed order: evaluated and fired in C++ on the market-data
 *  callback thread the instant its trigger hits — JS is never in the hot path. */
export interface ArmSpec {
    instrumentId: string;
    /** Buy fires when ask ≤ triggerPrice; sell when bid ≥ triggerPrice. */
    side: "buy" | "sell";
    triggerPrice: number;
    /** The order to send when triggered (a full InputOrder; set an OrderRef to
     *  correlate the resulting rtn-order). */
    order: Partial<InputOrder>;
}
export interface ArmHandle {
    readonly id: number;
    /** Remove the trigger. Returns false if it was already gone. */
    disarm(): boolean;
}
export declare class Trader extends TraderBase {
    private broker?;
    private investor?;
    private orderRefSeq;
    constructor(flowPath: string, fronts: string | string[]);
    getApiVersion(): string;
    getTradingDay(): string;
    /**
     * One-call post-connect handshake. Call it from the `front-connected` handler
     * (and again after a reconnect): authenticate (if appId/authCode given) →
     * login → confirm settlement → sync multipliers / positions / orders. Each
     * step just wraps the matching req / sync method, so you can hand-roll your
     * own flow instead when you need finer control. Returns the row counts from
     * the sync steps. Must run on a connected front (queries need the session up).
     */
    session(opts: SessionOptions): Promise<{
        multipliers: number;
        positions: number;
        orders: number;
    }>;
    /** Assign a unique numeric OrderRef when the caller left it blank. CTP treats
     *  OrderRef as a per-session sequence and rejects a duplicate or non-numeric
     *  ref with "不允许重复报单", so an unset ref would otherwise collide. */
    private withAutoOrderRef;
    /**
     * Insert an order through the C++ pre-trade risk gate. Resolves on submission:
     * CTP sends no success acknowledgement for an accepted order - only
     * OnRtnOrder / OnRtnTrade (surfaced as the rtn-order / rtn-trade events;
     * correlate by orderRef). It rejects only if the send itself is refused (risk
     * gate, rate limit, or a CTP API error code). A blank orderRef is assigned a
     * unique numeric value automatically.
     */
    reqOrderInsert(req?: Partial<InputOrder>): Promise<void>;
    /** Publish pre-trade risk limits to the C++ enforcer (takes effect at once). */
    riskSet(config: RiskConfig): this;
    /** Kill-switch: C++ immediately blocks every order-opening send that reaches
     *  the exchange at once — regular, exec, quote, for-quote, option-self-close
     *  and comb-action inserts. Cancels and other *actions* are deliberately left
     *  open so you can still pull working orders while halted; parked/staged orders
     *  (not sent to the exchange immediately) are likewise not gated. Takes effect
     *  at once; reverse with {@link resume}. */
    halt(): this;
    /** Release the kill-switch (see {@link halt}). */
    resume(): this;
    /** Set the reference price for an instrument (used by maxPriceDeviation). */
    setRefPrice(instrumentId: string, price: number): this;
    /** Auto-feed last prices from a MarketData feed so the maxPriceDeviation
     *  check has a live reference. */
    trackMarketData(md: MarketData): this;
    /** Set a contract's multiplier (合约乘数) for position-cost accounting. */
    setMultiplier(instrumentId: string, multiplier: number): this;
    /**
     * Cap the open position (in lots) for one instrument, enforced in C++ on
     * every opening order. Pass a number to cap both sides at once, or
     * `{ long, short }` to cap each side separately. Within `{ long, short }`,
     * **omitting a side leaves that side's current cap unchanged** (on a re-call it
     * is NOT reset to uncapped), and passing `<= 0` for a side removes its cap.
     * Long and short are always tracked independently.
     *
     * The check is fill-based (counts confirmed position, like maxPositionCost),
     * so rapid bursts of opens can momentarily overshoot before the fills land —
     * size the cap with that in mind.
     *
     *   td.setMaxPosition("rb2610", 100);                 // long<=100 and short<=100
     *   td.setMaxPosition("ru2610", { long: 100, short: 20 });
     */
    setMaxPosition(instrumentId: string, max: LotCap): this;
    /** Cap several instruments at once, e.g.
     *  `{ rb2610: 100, ru2610: { long: 100, short: 20 } }`. */
    setMaxPositions(limits: Record<string, LotCap>): this;
    /**
     * Cap one instrument's open-position cost (Σ open price × volume × multiplier,
     * long and short summed - a gross capital/concentration limit), enforced in
     * C++ on every opening order. This is per-instrument and independent of the
     * account-wide cap set via riskSet({ maxPositionCost }); both apply. Pass
     * `maxCost <= 0` to remove it. Fill-based, same caveat as maxPositionCost.
     */
    setMaxPositionCost(instrumentId: string, maxCost: number): this;
    /** Cap several instruments' open-position cost at once, e.g.
     *  `{ ag2608: 2_000_000, au2608: 5_000_000 }`. */
    setMaxPositionCosts(limits: Record<string, number>): this;
    /** Seed a pre-existing position's open cost (for the maxPositionCost cap). */
    seedPosition(instrumentId: string, side: "long" | "short", volume: number, openCost: number): this;
    /** Seed open-position cost from reqQryInvestorPosition() rows for GROSS-mode
     *  accounts (posiDirection '2' = long, '3' = short; any other value, incl. net
     *  '1', is bucketed as short). Cost caps sum both sides so the bucket doesn't
     *  matter for them, but a per-side *volume* cap on a net-mode instrument would
     *  track the wrong side — for net-mode accounts, seed those via seedPosition()
     *  with the side you intend. */
    seedFromPositions(positions: ReadonlyArray<{
        instrumentId: string;
        posiDirection: string;
        position: number;
        openCost: number;
    }>): this;
    /** Current total open-position cost tracked by the C++ risk engine. */
    positionCost(): number;
    /** Clear all tracked position cost. */
    resetPositions(): this;
    /**
     * Run a CTP query with flow-control retries. CTP rate-limits queries (~1/sec)
     * and one issued too soon after login can be rejected or come back empty
     * before the account is ready. With `requireNonEmpty`, an empty result is
     * treated as not-ready-yet and retried (for data that must exist, e.g. an
     * instrument); otherwise empty is returned as-is (e.g. a genuinely flat
     * position book, so a flat account doesn't waste the full retry budget).
     */
    private queryWithRetry;
    /** Fetch contract multipliers from CTP and apply them. No args queries all
     *  instruments (one request); otherwise queries the given symbols. Retries
     *  through cold-start flow control so the multiplier is reliably applied. */
    syncMultipliers(instrumentIds?: string[]): Promise<number>;
    /** Seed open-position cost from CTP (reqQryInvestorPosition). Uses the
     *  logged-in account unless brokerId/investorId are supplied. */
    syncPositions(opts?: {
        brokerId?: string;
        investorId?: string;
    }): Promise<number>;
    /**
     * Rebuild the in-flight reservation from CTP's currently-working orders
     * (reqQryOrder) - an authoritative resync. Call it after login and after any
     * reconnect so the position caps account for orders already working at the
     * broker: ones placed before a reconnect, or - since CTP delivers them too -
     * orders placed from another terminal on the same account. Returns the number
     * of working open orders re-reserved. Run syncMultipliers() first so the
     * reserved cost uses the right contract multiplier.
     */
    syncOrders(opts?: {
        brokerId?: string;
        investorId?: string;
    }): Promise<number>;
    /** @internal test-only: feed a synthetic fill into the position-cost tracker. */
    _applyTestTrade(instrumentId: string, isBuy: boolean, isOpen: boolean, price: number, volume: number): void;
    /** @internal test-only: drive the in-flight reservation tracker (OnRtnOrder).
     *  status: '1'/'3' = working (queueing), others (e.g. '0' filled, '5'
     *  cancelled) = terminal. */
    _applyTestOrder(frontId: number, sessionId: number, orderRef: string, instrumentId: string, isOpen: boolean, isLong: boolean, status: string, limitPrice: number, volTotal: number, volTraded: number): void;
    /**
     * Arm a latency-critical trigger: when `md` sees the condition, the order is
     * sent from C++ on the callback thread (through this Trader's risk gate),
     * with no JS round trip. One-shot. The acknowledgement arrives via the normal
     * rtn-order / rsp-order-insert events (correlate by the order's orderRef).
     */
    arm(md: MarketData, spec: ArmSpec): ArmHandle;
    /** Observability for armed triggers (they fire in C++ with no JS in the loop):
     *  how many fired and were sent vs were refused by the risk gate / send.
     *  A blocked armed order is otherwise invisible, so poll this after a trigger
     *  you expected to fire. */
    armStats(): {
        fired: number;
        blocked: number;
    };
    /** @internal used by MarketData.attachArm to share the arm registry. */
    _armRegistry(): unknown;
    /** @internal test-only: how many times armed triggers have fired (sent). */
    _armFireCount(): number;
    /** @internal test-only: how many armed triggers were blocked on fire. */
    _armBlockedCount(): number;
    on(event: "rtn-order", cb: (data: Order, opts: CallbackOptions) => void): this;
    on(event: "rtn-trade", cb: (data: Trade, opts: CallbackOptions) => void): this;
    on(event: "front-connected", cb: (data: undefined, opts: CallbackOptions) => void): this;
    on(event: "front-disconnected", cb: (reason: number, opts: CallbackOptions) => void): this;
    on(event: "rsp-user-login", cb: (data: RspUserLogin, opts: CallbackOptions) => void): this;
    on(event: "err-rtn-order-insert", cb: (data: InputOrder, opts: CallbackOptions) => void): this;
    on(event: string, cb: (...args: never[]) => void): this;
}
export { TraderEvent };
//# sourceMappingURL=trader.d.ts.map