npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@huskly/ibkr-client

v0.20.0

Published

Interactive Brokers Web API client with OAuth 1.0a authentication

Readme

IBKR Client (@huskly/ibkr-client)

A reusable TypeScript client for the Interactive Brokers Web API, authenticating over OAuth 1.0a without the Client Portal Gateway. This package contains no command-line interface; terminal commands and presentation belong exclusively in huskly/cli.

The OAuth 1.0a live-session-token handshake is performed by the ibkr-client package. See the ibind OAuth 1.0a wiki for how the keys below are generated and registered in the IBKR self-service portal.

Layout

The repository contains only the reusable client library:

| Path | Purpose | | -------------------------------- | --------------------------------------------------------------------------- | | src/types.ts | Broker-neutral BrokerClient interface and normalized domain types | | src/ibkr/ibkrClient.ts | IbkrClient — typed wrapper over ibkr-client implementing BrokerClient | | src/ibkr/oauthConfig.ts | Builds the OAuth config from .pem files + env vars | | src/ibkr/dhPrime.ts | Extracts the DH prime (hex) from dhparam.pem | | src/ibkr/optionContract.ts | Canonical OSI parsing and formatting for IBKR option contracts | | src/ibkr/derivativeContract.ts | Normalizes OPT/FOP identity and market-data availability |

The *.pem files (private_signature.pem, private_encryption.pem, dhparam.pem, plus the public keys) are the cryptographic material from the wiki setup step and are git-ignored.

Setup

yarn install --frozen-lockfile

Provide the account-specific secrets, either by copying the template:

cp .env.example .env
# then edit .env

or by exporting them in your shell:

export IBIND_OAUTH1A_CONSUMER_KEY=...        # 9-char consumer key from IBKR
export IBIND_OAUTH1A_ACCESS_TOKEN=...        # access token from the portal
export IBIND_OAUTH1A_ACCESS_TOKEN_SECRET=... # access token secret from the portal

Optional environment variables:

  • IBIND_OAUTH1A_REALM — OAuth realm (defaults to limited_poa for the individual self-service flow).
  • IBKR_KEYS_DIR — directory holding the .pem files (defaults to the current working directory).
  • IBKR_ACCOUNT_ID — target a specific account (otherwise the first is used).
  • IBKR_TRANSACTION_CURRENCY — transaction-query currency (defaults to USD).

Library API

IbkrClient owns IBKR authentication, requests, raw response types, and normalization. Consumers such as huskly-cli provide presentation, command routing, and caching. Public request types are the caller contract: this package assumes consumers use TypeScript and pass values accepted by those types. Provider responses remain untrusted and are validated at runtime. Its broker-neutral account API includes:

  • getAccountBalances() and getPositions() for account state. Account balances include typed margin.total, margin.securities, and margin.commodities snapshots with IBKR's available funds, buying power, excess liquidity, cushion, SMA, equity-with-loan, Reg-T, initial- and maintenance-margin, full, look-ahead, and leverage values. Margin values are null when IBKR omits or returns an invalid value; numeric zero remains 0.
  • getQuotes() and searchInstruments() for equity/ETF discovery and quotes.
  • fetchTransactionHistory() for normalized portfolio transactions.
  • fetchOrders() for normalized live orders, including aggregate WORKING matching across IBKR's active order states.

Strategy market data

The reusable IbkrClient also exposes typed, read-only strategy data:

  • getPriceHistory(...) returns normalized OHLCV bars.
  • getOptionExpiries(...) discovers weekly and monthly maturities across month buckets.
  • getOptionChain(...) returns an exact-expiry chain with canonical OSI symbols, conids, bid/ask/mid prices, delta, session volume, and open interest.
  • getOptionQuote(...) resolves and prices one exact contract with the same market-data shape.
  • getOptionContract(conid) maps a broker conid back to durable OSI identity.

Broker-neutral derivative discovery

IbkrClient implements the capability-specific DerivativeDiscoveryClient without adding derivative operations to the smaller account-oriented BrokerClient:

  • getDerivativeExpiries(...) lists exact series identity over a calendar range.
  • getDerivativeContracts(...) discovers all matching contracts for one expiration.
  • resolveDerivativeContract(...) returns exactly one contract and rejects ambiguous trading classes.
  • getDerivativeChain(...) prices one exact expiration and fails when no usable bid/ask exists.
  • getDerivativeReferenceQuote(...) follows IBKR's undConid to quote the actual linked underlying contract, such as the September NQ future behind an August QN3 option.

Both OPT and FOP use the stateful secdef/search -> secdef/strikes -> secdef/info sequence. FOP discovery derives a unique exchange such as CME from the search result when the caller does not provide one. Index-option callers can select a venue explicitly, such as SMART.

