@huskly/ibkr-client 2.4.3 → 2.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -0
- package/dist/flex/flexClient.d.ts +17 -0
- package/dist/flex/flexClient.d.ts.map +1 -0
- package/dist/flex/flexClient.js +153 -0
- package/dist/flex/flexClient.js.map +1 -0
- package/dist/flex/flexStatement.d.ts +10 -0
- package/dist/flex/flexStatement.d.ts.map +1 -0
- package/dist/flex/flexStatement.js +104 -0
- package/dist/flex/flexStatement.js.map +1 -0
- package/dist/flex/types.d.ts +24 -0
- package/dist/flex/types.d.ts.map +1 -0
- package/dist/flex/types.js +2 -0
- package/dist/flex/types.js.map +1 -0
- package/dist/ibkr/equityContract.d.ts +4 -0
- package/dist/ibkr/equityContract.d.ts.map +1 -0
- package/dist/ibkr/equityContract.js +45 -0
- package/dist/ibkr/equityContract.js.map +1 -0
- package/dist/ibkr/ibkrClient.d.ts +10 -1
- package/dist/ibkr/ibkrClient.d.ts.map +1 -1
- package/dist/ibkr/ibkrClient.js +148 -1
- package/dist/ibkr/ibkrClient.js.map +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +31 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -1
package/dist/ibkr/ibkrClient.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { ASSET_CLASS_LABELS, toNullableNumber, toNumber } from "../helpers.js";
|
|
3
3
|
import { normalizeOptionContract, parseOsiOptionSymbol } from "./optionContract.js";
|
|
4
|
+
import { normalizeEquityContract } from "./equityContract.js";
|
|
4
5
|
import { normalizeDerivativeContract, normalizeDerivativeDataAvailability, } from "./derivativeContract.js";
|
|
5
6
|
import { IbkrRequestScheduler, } from "./requestScheduler.js";
|
|
6
7
|
// `ibkr-client`'s published ESM build is broken: its `import` condition points
|
|
@@ -572,6 +573,39 @@ export class IbkrClient {
|
|
|
572
573
|
: [],
|
|
573
574
|
};
|
|
574
575
|
}
|
|
576
|
+
async previewEquityOrder(request) {
|
|
577
|
+
this.assertOpen();
|
|
578
|
+
this.validateEquityOrderFields(request);
|
|
579
|
+
return this.withTradingMutation(request.accountId, "IBKR brokerage session is not safely authenticated for What-If", async (diagnostics) => {
|
|
580
|
+
await this.req({
|
|
581
|
+
path: "iserver/marketdata/snapshot",
|
|
582
|
+
params: { conids: String(request.contract.conid), fields: "6509" },
|
|
583
|
+
});
|
|
584
|
+
const response = await this.singleAttemptRequest({
|
|
585
|
+
path: `iserver/account/${request.accountId}/orders/whatif`,
|
|
586
|
+
method: "POST",
|
|
587
|
+
data: { orders: [this.equityOrderTicket(request)] },
|
|
588
|
+
});
|
|
589
|
+
return this.normalizeComboPreview(request.accountId, diagnostics, response);
|
|
590
|
+
});
|
|
591
|
+
}
|
|
592
|
+
async submitEquityOrder(request) {
|
|
593
|
+
this.assertOpen();
|
|
594
|
+
this.validateEquityOrderFields(request);
|
|
595
|
+
if (!request.clientOrderId.trim() || request.clientOrderId.length > 64) {
|
|
596
|
+
throw new Error("Client order ID must contain 1 to 64 characters");
|
|
597
|
+
}
|
|
598
|
+
return this.withTradingMutation(request.accountId, "IBKR brokerage session is not safely authenticated for submission", async () => {
|
|
599
|
+
const response = await this.singleAttemptRequest({
|
|
600
|
+
path: `iserver/account/${request.accountId}/orders`,
|
|
601
|
+
method: "POST",
|
|
602
|
+
data: {
|
|
603
|
+
orders: [{ ...this.equityOrderTicket(request), cOID: request.clientOrderId }],
|
|
604
|
+
},
|
|
605
|
+
});
|
|
606
|
+
return this.normalizeOrderSubmission(response, request.clientOrderId);
|
|
607
|
+
});
|
|
608
|
+
}
|
|
575
609
|
async previewDerivativeCombo(request) {
|
|
576
610
|
this.assertOpen();
|
|
577
611
|
this.validateComboPreview(request);
|
|
@@ -1578,6 +1612,15 @@ export class IbkrClient {
|
|
|
1578
1612
|
await this.wait(Math.min(pollMs, Math.max(0, deadline - this.now())));
|
|
1579
1613
|
}
|
|
1580
1614
|
}
|
|
1615
|
+
getEquityOrderStatus(accountId, orderId) {
|
|
1616
|
+
return this.getDerivativeOrderStatus(accountId, orderId);
|
|
1617
|
+
}
|
|
1618
|
+
findEquityOrder(input) {
|
|
1619
|
+
return this.findDerivativeOrder(input);
|
|
1620
|
+
}
|
|
1621
|
+
cancelEquityOrder(input) {
|
|
1622
|
+
return this.cancelDerivativeOrder({ ...input, assetClass: "STK" });
|
|
1623
|
+
}
|
|
1581
1624
|
async cancelDerivativeOrder(input) {
|
|
1582
1625
|
this.assertOpen();
|
|
1583
1626
|
if (!input.accountId.trim() || !input.orderId.trim()) {
|
|
@@ -1979,6 +2022,72 @@ export class IbkrClient {
|
|
|
1979
2022
|
return quotes;
|
|
1980
2023
|
}
|
|
1981
2024
|
/** Resolve equity/ETF symbols to IBKR contracts via `trsrv/stocks`. */
|
|
2025
|
+
/** Resolve one exact SMART-routed US stock or ETF contract. */
|
|
2026
|
+
async resolveEquityContract(symbol) {
|
|
2027
|
+
this.assertOpen();
|
|
2028
|
+
const requestedSymbol = symbol.trim().toUpperCase();
|
|
2029
|
+
if (!requestedSymbol || /[\r\n\t]/.test(symbol)) {
|
|
2030
|
+
throw new Error("Equity contract resolution requires a usable symbol");
|
|
2031
|
+
}
|
|
2032
|
+
const response = await this.req({
|
|
2033
|
+
path: "trsrv/stocks",
|
|
2034
|
+
params: { symbols: requestedSymbol },
|
|
2035
|
+
});
|
|
2036
|
+
if (!isUnknownRecord(response)) {
|
|
2037
|
+
throw new Error("IBKR returned incomplete exact US equity search evidence");
|
|
2038
|
+
}
|
|
2039
|
+
const rawListings = response[requestedSymbol];
|
|
2040
|
+
if (!Array.isArray(rawListings)) {
|
|
2041
|
+
throw new Error("IBKR returned no exact US equity listing evidence");
|
|
2042
|
+
}
|
|
2043
|
+
const byConid = new Map();
|
|
2044
|
+
let conflicting = false;
|
|
2045
|
+
for (const rawListing of rawListings) {
|
|
2046
|
+
if (!isUnknownRecord(rawListing) || rawListing["assetClass"] !== "STK")
|
|
2047
|
+
continue;
|
|
2048
|
+
const contracts = rawListing["contracts"];
|
|
2049
|
+
if (!Array.isArray(contracts))
|
|
2050
|
+
continue;
|
|
2051
|
+
for (const rawContract of contracts) {
|
|
2052
|
+
if (!isUnknownRecord(rawContract) || rawContract["isUS"] !== true)
|
|
2053
|
+
continue;
|
|
2054
|
+
const conid = rawContract["conid"];
|
|
2055
|
+
const primaryExchange = this.trimmedString(rawContract["exchange"])?.toUpperCase();
|
|
2056
|
+
if (!Number.isSafeInteger(conid) || conid <= 0 || !primaryExchange)
|
|
2057
|
+
continue;
|
|
2058
|
+
const previous = byConid.get(conid);
|
|
2059
|
+
if (previous !== undefined && previous !== primaryExchange)
|
|
2060
|
+
conflicting = true;
|
|
2061
|
+
byConid.set(conid, primaryExchange);
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
2064
|
+
if (conflicting || byConid.size !== 1) {
|
|
2065
|
+
throw new Error("IBKR did not return one exact US equity listing");
|
|
2066
|
+
}
|
|
2067
|
+
const [candidate] = byConid;
|
|
2068
|
+
if (candidate === undefined)
|
|
2069
|
+
throw new Error("IBKR returned no exact US equity listing");
|
|
2070
|
+
const [conid, primaryExchange] = candidate;
|
|
2071
|
+
const evidence = this.contractReferenceEvidence(conid, await this.readContractReference(conid));
|
|
2072
|
+
const validExchanges = evidence.validExchanges
|
|
2073
|
+
?.split(",")
|
|
2074
|
+
.map((value) => value.trim().toUpperCase())
|
|
2075
|
+
.filter(Boolean) ?? [];
|
|
2076
|
+
const contract = normalizeEquityContract({
|
|
2077
|
+
conid: evidence.conid,
|
|
2078
|
+
assetClass: evidence.instrumentType?.toUpperCase(),
|
|
2079
|
+
symbol: evidence.symbol,
|
|
2080
|
+
exchange: evidence.exchange,
|
|
2081
|
+
primaryExchange,
|
|
2082
|
+
currency: evidence.currency,
|
|
2083
|
+
});
|
|
2084
|
+
if (contract?.conid !== conid ||
|
|
2085
|
+
contract.symbol !== requestedSymbol ||
|
|
2086
|
+
!validExchanges.includes("SMART")) {
|
|
2087
|
+
throw new Error("IBKR equity contract details are incomplete or conflicting");
|
|
2088
|
+
}
|
|
2089
|
+
return contract;
|
|
2090
|
+
}
|
|
1982
2091
|
async searchInstruments(symbol, projection = "symbol-search") {
|
|
1983
2092
|
this.assertOpen();
|
|
1984
2093
|
if (projection !== "symbol-search" && projection !== "search") {
|
|
@@ -2439,6 +2548,44 @@ export class IbkrClient {
|
|
|
2439
2548
|
return false;
|
|
2440
2549
|
}
|
|
2441
2550
|
}
|
|
2551
|
+
validateEquityOrderFields(request) {
|
|
2552
|
+
if (!request.accountId.trim())
|
|
2553
|
+
throw new Error("An explicit IBKR account ID is required");
|
|
2554
|
+
if (normalizeEquityContract(request.contract) === null) {
|
|
2555
|
+
throw new Error("Equity order requires one exact US equity contract");
|
|
2556
|
+
}
|
|
2557
|
+
const fields = request;
|
|
2558
|
+
if (fields["side"] !== "BUY" && fields["side"] !== "SELL") {
|
|
2559
|
+
throw new Error("Equity order side must be BUY or SELL");
|
|
2560
|
+
}
|
|
2561
|
+
if (!Number.isSafeInteger(fields["quantity"]) || fields["quantity"] <= 0) {
|
|
2562
|
+
throw new Error("Equity order quantity must be a positive integer");
|
|
2563
|
+
}
|
|
2564
|
+
if (fields["orderType"] !== "LMT" ||
|
|
2565
|
+
typeof fields["limit"] !== "number" ||
|
|
2566
|
+
!Number.isFinite(fields["limit"]) ||
|
|
2567
|
+
fields["limit"] <= 0) {
|
|
2568
|
+
throw new Error("Equity LIMIT order requires a positive limit price");
|
|
2569
|
+
}
|
|
2570
|
+
if (fields["tif"] !== "DAY" && fields["tif"] !== "GTC") {
|
|
2571
|
+
throw new Error("Equity order TIF must be DAY or GTC");
|
|
2572
|
+
}
|
|
2573
|
+
if (fields["session"] !== "REGULAR" && fields["session"] !== "OVERNIGHT") {
|
|
2574
|
+
throw new Error("Equity order session must be REGULAR or OVERNIGHT");
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
equityOrderTicket(request) {
|
|
2578
|
+
return {
|
|
2579
|
+
acctId: request.accountId,
|
|
2580
|
+
conid: request.contract.conid,
|
|
2581
|
+
orderType: "LMT",
|
|
2582
|
+
side: request.side,
|
|
2583
|
+
price: request.limit,
|
|
2584
|
+
tif: request.tif,
|
|
2585
|
+
quantity: request.quantity,
|
|
2586
|
+
outsideRTH: request.session === "OVERNIGHT",
|
|
2587
|
+
};
|
|
2588
|
+
}
|
|
2442
2589
|
validateSingleOrder(request) {
|
|
2443
2590
|
this.validateSingleOrderFields(request);
|
|
2444
2591
|
const identityFields = request;
|
|
@@ -2489,7 +2636,7 @@ export class IbkrClient {
|
|
|
2489
2636
|
}
|
|
2490
2637
|
}
|
|
2491
2638
|
cmeOperatorMetadata(assetClass, input) {
|
|
2492
|
-
if (assetClass
|
|
2639
|
+
if (assetClass !== "FOP")
|
|
2493
2640
|
return {};
|
|
2494
2641
|
if (!input.extOperator?.trim() || input.manualIndicator === undefined) {
|
|
2495
2642
|
throw new Error(`${assetClass} orders require exact CME operator metadata`);
|