const nq = await client.resolveDerivativeContract({
  assetClass: "FOP",
  underlying: "NQ",
  expiration: "2026-08-21",
  strike: 26600,
  right: "P",
  tradingClass: "QN3",
});
// nq.multiplier === 20; nq.exchange === "CME"

const ndxp = await client.resolveDerivativeContract({
  assetClass: "OPT",
  underlying: "NDX",
  expiration: "2026-08-20",
  strike: 26600,
  right: "P",
  tradingClass: "NDXP",
  exchange: "SMART",
});

Semantic identity consists of asset class, underlying, expiration, strike, right, trading class, exchange, and multiplier. conid is returned only because this package is the IBKR boundary; it is broker-local, can change, and must not be persisted as durable strategy identity. Optional settlement and exercise-style fields are preserved when IBKR supplies them.

Derivative quotes use nullable values for missing prices and Greeks and normalize field 6509 to live, delayed, frozen, frozen-delayed, or unavailable. A missing subscription is never reported as live data. All discovery APIs are read-only and do not call preview, order, warning-reply, or cancellation endpoints.

Contract discovery always calls secdef/search before secdef/strikes, because IBKR keeps that priming state in the authenticated session. Empty post-prime strikes and incomplete bid/ask/delta snapshots throw instead of looking like a valid chain with no candidates. Request shaping is resilient by design: option discovery normalizes the requested symbol, applies bounded batching for secondary-definition and market-data calls, and retries read-only requests on transient 429 responses with capped exponential backoff (including Retry-After headers when available). If every returned contract is unusable (missing bid/ask/delta), the client now fails noisily so callers can handle that condition explicitly. Option volume and open interest are required nullable fields: numeric zero remains zero, while missing, unsupported, or non-finite provider values are returned as null. Conids are broker-boundary identifiers; consumers should persist the returned OSI symbol.

Explicit derivative What-If previews

IbkrClient also implements DerivativePreviewClient, a deliberately narrow capability with no placement, reply-confirmation, modification, or cancellation method. Callers must provide an exact account ID; the client never selects the first account for preview work.

  • getTradingDiagnostics(accountId) reports authentication, selected account, paper/live state, market-data access, and advisory asset permissions. It does not switch accounts or preview an order.
  • previewDerivativeCombo(...) accepts two exact contracts with signed ratios and a positive user-facing credit/debit. It requests the required market-data snapshot, constructs one atomic conidex, and calls only /orders/whatif.

For a BUY-oriented combo, IBKR encodes a net credit as a negative limit price. That provider detail remains inside this package: callers send { priceEffect: "CREDIT", limit: 39 }. The normalized result includes paper/live environment, commission, initial and maintenance margin, warnings, rejection reasons, and submitted: false. An incomplete nominal success fails closed. Permission metadata is diagnostic only; the What-If response remains authoritative.

Guarded derivative order execution

IbkrClient implements a separate DerivativeExecutionClient capability for callers that have already enforced their own reviewed-preview workflow. It does not persist previews or decide whether live execution is allowed.

Submission rejection is authoritative only when IBKR returns its documented top-level error object with a meaningful message, code, or failure status. Array-contained or non-diagnostic error fields return recovery_required, because they do not prove that every submitted ticket failed.

  • submitDerivativeSingleOrder(...) places one single-leg LIMIT or STOP option order with exact contract, side, quantity, TIF, and session. Standalone orders require a unique client order ID; sequentially attached child orders instead require the parent's exact ID and omit their own cOID, as required by IBKR. LIMIT and STOP requests are discriminated: LIMIT orders require only a positive limit, while STOP orders require only a positive stopPrice. Equity-option (OPT) orders omit CME-only fields; futures-option (FOP) orders require the caller's exact extOperator and manualIndicator. Mixed, multiple, unknown, or malformed response evidence returns recovery_required; pending-cancel acknowledgements also require recovery. Callers must reconcile the retained broker order IDs before another write.
  • submitDerivativeCombo(...) places one atomic combo with the exact legs, signed ratios, quantity, price effect, limit, TIF, and session supplied by the caller. The request requires a unique client order ID. Futures-option (FOP) writes also require the caller's exact CME extOperator and manual/automated-origin manualIndicator; equity-option (OPT) writes omit both CME-only fields. As with single orders, ambiguous response evidence returns recovery_required and must block blind resubmission.
  • submitDerivativeContingentOrders(...) places a general parent/child LIMIT-or-STOP pair in one bracket request. The parent carries the caller's unique client order ID; the client derives the child's parentId from it and deliberately omits a child cOID. Parent and child must target the same account, but may reference the same or different contracts. IBKR does not promise an all-or-none response: accepted therefore requires exactly two non-failure acknowledgements, while mixed, incomplete, pending-cancel, canceled, rejected, unknown, or malformed evidence is returned as recovery_required with every observed broker order ID retained.
  • acknowledgeOrderWarning(...) replies once to an exact broker warning ID. Warnings with a non-empty array of string message IDs are marked known; callers must still approve exact IDs, and malformed or missing IDs remain unknown.
  • A warning from submitDerivativeContingentOrders(...) includes a typed continuation containing the exact reply ID and parent client ID. Pass that object unchanged to acknowledgeContingentOrderWarning(...); its result retains both broker acknowledgements or returns all partial evidence as recovery_required. Do not route contingent warnings through the single-order acknowledgement method.
  • getDerivativeOrderStatus(...) uses IBKR's exact order-ID status endpoint so fast terminal orders remain visible after live-list eviction. It normalizes pending, working, partial-fill, fill, canceled, and rejected lifecycle states with leg ratios and order economics, and fails closed on identity mismatch, unknown status, or missing aggregate quantities.
  • findDerivativeOrder(...) accepts exactly one broker order ID or caller-supplied customer order ID (cOID/order_ref). Broker IDs go directly to the exact endpoint; a customer ID is resolved through the live list and then read by exact broker ID.
  • listActiveDerivativeOrders(accountId) is the pre-placement risk view for one exact account. It preserves every signed USD conidex member, including exchange-qualified spreads, and applies the outer order side to each ratio (or preserves a single conid/side). Caller and broker IDs, including echoed client parentId ownership, remain distinct alongside graph role, quantities, lifecycle, pricing, TIF, session, timestamps, and unambiguous single-leg OSI option identity. Combo identities are not inferred from description order without a conid correlation. Malformed legs, aggregate-only rows, unknown statuses or directions, missing or ambiguous parents, and duplicate graph members are returned with explicit uncertainty rather than silently discarded. An account mismatch or an incomplete IBKR snapshot rejects the entire read, preventing a partial collection from being used as the pre-placement risk view. This active collection is not terminal history: after an order leaves it, use getDerivativeOrderStatus(...) with its broker ID as the authoritative exact lookup.
  • getDerivativeExecutions(...) reads up to seven calendar days from IBKR's trade history and returns individual leg fills with execution ID, conid, side, quantity, price, commission, commission currency, net amount, venue, and execution time. Customer order IDs allow fills to be reconciled back to the atomic combo. Missing provider values remain null rather than estimated.
  • reconcileDerivativeComboExecution(...) polls delayed trade publication to a bounded deadline, correlates by the unique client order reference, rejects duplicate or mismatched evidence, and validates each expected leg's side and ratio-derived quantity. Its sanitized result separates gross option points, multiplier-adjusted gross dollars, commission, and net dollars without exposing account or execution IDs.
  • cancelDerivativeOrder(...) sends one exact cancellation request and returns only a typed requested acknowledgement. Its required assetClass lets the client apply the same product-aware CME metadata rule without guessing from an order ID. Callers remain responsible for reading until a terminal state and verifying that cancellation reached CANCELED.

Placement, warning replies, and cancellation deliberately use single-attempt HTTP writes so a transport retry cannot duplicate a broker action. A BUY-oriented net credit remains negative at the IBKR boundary, matching the What-If ticket exactly. Every write requires the exact account ID; the library never falls back to the first account. Broker-declared order rejections retain their structured response in BrokerErrorDetail.details; transport exceptions are rethrown intact.

Request pacing and temporary blocks

Every authenticated request runs through one priority scheduler per IbkrClient. The default limits allow at most ten requests globally and one exploratory security-definition request at a time. Order preview, status, warning, cancellation, and immediate-trade requests take priority over queued discovery. Multi-month derivative discovery also primes each month serially, while an exact expiry/strike/right request expands only that requested contract.

A 429 pauses the shared queue behind one Retry-After-aware exponential backoff with jitter; individual queued reads do not start independent retry loops. Exhausted throttling throws IbkrRequestSchedulerError with code IBKR_THROTTLED. A broker temporary-block response opens a bounded circuit, rejects queued work, and throws code IBKR_TEMPORARILY_BLOCKED without retrying. IbkrClient accepts optional scheduler limits and an onRequestTelemetry callback. Telemetry contains only a sanitized endpoint category, event, attempt, and delay—never account IDs, order IDs, credentials, or request payloads.

Authorized read-only smoke test

Run this only after the account owner authorizes a read-only brokerage request and the OAuth environment from Setup is present. Supply an explicit calendar window; the client does not source strategy time from IBKR. This calls account, security-definition, history, and market-data endpoints only—never preview, placement, reply-confirmation, or cancellation.

IBKR_SMOKE_SYMBOL=MSTR \
IBKR_SMOKE_FROM=2026-08-01 \
IBKR_SMOKE_TO=2026-08-31 \
node --input-type=module <<'NODE'
import { IbkrClient, buildOauthConfig } from "./dist/index.js";

const client = new IbkrClient(buildOauthConfig());
await client.init();
const symbol = process.env.IBKR_SMOKE_SYMBOL;
const from = process.env.IBKR_SMOKE_FROM;
const to = process.env.IBKR_SMOKE_TO;
if (!symbol || !from || !to) throw new Error("Smoke symbol/from/to are required");

const [balances, history, expiries] = await Promise.all([
  client.getAccountBalances(),
  client.getPriceHistory({ symbol, days: 5 }),
  client.getOptionExpiries(symbol, "C", from, to),
]);
const expiry = expiries[0];
if (!expiry) throw new Error(`No listed expiries for ${symbol} in ${from}..${to}`);
const chain = await client.getOptionChain(symbol, expiry);
console.log({ equityRead: Number.isFinite(balances.netLiquidation), historyBars: history.length,
  expiry, contracts: chain.length, first: chain[0]?.symbol });
NODE

Development

yarn lint       # eslint
yarn format     # prettier --write
yarn typecheck  # tsc --noEmit
yarn test       # typecheck + native node:test suite
yarn build      # tsc -> dist/
yarn run check  # lint + format:check + typecheck

CI (.github/workflows/ci.yml) runs lint, format check, typecheck, tests, and build on every push and pull request, plus gitleaks to guard against committed secrets.

Security notes

  • The private keys and .env are git-ignored — do not commit them.
  • Credentials are read from the environment, never hardcoded.
  • CI scans every change with gitleaks to catch accidentally committed secrets.

Resumable derivative order graphs

submitDerivativeOrderGraph(...) is the bounded (one through eight member) bracket API for broker-hosted derivative protection. A graph may combine atomic two-leg LIMIT combos with single-option LIMIT, STOP, and MARKET members. Every node has a caller-stable memberId; exactly one root carries rootClientOrderId. Each later node uses parentMemberId to name a member that comes before it. IBKR receives a deterministic cOID for every node. A non-root node also receives the deterministic parentId of its exact parent. This means that a graph can contain children, grandchildren, and deeper descendants. Accepted and fail-closed results retain each node's immutable request evidence (contracts/conids, signed ratios, side, quantity, TIF, session, and prices), stable depth role, parent member/broker IDs, and every broker order ID. Conids remain correlation evidence only; callers still own durable semantic option identity.

Warnings are never acknowledged automatically. A warning result contains a JSON-safe continuation with the exact reply ID, full graph request, and all correlation accumulated so far; persist it before calling acknowledgeDerivativeOrderGraphWarning(...). Each placement or reply is attempted once after revalidating the account's authentication and competing-session safety; chained and unknown warnings remain pending for the caller, and mixed, partial, duplicated, terminal, malformed, or ambiguous acknowledgements return recovery_required. Broker IDs from ambiguous acknowledgements are retained as uncorrelated responses rather than assigned to nodes by position. A failed synchronous response retains a readable text or warning_message in errors and recovery reasons when IBKR supplies one. recoverDerivativeOrderGraph(...) reconstructs the exact graph from its root client ID, any known member broker ID, and terminal evidence keyed by the durable root client ID. It searches filtered filled, canceled, and inactive order snapshots before using recent execution evidence; an exact broker ID is still queried even if execution history is unavailable. It requires complete active and filtered terminal snapshot markers, traverses both nested child collection aliases, and rejects contradictory aliases within a broker response before correlation. It correlates the root by the transmitted client ID and its complete ticket. It correlates each descendant by the deterministic client order ID of its exact parent, plus the contract or complete combo legs, order type, side, quantity, applicable signed limit or stop price, TIF, and regular/overnight session. Recovery accepts terminal members when the evidence is non-ambiguous and complete, preserves broker terminal states for each member, and fails closed when evidence is partial, duplicated, ambiguous, unknown, includes an unexpected attached order, or cannot prove required account, broker ID, or parent identity links. No recoveries involve writes. Failed terminal snapshot lookups force recovery_required; trade-linked members whose exact status lookup fails remain preserved as uncorrelated evidence instead of being discarded. This recovery API is the safety boundary: consumers should not bypass it with the private raw request client.