@nemesis-oss/binance-sdk 1.0.0 → 3.0.0
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/LICENSE +21 -0
- package/README.md +409 -10
- package/dist/index-C06A22Dz.d.cts +6240 -0
- package/dist/index-C06A22Dz.d.ts +6240 -0
- package/dist/index.cjs +11749 -2826
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1955 -295
- package/dist/index.d.ts +1955 -295
- package/dist/index.js +11554 -2825
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.cjs +8666 -2128
- package/dist/mcp/index.cjs.map +1 -1
- package/dist/mcp/index.js +8667 -2129
- package/dist/mcp/index.js.map +1 -1
- package/dist/tools/index.cjs +578 -481
- package/dist/tools/index.cjs.map +1 -1
- package/dist/tools/index.d.cts +3 -1
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.js +577 -481
- package/dist/tools/index.js.map +1 -1
- package/package.json +13 -5
- package/dist/index-D2T_Rhwj.d.cts +0 -2055
- package/dist/index-D2T_Rhwj.d.ts +0 -2055
package/dist/index.d.ts
CHANGED
|
@@ -1,318 +1,1945 @@
|
|
|
1
|
-
import { F as
|
|
2
|
-
export { A as Account, a as AccountAsset, b as AccountAssetSchema, c as AccountPosition, d as AccountPositionSchema, e as AccountSchema, f as AggTrade, g as AggTradeSchema, h as AggTradesResponseSchema, i as AlgoOrder, j as AlgoOrderResponseSchema, k as AlgoOrderSchema, l as AlgoOrdersResponseSchema, m as ApiTradingStatus, n as ApiTradingStatusSchema, o as AuthMode, p as AvgPrice, q as AvgPriceSchema, r as Balance, s as BalanceSchema, t as BalancesResponseSchema, u as BaseWS, v as BaseWSOptions, w as BatchOrdersItem, x as BatchOrdersItemSchema, y as BatchOrdersResponseSchema, z as BinanceClientOptions, C as BookTicker, D as BookTickerSchema, E as BracketOrderParams, G as ClosePositionParams, H as CommissionRate, I as CommissionRateSchema, J as ContractType, K as CountdownCancelAll, L as CountdownCancelAllSchema, M as CreateOrderParams, N as CreateOrderParamsSchema, O as DepthSnapshot, P as DepthSnapshotSchema, Q as ExchangeInfo, R as ExchangeInfoSchema, S as ExchangeSymbol, T as ExchangeSymbolSchema, U as FeeBurnStatus, V as FeeBurnStatusSchema, W as FundingRate, X as FundingRateHistorySchema, Y as FundingRateSchema, Z as FuturesAccount, _ as FuturesData, $ as FuturesDataEndpoints, a0 as FuturesMarketWS, a1 as FuturesOps, a2 as FuturesToolkit, a3 as FuturesTrading, a4 as FuturesUserWS, a5 as FuturesUserWSOptions, a6 as HttpClient, a7 as HttpClientOptions, a8 as HttpMethod, a9 as Income, aa as IncomeHistoryResponseSchema, ab as IncomeSchema, ac as Kline, ad as KlineInterval, ae as KlineIntervalSchema, af as KlineSchema, ag as KlinesResponseSchema, ah as LeverageBracket, ai as LeverageBracketResponseSchema, aj as LeverageBracketSchema, ak as LeverageChange, al as LeverageChangeSchema, am as ListenKey, an as ListenKeySchema, ao as LongShortRatioEntry, ap as LongShortRatioEntrySchema, aq as LongShortRatioSchema, ar as MarginTypeChange, as as MarginTypeChangeSchema, at as MarkPriceSpeed, au as MarketDataBase, av as ModifyOrderParams, aw as ModifyOrderParamsSchema, ax as MultiAssetsMargin, ay as MultiAssetsMarginSchema, az as NewOrderAck, aA as NewOrderAckSchema, aB as OpenInterest, aC as OpenInterestHistEntry, aD as OpenInterestHistEntrySchema, aE as OpenInterestHistSchema, aF as OpenInterestSchema, aG as Order, aH as OrderModifyHistoryEntry, aI as OrderModifyHistoryEntrySchema, aJ as OrderModifyHistorySchema, aK as OrderResponseSchema, aL as OrderSchema, aM as OrdersResponseSchema, aN as PositionMarginChange, aO as PositionMarginChangeSchema, aP as PositionMarginHistoryEntry, aQ as PositionMarginHistoryEntrySchema, aR as PositionMarginHistorySchema, aS as PositionMode, aT as PositionModeSchema, aU as PositionRisk, aV as PositionRiskResponseSchema, aW as PositionRiskSchema, aX as PositionSizing, aY as PremiumIndex, aZ as PremiumIndexSchema, a_ as RateLimitOrder, a$ as RateLimitOrderResponseSchema, b0 as RateLimitOrderSchema, b1 as SizePositionParams, b2 as SpotAccount, b3 as SpotAccountInfo, b4 as SpotAccountSchema, b5 as SpotBalance, b6 as SpotBalanceSchema, b7 as SpotBalanceUpdate, b8 as SpotBalanceUpdateSchema, b9 as SpotExecutionReport, ba as SpotExecutionReportSchema, bb as SpotListStatus, bc as SpotListStatusSchema, bd as SpotMarket, be as SpotMarketWS, bf as SpotOCOOrderAck, bg as SpotOCOOrderAckResponseSchema, bh as SpotOCOOrderAckSchema, bi as SpotOCOOrdersResponseSchema, bj as SpotOrder, bk as SpotOrderResponseSchema, bl as SpotOrderSchema, bm as SpotOrdersResponseSchema, bn as SpotOutboundAccountPosition, bo as SpotOutboundAccountPositionSchema, bp as SpotTrade, bq as SpotTradeSchema, br as SpotTradesResponseSchema, bs as SpotTrading, bt as SpotUserDataEvent, bu as SpotUserDataStream, bv as SpotUserWS, bw as SpotUserWSOptions, bx as SymbolRules, by as TakerLongShortRatioEntry, bz as TakerLongShortRatioEntrySchema, bA as TakerLongShortRatioSchema, bB as Ticker24hr, bC as Ticker24hrSchema, bD as TickerPrice, bE as TickerPriceSchema, bF as ToolContext, bG as ToolDefinition, bH as Trade, bI as TradeSchema, bJ as TradesResponseSchema, bK as UserDataAccountUpdate, bL as UserDataAccountUpdateSchema, bM as UserDataEvent, bN as UserDataMarginCall, bO as UserDataMarginCallSchema, bP as UserDataOrderTradeUpdate, bQ as UserDataOrderTradeUpdateSchema, bR as UserDataStream, bS as UserTrade, bT as UserTradeSchema, bU as UserTradesResponseSchema, bV as WsApi, bW as WsApiOptions, bX as WsApiResponse, bY as WsApiResponseSchema, bZ as accountTools, b_ as createFuturesToolkit, b$ as createPaperContext, c0 as derivedTools, c1 as floorToStep, c2 as formatToDecimals, c3 as getBufferedWsEvents, c4 as marketDataTools, c5 as paperTools, c6 as parseSpotUserDataEvent, c7 as parseUserDataEvent, c8 as roundToStep, c9 as spotTools, ca as stepDecimals, cb as symbolRulesFrom, cc as textResult, cd as toAnthropicTool, ce as toJsonSchema, cf as toMCPTool, cg as toOpenAITool, ch as toToolList, ci as toolkitToFormats, cj as tradingTools, ck as wsTools } from './index-D2T_Rhwj.js';
|
|
3
|
-
import {
|
|
1
|
+
import { B as BinanceClient, a as BinanceClientOptions, C as CoreContext, E as ExecutionAdapter, P as PaperTradingEngine, b as CreateOrderParams, O as OrderKey, c as OrderShape, U as UserStreamLike, d as ExecutionReportShape, e as ExecutionFill, f as EventBus, W as WsConnection, g as WsConnectionOptions, h as ExecutionManager, i as BookLevel, j as OrderBookMetrics, D as DiffInput, k as BookSnapshotInput, l as UsdmSurface, S as SpotSurface, m as CoinMSurface, n as WsFamily, R as ReconnectPolicy, F as FuturesMarketWS, o as SpotMarketWS, p as CoinMMarketWS, q as WsConnectionStats, r as SubscribeOptions, s as Subscription, t as PaperTradingOptions, u as FuturesToolkitOptions } from './index-C06A22Dz.js';
|
|
2
|
+
export { A as Account, v as AccountAsset, w as AccountAssetSchema, x as AccountPosition, y as AccountPositionSchema, z as AccountSchema, G as AccountSnapshot, H as AccountSnapshotSchema, I as AggTrade, J as AggTradeSchema, K as AggTradesResponseSchema, L as AlgoOrder, M as AlgoOrderResponseSchema, N as AlgoOrderSchema, Q as AlgoOrdersResponseSchema, T as ApiKeyPermissions, V as ApiKeyPermissionsSchema, X as ApiTradingStatus, Y as ApiTradingStatusSchema, Z as AssetDetail, _ as AssetDetailSchema, $ as AuditAction, a0 as AuditOutcome, a1 as AuditRecord, a2 as AuditSink, a3 as AuthMode, a4 as AvgPrice, a5 as AvgPriceSchema, a6 as Balance, a7 as BalanceSchema, a8 as BalancesResponseSchema, a9 as BaseWS, aa as BaseWSOptions, ab as BatchOrdersItem, ac as BatchOrdersItemSchema, ad as BatchOrdersResponseSchema, ae as BinanceUsdmFeeModel, af as BookTicker, ag as BookTickerSchema, ah as BookView, ai as BorrowRepayParams, aj as BorrowRepayParamsSchema, ak as BorrowRepayRecordEntry, al as BorrowRepayRecordEntrySchema, am as BorrowRepayRecordResponse, an as BorrowRepayRecordResponseSchema, ao as BorrowRepayResult, ap as BorrowRepayResultSchema, aq as BracketOrderParams, ar as CircuitBreakerState, as as ClosePositionParams, at as CoinMAccount, au as CoinMAccountAsset, av as CoinMAccountAssetSchema, aw as CoinMAccountInfo, ax as CoinMAccountPosition, ay as CoinMAccountPositionSchema, az as CoinMAccountSchema, aA as CoinMBalance, aB as CoinMBalanceResponseSchema, aC as CoinMBalanceSchema, aD as CoinMCommissionRate, aE as CoinMCommissionRateSchema, aF as CoinMContractType, aG as CoinMCreateOrderParams, aH as CoinMCreateOrderParamsSchema, aI as CoinMIncome, aJ as CoinMIncomeHistoryResponseSchema, aK as CoinMIncomeSchema, aL as CoinMLeverageBracket, aM as CoinMLeverageBracketResponseSchema, aN as CoinMLeverageBracketSchema, aO as CoinMLeverageChange, aP as CoinMLeverageChangeSchema, aQ as CoinMMarginTypeChange, aR as CoinMMarginTypeChangeSchema, aS as CoinMMarkPriceSpeed, aT as CoinMMarket, aU as CoinMOpenInterestHistEntry, aV as CoinMOpenInterestHistEntrySchema, aW as CoinMOpenInterestHistSchema, aX as CoinMOrder, aY as CoinMOrderResponseSchema, aZ as CoinMOrderSchema, a_ as CoinMOrdersResponseSchema, a$ as CoinMPositionMarginChange, b0 as CoinMPositionMarginChangeSchema, b1 as CoinMPositionMode, b2 as CoinMPositionModeSchema, b3 as CoinMPositionRisk, b4 as CoinMPositionRiskResponseSchema, b5 as CoinMPositionRiskSchema, b6 as CoinMTrading, b7 as CoinMUserDataStream, b8 as CoinMUserTrade, b9 as CoinMUserTradeSchema, ba as CoinMUserTradesResponseSchema, bb as CoinMUserWS, bc as CoinMUserWSOptions, bd as CommissionRate, be as CommissionRateSchema, bf as CompositeModel, bg as ContractType, bh as CoreTransportOptions, bi as CountdownCancelAll, bj as CountdownCancelAllSchema, bk as CreateOrderParamsSchema, bl as Credentials, bm as CrossMarginAccount, bn as CrossMarginAccountSchema, bo as Decimal, bp as DecimalInput, bq as DepositAddress, br as DepositAddressSchema, bs as DepositHistoryEntry, bt as DepositHistoryEntrySchema, bu as DepositHistoryResponseSchema, bv as DepthSnapshot, bw as DepthSnapshotSchema, bx as DustConversionResult, by as DustConversionResultSchema, bz as DustLogEntry, bA as DustLogEntrySchema, bB as DustLogResponse, bC as DustLogResponseSchema, bD as Endpoints, bE as Environment, bF as EventBusOptions, bG as ExchangeInfo, bH as ExchangeInfoSchema, bI as ExchangeSymbol, bJ as ExchangeSymbolSchema, bK as Execution, bL as ExecutionBackend, bM as ExecutionContext, bN as ExecutionGateway, bO as ExecutionGatewayOptions, bP as ExecutionManagerOptions, bQ as ExecutionModel, bR as ExecutionQuote, bS as ExecutionUnknownError, bT as FanOutAuditSink, bU as FeeBurnStatus, bV as FeeBurnStatusSchema, bW as FeeModel, bX as FeeQuote, bY as FundingAsset, bZ as FundingAssetSchema, b_ as FundingAssetsResponseSchema, b$ as FundingRate, c0 as FundingRateHistorySchema, c1 as FundingRateSchema, c2 as FuturesAccount, c3 as FuturesData, c4 as FuturesDataEndpoints, c5 as FuturesExecutionAdapter, c6 as FuturesMarket, c7 as FuturesNamespace, c8 as FuturesOps, c9 as FuturesToolkit, ca as FuturesTrading, cb as FuturesUserWS, cc as FuturesUserWSOptions, cd as FuturesWsApiClient, ce as HttpClient, cf as HttpClientOptions, cg as HttpMethod, ch as InMemoryAuditSink, ci as Income, cj as IncomeHistoryResponseSchema, ck as IncomeSchema, cl as InstantFillModel, cm as IsolatedMarginAccount, cn as IsolatedMarginAccountSchema, co as IsolatedMarginAsset, cp as IsolatedMarginAssetSchema, cq as IsolatedMarginSymbol, cr as IsolatedMarginSymbolSchema, cs as IsolatedTransferParams, ct as IsolatedTransferParamsSchema, cu as Kline, cv as KlineInterval, cw as KlineIntervalSchema, cx as KlineSchema, cy as KlinesResponseSchema, cz as LatencyModel, cA as LeverageBracket, cB as LeverageBracketResponseSchema, cC as LeverageBracketSchema, cD as LeverageChange, cE as LeverageChangeSchema, cF as ListenKey, cG as ListenKeySchema, cH as LongShortRatioEntry, cI as LongShortRatioEntrySchema, cJ as LongShortRatioSchema, cK as MarginAccount, cL as MarginAssetBalance, cM as MarginAssetBalanceSchema, cN as MarginCreateOrderParams, cO as MarginCreateOrderParamsSchema, cP as MarginForceLiquidationEntry, cQ as MarginForceLiquidationEntrySchema, cR as MarginForceLiquidationResponse, cS as MarginForceLiquidationResponseSchema, cT as MarginInterestHistoryEntry, cU as MarginInterestHistoryEntrySchema, cV as MarginInterestHistoryResponse, cW as MarginInterestHistoryResponseSchema, cX as MarginNamespace, cY as MarginOrder, cZ as MarginOrderResponseSchema, c_ as MarginOrderSchema, c$ as MarginOrdersResponseSchema, d0 as MarginPriceIndex, d1 as MarginPriceIndexSchema, d2 as MarginTrade, d3 as MarginTradeSchema, d4 as MarginTradesResponseSchema, d5 as MarginTrading, d6 as MarginTransferResult, d7 as MarginTransferResultSchema, d8 as MarginTypeChange, d9 as MarginTypeChangeSchema, da as MarkPriceSpeed, db as MarketDataBase, dc as MaxBorrowable, dd as MaxBorrowableSchema, de as MaxTransferable, df as MaxTransferableSchema, dg as ModifyOrderParams, dh as ModifyOrderParamsSchema, di as MultiAssetsMargin, dj as MultiAssetsMarginSchema, dk as NewOrderAck, dl as NewOrderAckSchema, dm as OpenInterest, dn as OpenInterestHistEntry, dp as OpenInterestHistEntrySchema, dq as OpenInterestHistSchema, dr as OpenInterestSchema, ds as Order, dt as OrderBook, du as OrderBookEngine, dv as OrderBookEngineOptions, dw as OrderBookModel, dx as OrderModifyHistoryEntry, dy as OrderModifyHistoryEntrySchema, dz as OrderModifyHistorySchema, dA as OrderResponseSchema, dB as OrderSchema, dC as OrdersResponseSchema, dD as PaperAccount, dE as PaperOrder, dF as PaperPosition, dG as PaperPositionSide, dH as PartialFillModel, dI as PositionMarginChange, dJ as PositionMarginChangeSchema, dK as PositionMarginHistoryEntry, dL as PositionMarginHistoryEntrySchema, dM as PositionMarginHistorySchema, dN as PositionMode, dO as PositionModeSchema, dP as PositionRisk, dQ as PositionRiskResponseSchema, dR as PositionRiskSchema, dS as PositionSizing, dT as PremiumIndex, dU as PremiumIndexSchema, dV as RateLimitOrder, dW as RateLimitOrderResponseSchema, dX as RateLimitOrderSchema, dY as RateLimitTracker, dZ as RateLimitTrackerOptions, d_ as RateLimitUsage, d$ as RawLevel, e0 as ReconciliationState, e1 as RestHost, e2 as RetryPolicy, e3 as RiskGateway, e4 as RiskGatewayOptions, e5 as RiskStatus, e6 as SdkEvent, e7 as SdkEventListener, e8 as SdkLogger, e9 as SignatureAlgorithm, ea as Signer, eb as SignerOptions, ec as SizePositionParams, ed as SlippageModel, ee as SpotAccount, ef as SpotAccountInfo, eg as SpotAccountSchema, eh as SpotBalance, ei as SpotBalanceSchema, ej as SpotBalanceUpdate, ek as SpotBalanceUpdateSchema, el as SpotExecutionAdapter, em as SpotExecutionReport, en as SpotExecutionReportSchema, eo as SpotListStatus, ep as SpotListStatusSchema, eq as SpotMarket, er as SpotOCOOrderAck, es as SpotOCOOrderAckResponseSchema, et as SpotOCOOrderAckSchema, eu as SpotOCOOrdersResponseSchema, ev as SpotOrder, ew as SpotOrderResponseSchema, ex as SpotOrderSchema, ey as SpotOrdersResponseSchema, ez as SpotOutboundAccountPosition, eA as SpotOutboundAccountPositionSchema, eB as SpotTrade, eC as SpotTradeSchema, eD as SpotTradesResponseSchema, eE as SpotTrading, eF as SpotUserDataEvent, eG as SpotUserDataStream, eH as SpotUserWS, eI as SpotUserWSOptions, eJ as SpotWsApi, eK as SpotWsApiClient, eL as SpotWsApiOptions, eM as StreamAuditSink, eN as SubAccount, eO as SubAccountDepositAddress, eP as SubAccountDepositAddressSchema, eQ as SubAccountDepositHistoryEntry, eR as SubAccountDepositHistoryEntrySchema, eS as SubAccountDepositHistoryResponseSchema, eT as SubAccountEnableResult, eU as SubAccountEnableResultSchema, eV as SubAccountEntry, eW as SubAccountEntrySchema, eX as SubAccountFuturesSummary, eY as SubAccountFuturesSummarySchema, eZ as SubAccountListResponse, e_ as SubAccountListResponseSchema, e$ as SubAccountMarginAccount, f0 as SubAccountMarginAccountSchema, f1 as SubAccountSpotAsset, f2 as SubAccountSpotAssetSchema, f3 as SubAccountSpotAssetsResponse, f4 as SubAccountSpotAssetsResponseSchema, f5 as SubAccountSpotSummary, f6 as SubAccountSpotSummaryEntry, f7 as SubAccountSpotSummaryEntrySchema, f8 as SubAccountSpotSummarySchema, f9 as SubAccountStatus, fa as SubAccountStatusResponseSchema, fb as SubAccountStatusSchema, fc as SubAccountUniversalTransferEntry, fd as SubAccountUniversalTransferEntrySchema, fe as SubAccountUniversalTransferHistoryResponse, ff as SubAccountUniversalTransferHistoryResponseSchema, fg as SubAccountUniversalTransferParams, fh as SubAccountUniversalTransferParamsSchema, fi as SubAccountUniversalTransferResult, fj as SubAccountUniversalTransferResultSchema, fk as SubscriptionState, fl as SymbolRules, fm as TakerLongShortRatioEntry, fn as TakerLongShortRatioEntrySchema, fo as TakerLongShortRatioSchema, fp as TakerMakerFeeModel, fq as Ticker24hr, fr as Ticker24hrSchema, fs as TickerPrice, ft as TickerPriceSchema, fu as ToolContext, fv as ToolDefinition, fw as Trade, fx as TradeFeeEntry, fy as TradeFeeEntrySchema, fz as TradeFeeResponseSchema, fA as TradeSchema, fB as TradesResponseSchema, fC as TradingPolicy, fD as TradingPolicyOptions, fE as TransferType, fF as TransferTypeSchema, fG as UniversalTransferHistoryEntry, fH as UniversalTransferHistoryEntrySchema, fI as UniversalTransferHistoryResponse, fJ as UniversalTransferHistoryResponseSchema, fK as UniversalTransferParams, fL as UniversalTransferParamsSchema, fM as UniversalTransferResult, fN as UniversalTransferResultSchema, fO as UserAsset, fP as UserAssetSchema, fQ as UserAssetsResponseSchema, fR as UserDataAccountUpdate, fS as UserDataAccountUpdateSchema, fT as UserDataEvent, fU as UserDataMarginCall, fV as UserDataMarginCallSchema, fW as UserDataOrderTradeUpdate, fX as UserDataOrderTradeUpdateSchema, fY as UserDataStream, fZ as UserTrade, f_ as UserTradeSchema, f$ as UserTradesResponseSchema, g0 as VirtualSubAccountResult, g1 as VirtualSubAccountResultSchema, g2 as WS_FAMILY_LIMITS, g3 as WS_PLATFORM_DEFAULTS, g4 as Wallet, g5 as WithdrawHistoryEntry, g6 as WithdrawHistoryEntrySchema, g7 as WithdrawHistoryResponseSchema, g8 as WithdrawParams, g9 as WithdrawParamsSchema, ga as WithdrawResult, gb as WithdrawResultSchema, gc as WsAggTradePayload, gd as WsAggTradePayloadSchema, ge as WsApi, gf as WsApiClient, gg as WsApiClientOptions, gh as WsApiClientState, gi as WsApiOptions, gj as WsApiResponse, gk as WsApiResponseSchema, gl as WsApiSurface, gm as WsAssetIndexPayload, gn as WsAssetIndexPayloadSchema, go as WsBookTickerPayload, gp as WsBookTickerPayloadSchema, gq as WsCompositeIndexPayload, gr as WsCompositeIndexPayloadSchema, gs as WsConnectionState, gt as WsDepthUpdatePayload, gu as WsDepthUpdatePayloadSchema, gv as WsFamilyLimits, gw as WsFamilyStreams, gx as WsForceOrderPayload, gy as WsForceOrderPayloadSchema, gz as WsKlinePayload, gA as WsKlinePayloadSchema, gB as WsMarkPricePayload, gC as WsMarkPricePayloadSchema, gD as WsMiniTickerPayload, gE as WsMiniTickerPayloadSchema, gF as WsPlatform, gG as WsPlatformOptions, gH as WsPlatformStats, gI as WsRequestOptions, gJ as WsRollingWindowTickerPayload, gK as WsRollingWindowTickerPayloadSchema, gL as WsSpotAvgPricePayload, gM as WsSpotAvgPricePayloadSchema, gN as WsStreamPayload, gO as WsTicker24hrPayload, gP as WsTicker24hrPayloadSchema, gQ as WsTradePayload, gR as WsTradePayloadSchema, gS as accountTools, gT as auditRecordFromExecution, gU as createFuturesToolkit, gV as createPaperContext, gW as createWsPlatform, gX as dec, gY as derivedTools, gZ as executionTools, g_ as familyLimits, g$ as floorToStep, h0 as formatToDecimals, h1 as forwardEventsToLogger, h2 as getBufferedWsEvents, h3 as isExecutionAdapter, h4 as marketDataTools, h5 as paperTools, h6 as parseSpotUserDataEvent, h7 as parseUserDataEvent, h8 as parseWsPayload, h9 as resolveEnvironment, ha as roundToStep, hb as spotTools, hc as stepDecimals, hd as sum, he as symbolRulesFrom, hf as textResult, hg as toAnthropicTool, hh as toJsonSchema, hi as toMCPTool, hj as toOpenAITool, hk as toToolList, hl as toolkitToFormats, hm as tradingTools, hn as vwap, ho as wsTools } from './index-C06A22Dz.js';
|
|
3
|
+
import { EventEmitter } from 'node:events';
|
|
4
|
+
import WebSocket from 'ws';
|
|
4
5
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
5
|
-
import '
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
6
|
+
import 'axios';
|
|
7
|
+
import 'zod';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Single source of truth for the package version string.
|
|
11
|
+
*
|
|
12
|
+
* Read by:
|
|
13
|
+
* - {@link ./index.js} — re-exports as the public `VERSION` constant.
|
|
14
|
+
* - {@link ../mcp/server.js} — the MCP server identifies itself with this
|
|
15
|
+
* value when negotiating with hosts (was previously hard-coded as
|
|
16
|
+
* `'2.0.0'`, which drifted from `package.json`).
|
|
17
|
+
*
|
|
18
|
+
* Update in lockstep with the `version` field in `package.json` and the
|
|
19
|
+
* `[Unreleased]` / latest release section in `CHANGELOG.md`.
|
|
20
|
+
*/
|
|
21
|
+
declare const VERSION = "3.0.0";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Standalone product clients.
|
|
25
|
+
*
|
|
26
|
+
* `BinanceClient` is the full multi-product surface; these factories give
|
|
27
|
+
* focused callers just their product plus the shared lifecycle helpers
|
|
28
|
+
* (`syncTime`, `close`), constructed from the same client underneath — no
|
|
29
|
+
* duplicated wiring, no divergent behavior:
|
|
30
|
+
*
|
|
31
|
+
* ```ts
|
|
32
|
+
* import { createSpotClient, createUSDMClient } from '@nemesis-oss/binance-sdk';
|
|
33
|
+
*
|
|
34
|
+
* const spot = createSpotClient({ apiKey, apiSecret });
|
|
35
|
+
* await spot.syncTime();
|
|
36
|
+
* const books = await spot.market.depth('BTCUSDT', 20);
|
|
37
|
+
* spot.close();
|
|
38
|
+
*
|
|
39
|
+
* const usdm = createUSDMClient({ apiKey, apiSecret });
|
|
40
|
+
* await usdm.execution.placeOrder({ symbol: 'BTCUSDT', ... });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
/** The spot product surface plus shared lifecycle helpers. */
|
|
44
|
+
type SpotClient$1 = BinanceClient['spot'] & {
|
|
45
|
+
/** Sync local clocks against every REST host (mitigates -1021). */
|
|
46
|
+
syncTime(): Promise<void>;
|
|
47
|
+
/** Close all websockets and user streams created for this client. */
|
|
48
|
+
close(): void;
|
|
49
|
+
/** Rate-limit usage snapshots, per REST host. */
|
|
50
|
+
getRateLimitUsage(): ReturnType<BinanceClient['getRateLimitUsage']>;
|
|
29
51
|
};
|
|
52
|
+
/** The USDⓈ-M futures product surface plus shared lifecycle helpers. */
|
|
53
|
+
type USDMClient$1 = BinanceClient['futures'] & {
|
|
54
|
+
syncTime(): Promise<void>;
|
|
55
|
+
close(): void;
|
|
56
|
+
getRateLimitUsage(): ReturnType<BinanceClient['getRateLimitUsage']>;
|
|
57
|
+
};
|
|
58
|
+
/** The COIN-M futures product surface plus shared lifecycle helpers. */
|
|
59
|
+
type CoinMClient$1 = BinanceClient['coinm'] & {
|
|
60
|
+
syncTime(): Promise<void>;
|
|
61
|
+
close(): void;
|
|
62
|
+
getRateLimitUsage(): ReturnType<BinanceClient['getRateLimitUsage']>;
|
|
63
|
+
};
|
|
64
|
+
/** A spot-only client (market, account, trading, execution, ws, wsApi). */
|
|
65
|
+
declare function createSpotClient(options?: BinanceClientOptions): SpotClient$1;
|
|
66
|
+
/** A USDⓈ-M futures-only client (market, data, account, trading, ops, execution, ws, wsApi). */
|
|
67
|
+
declare function createUSDMClient(options?: BinanceClientOptions): USDMClient$1;
|
|
68
|
+
/** A COIN-M futures-only client (market, account, trading, ws). */
|
|
69
|
+
declare function createCoinMClient(options?: BinanceClientOptions): CoinMClient$1;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Lossless JSON parsing for Binance payloads.
|
|
73
|
+
*
|
|
74
|
+
* `JSON.parse` coerces every integer to a JS double, silently corrupting values
|
|
75
|
+
* above Number.MAX_SAFE_INTEGER (2^53 - 1). Binance identifiers (orderId,
|
|
76
|
+
* tradeId, updateId, transaction IDs) are the classic casualties: two distinct
|
|
77
|
+
* exchange IDs can compare equal after parsing.
|
|
78
|
+
*
|
|
79
|
+
* `parseJsonLossless` pre-passes the raw text and quotes any integer literal
|
|
80
|
+
* whose magnitude exceeds the safe range, so oversized IDs surface as decimal
|
|
81
|
+
* *strings* instead of silently-corrupted numbers. Values within the safe range
|
|
82
|
+
* keep their original type, which keeps all existing Zod schemas valid.
|
|
83
|
+
*/
|
|
84
|
+
declare function parseJsonLossless(text: string): unknown;
|
|
85
|
+
/** Type-safe wrapper for callers that know the payload shape. */
|
|
86
|
+
declare function parseJsonLosslessAs<T>(text: string): T;
|
|
87
|
+
/**
|
|
88
|
+
* Deep-walks a parsed payload converting long integer *strings* back into
|
|
89
|
+
* numbers only when they are safe — useful when the caller wants uniform
|
|
90
|
+
* `number` types for fields that are practically always small.
|
|
91
|
+
*/
|
|
92
|
+
declare function normalizeIntStrings<T>(value: T): T;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Product identifiers — the canonical set v3 namespaces address. Grows as
|
|
96
|
+
* products are migrated onto the platform (Options, Portfolio Margin, …).
|
|
97
|
+
*/
|
|
98
|
+
type ProductId = 'spot' | 'usdm' | 'coinm' | 'margin' | 'wallet' | 'subaccount' | 'convert';
|
|
99
|
+
/**
|
|
100
|
+
* v3 product boundary.
|
|
101
|
+
*
|
|
102
|
+
* A product client is anything constructed from a shared {@link CoreContext}
|
|
103
|
+
* that owns its product's surface. The contract is deliberately tiny —
|
|
104
|
+
* products differ wildly in surface, but every one of them:
|
|
105
|
+
*
|
|
106
|
+
* - declares its `product` id (routing key for the contract catalog,
|
|
107
|
+
* observability dimensions, and coverage accounting);
|
|
108
|
+
* - carries the core context it was built from (so callers can reach the
|
|
109
|
+
* shared transports, credentials and events without globals);
|
|
110
|
+
* - closes the product's own resources (websockets, listen-key timers).
|
|
111
|
+
*
|
|
112
|
+
* The raw API layer (`client.futures.usdm.trading.createOrder`) and the
|
|
113
|
+
* domain execution layer (`client.futures.usdm.execution.placeOrder`) both
|
|
114
|
+
* live inside product clients — API fidelity and trading infrastructure are
|
|
115
|
+
* siblings, not alternatives.
|
|
116
|
+
*/
|
|
117
|
+
interface ProductClient {
|
|
118
|
+
/** Canonical product id ('usdm', 'spot', …). */
|
|
119
|
+
readonly product: ProductId;
|
|
120
|
+
/** The shared runtime this product was constructed from. */
|
|
121
|
+
readonly core: CoreContext;
|
|
122
|
+
/**
|
|
123
|
+
* Close this product's resources (WS connections, keep-alive timers).
|
|
124
|
+
* Safe to call more than once.
|
|
125
|
+
*/
|
|
126
|
+
close(): void;
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Factory signature for lazy product construction on a multi-product
|
|
130
|
+
* facade: receives the shared context, returns the cached product surface.
|
|
131
|
+
*/
|
|
132
|
+
type ProductFactory<T> = (core: CoreContext) => T;
|
|
133
|
+
/** True when a value satisfies the v3 product boundary. */
|
|
134
|
+
declare function isProductClient(value: unknown): value is ProductClient;
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Paper execution backend: routes orders through the local
|
|
138
|
+
* {@link PaperTradingEngine} simulator instead of the exchange, behind the
|
|
139
|
+
* exact same {@link ExecutionAdapter} interface the live products use.
|
|
140
|
+
*
|
|
141
|
+
* The point is *identical semantics*: `placeOrder` returns the same
|
|
142
|
+
* `Execution` envelope, `reconcile()` runs the same matrix, fetches of
|
|
143
|
+
* unknown orders fail with the same `-2013` Binance error the exchange would
|
|
144
|
+
* return, and fills stream in as execution reports just like a user-data
|
|
145
|
+
* stream would deliver them. Code written against the paper backend can be
|
|
146
|
+
* switched to live by changing one configuration value.
|
|
147
|
+
*
|
|
148
|
+
* The simulator fills instantly (per its execution model), so:
|
|
149
|
+
* - `createOrder` resolves with the fill already booked;
|
|
150
|
+
* - `fetchOrder` replays the stored record (reconciliation succeeds);
|
|
151
|
+
* - `cancelOrder` mirrors the exchange's `-2011` for orders no longer on
|
|
152
|
+
* the book, which the ExecutionManager reconciles into a terminal
|
|
153
|
+
* CANCELED execution — the same path live takes.
|
|
154
|
+
*/
|
|
155
|
+
declare class PaperExecutionAdapter implements ExecutionAdapter {
|
|
156
|
+
private readonly engine;
|
|
157
|
+
private readonly options;
|
|
158
|
+
readonly product = "paper";
|
|
159
|
+
/** clientOrderId → raw order record, the simulator's "matching engine" view. */
|
|
160
|
+
private readonly orders;
|
|
161
|
+
/**
|
|
162
|
+
* Report listeners. Paper backends routinely have several consumers — the
|
|
163
|
+
* execution manager's ledger *and* (v3) the platform's paper session that
|
|
164
|
+
* translates fills into user-stream frames — so `onReport` fans out to
|
|
165
|
+
* every registered listener instead of replacing the previous one.
|
|
166
|
+
*/
|
|
167
|
+
private readonly reportHandlers;
|
|
168
|
+
constructor(engine: PaperTradingEngine, options?: {
|
|
169
|
+
commissionAsset?: string;
|
|
170
|
+
});
|
|
171
|
+
/** The underlying simulator (account, positions, order history). */
|
|
172
|
+
get simulator(): PaperTradingEngine;
|
|
173
|
+
createOrder(submission: CreateOrderParams): Promise<Record<string, unknown>>;
|
|
174
|
+
fetchOrder(symbol: string, key: OrderKey): Promise<Record<string, unknown>>;
|
|
175
|
+
/**
|
|
176
|
+
* Every order record the simulator holds, client-order-id keyed — the
|
|
177
|
+
* authoritative "REST view" a paper-mode reconciliation pass folds into the
|
|
178
|
+
* v3 platform's order tracker (live reconciliation reads `/openOrders`).
|
|
179
|
+
*/
|
|
180
|
+
listOrderRecords(): Record<string, unknown>[];
|
|
181
|
+
cancelOrder(symbol: string, key: OrderKey): Promise<Record<string, unknown>>;
|
|
182
|
+
toOrderShape(raw: Record<string, unknown>): OrderShape;
|
|
183
|
+
/** Paper has no user-data stream; attach is a no-op. */
|
|
184
|
+
setUserStream(_ws: UserStreamLike | null): void;
|
|
185
|
+
/** Register a report listener; every listener fires for every fill (additive). */
|
|
186
|
+
onReport(handler: (report: ExecutionReportShape) => void): void;
|
|
187
|
+
}
|
|
30
188
|
|
|
31
|
-
|
|
32
|
-
|
|
189
|
+
/**
|
|
190
|
+
* v3 execution platform — shared domain types.
|
|
191
|
+
*
|
|
192
|
+
* Milestone 3 turns execution from a per-call concern into a platform: the
|
|
193
|
+
* user-data stream becomes a managed session (listen-key lifecycle, keep-alive,
|
|
194
|
+
* rotation), order and position state become live feeds updated by that stream,
|
|
195
|
+
* and retry decisions become an explicit, classified contract instead of an
|
|
196
|
+
* implicit code path. These types are the vocabulary all three pieces share.
|
|
197
|
+
*/
|
|
198
|
+
/**
|
|
199
|
+
* Binance order lifecycle statuses — the union of REST and user-stream values
|
|
200
|
+
* across USDⓈ-M and Spot. The trailing `string` intersection keeps the union
|
|
201
|
+
* open: Binance adds statuses (e.g. `EXPIRED_IN_MATCH` for STP) without
|
|
202
|
+
* breaking compilation, while known values still autocomplete.
|
|
203
|
+
*/
|
|
204
|
+
type OrderStatus = 'NEW' | 'PARTIALLY_FILLED' | 'FILLED' | 'CANCELED' | 'PENDING_CANCEL' | 'REJECTED' | 'EXPIRED' | 'EXPIRED_IN_MATCH' | 'PARTIALLY_CANCELED' | 'LIQUIDATED' | (string & Record<never, never>);
|
|
205
|
+
/**
|
|
206
|
+
* True when the status is a terminal order state — no further execution
|
|
207
|
+
* reports or REST polls can change the outcome (late events for a terminal
|
|
208
|
+
* order are protocol noise; trackers stop mutating terminal records).
|
|
209
|
+
*/
|
|
210
|
+
declare function isTerminalOrderStatus(status: string): boolean;
|
|
211
|
+
/**
|
|
212
|
+
* Live view of one order, folded from every source that observes it:
|
|
213
|
+
* user-stream execution reports, REST order queries, and the execution
|
|
214
|
+
* manager's submissions. All prices and quantities are exact decimal strings
|
|
215
|
+
* (see `core/decimal`) — never binary floats.
|
|
216
|
+
*
|
|
217
|
+
* Unlike the {@link import('../types.js').Execution} ledger entry (which
|
|
218
|
+
* follows one *intent*), the OrderRecord follows one *order* — including
|
|
219
|
+
* orders placed outside this SDK (by hand, by another bot, by the UI): the
|
|
220
|
+
* user stream reports them all.
|
|
221
|
+
*/
|
|
222
|
+
interface OrderRecord {
|
|
223
|
+
/** Exchange reconciliation key (same field the execution manager derives). */
|
|
224
|
+
clientOrderId: string;
|
|
225
|
+
/** Exchange-assigned id, once any source has reported it. */
|
|
226
|
+
exchangeOrderId?: number;
|
|
33
227
|
symbol: string;
|
|
34
|
-
side:
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
228
|
+
side: string;
|
|
229
|
+
type: string;
|
|
230
|
+
status: OrderStatus;
|
|
231
|
+
/** Requested quantity when a source reported it, else null. */
|
|
232
|
+
originalQuantity: string | null;
|
|
233
|
+
/** Cumulative executed quantity — decimal string. */
|
|
234
|
+
executedQuantity: string;
|
|
235
|
+
/**
|
|
236
|
+
* Cumulative quote volume — decimal string. USDⓈ-M execution reports do not
|
|
237
|
+
* carry it; the fold computes `executedQuantity × averagePrice` as an
|
|
238
|
+
* estimate, exactly like the execution ledger does.
|
|
239
|
+
*/
|
|
240
|
+
cumulativeQuoteQuantity: string | null;
|
|
241
|
+
/** Volume-weighted average fill price — decimal string or null. */
|
|
242
|
+
averagePrice: string | null;
|
|
243
|
+
/** Individual fills, appended once per trade, deduplicated by trade id. */
|
|
244
|
+
fills: ExecutionFill[];
|
|
245
|
+
positionSide?: string;
|
|
246
|
+
reduceOnly?: boolean;
|
|
247
|
+
timeInForce?: string;
|
|
248
|
+
/** Epoch ms when this order was first observed by any source. */
|
|
249
|
+
firstSeenAt: number;
|
|
250
|
+
/** Epoch ms of the last applied update. */
|
|
251
|
+
updatedAt: number;
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* One fully-normalized order observation — the unit the tracker folds into
|
|
255
|
+
* {@link OrderRecord}. Produced from either a user-stream event or a REST
|
|
256
|
+
* order view by the platform normalizers.
|
|
257
|
+
*/
|
|
258
|
+
interface OrderUpdate {
|
|
259
|
+
clientOrderId: string;
|
|
260
|
+
exchangeOrderId?: number;
|
|
261
|
+
symbol: string;
|
|
262
|
+
side: string;
|
|
263
|
+
type: string;
|
|
264
|
+
status: string;
|
|
265
|
+
originalQuantity: string | null;
|
|
266
|
+
executedQuantity: string;
|
|
267
|
+
cumulativeQuoteQuantity: string | null;
|
|
268
|
+
averagePrice: string | null;
|
|
269
|
+
/** The trade this update carries, when it reports one (fill quantity > 0). */
|
|
270
|
+
lastFill: ExecutionFill | null;
|
|
271
|
+
positionSide?: string;
|
|
272
|
+
reduceOnly?: boolean;
|
|
273
|
+
timeInForce?: string;
|
|
274
|
+
/** Epoch ms of the observation (event transaction time or REST time). */
|
|
275
|
+
updateTime: number;
|
|
276
|
+
source: 'user-stream' | 'rest';
|
|
277
|
+
/** The raw event/response this was normalized from, for auditing. */
|
|
278
|
+
raw: Record<string, unknown>;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Live view of one position (USDⓈ-M), folded from `ACCOUNT_UPDATE` user-stream
|
|
282
|
+
* events. Keyed by `symbol` + `positionSide` — one-way mode reports `BOTH`,
|
|
283
|
+
* hedge mode reports `LONG` and `SHORT` separately.
|
|
284
|
+
*/
|
|
285
|
+
interface PositionRecord {
|
|
286
|
+
symbol: string;
|
|
287
|
+
/** 'BOTH' (one-way mode) | 'LONG' | 'SHORT' (hedge mode). */
|
|
288
|
+
positionSide: string;
|
|
289
|
+
/** Signed position amount — decimal string (negative for shorts). */
|
|
290
|
+
positionAmount: string;
|
|
291
|
+
/** Average entry price — decimal string. */
|
|
292
|
+
entryPrice: string;
|
|
293
|
+
/** Unrealized PnL as of the last event — decimal string (stream value; refresh via REST for precision). */
|
|
294
|
+
unrealizedPnl: string | null;
|
|
295
|
+
/** 'isolated' | 'cross' when reported, else null. */
|
|
296
|
+
marginType: string | null;
|
|
297
|
+
/** Isolated-position wallet balance when reported, else null. */
|
|
298
|
+
isolatedWallet: string | null;
|
|
299
|
+
/** Epoch ms of the last applied event. */
|
|
300
|
+
updatedAt: number;
|
|
42
301
|
}
|
|
43
|
-
|
|
44
|
-
|
|
302
|
+
/**
|
|
303
|
+
* One normalized position change — an `ACCOUNT_UPDATE` position entry
|
|
304
|
+
* (`a.P[i]`), decimal strings everywhere.
|
|
305
|
+
*/
|
|
306
|
+
interface PositionUpdate {
|
|
45
307
|
symbol: string;
|
|
46
|
-
|
|
47
|
-
|
|
308
|
+
positionSide: string;
|
|
309
|
+
positionAmount: string;
|
|
310
|
+
entryPrice: string;
|
|
311
|
+
unrealizedPnl: string | null;
|
|
312
|
+
marginType: string | null;
|
|
313
|
+
isolatedWallet: string | null;
|
|
314
|
+
updateTime: number;
|
|
315
|
+
/** The raw `a.P` entry this was normalized from. */
|
|
316
|
+
raw: Record<string, unknown>;
|
|
317
|
+
}
|
|
318
|
+
/** Consumer-visible lifecycle of a managed {@link UserStreamSession}. */
|
|
319
|
+
type UserSessionState = 'idle' | 'starting' | 'live' | 'reconnecting' | 'closed';
|
|
320
|
+
/**
|
|
321
|
+
* REST listen-key lifecycle the session drives. Product adapters implement
|
|
322
|
+
* this over the shared `HttpClient` (USDⓈ-M `/fapi/v1/listenKey`, Spot
|
|
323
|
+
* `/api/v3/userDataStream`) — swap it out for a captive/mock backend in tests.
|
|
324
|
+
*/
|
|
325
|
+
interface ListenKeyApi {
|
|
326
|
+
/** Create a fresh listen key. */
|
|
327
|
+
create(): Promise<string>;
|
|
328
|
+
/**
|
|
329
|
+
* Renew a listen key. Binance disconnects user streams after 60 minutes
|
|
330
|
+
* without renewal; the session calls this roughly every 30 minutes.
|
|
331
|
+
*/
|
|
332
|
+
keepAlive(listenKey: string): Promise<void>;
|
|
333
|
+
/** Delete a listen key (server-side stream teardown). Best-effort. */
|
|
334
|
+
close?(listenKey: string): Promise<void>;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Result of one {@link ExecutionPlatform.reconcile} pass: how many REST (or
|
|
338
|
+
* simulator) views were folded into the trackers. A *view count*, not a
|
|
339
|
+
* change count — folding a view does not always mutate a record (the stream
|
|
340
|
+
* may have reported the same state already).
|
|
341
|
+
*/
|
|
342
|
+
interface ReconciliationSummary {
|
|
343
|
+
/** Order views folded (open orders from REST, or the simulator's history). */
|
|
344
|
+
orders: number;
|
|
345
|
+
/** Position records folded (position-risk REST rows, or simulator positions). */
|
|
346
|
+
positions: number;
|
|
347
|
+
/** Epoch ms of the snapshot this pass folded. */
|
|
348
|
+
fetchedAt: number;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Execution platform defaults. Keep-alive runs at the documented 30-minute
|
|
352
|
+
* cadence; a key is rotated after 3 consecutive keep-alive failures (dead
|
|
353
|
+
* key) or 6 reconnect attempts without an OPEN (network partition or the
|
|
354
|
+
* key expired server-side mid-outage).
|
|
355
|
+
*/
|
|
356
|
+
declare const EXECUTION_PLATFORM_DEFAULTS: {
|
|
357
|
+
/** Listen-key renewal cadence (Binance kills keys after 60 min). */
|
|
358
|
+
readonly keepAliveIntervalMs: number;
|
|
359
|
+
/** Consecutive keep-alive failures before the key is rotated. */
|
|
360
|
+
readonly keepAliveFailuresBeforeRotation: 3;
|
|
361
|
+
/**
|
|
362
|
+
* Reconnect attempts (with backoff) before the key is rotated — the escape
|
|
363
|
+
* hatch for "server accepts nothing on this key anymore".
|
|
364
|
+
*/
|
|
365
|
+
readonly reconnectAttemptsBeforeRotation: 6;
|
|
366
|
+
/** Retained order records before the oldest are evicted. */
|
|
367
|
+
readonly maxTrackedOrders: 5000;
|
|
368
|
+
/** Default wait for an order to reach a terminal status. */
|
|
369
|
+
readonly terminalTimeoutMs: 30000;
|
|
370
|
+
};
|
|
371
|
+
/**
|
|
372
|
+
* What a retry of a failed order operation means, semantically:
|
|
373
|
+
*
|
|
374
|
+
* - `'safe'` — the failure proves no order side effect occurred; retrying is
|
|
375
|
+
* free of double-execution risk (order never reached the engine, request
|
|
376
|
+
* was throttled before processing, target of a cancel already gone).
|
|
377
|
+
* - `'idempotent'` — the target backend enforces the idempotency key, so a
|
|
378
|
+
* retry with the same key cannot double-execute. The live exchange never
|
|
379
|
+
* earns this label (Binance does not enforce `newClientOrderId`
|
|
380
|
+
* uniqueness); it is reserved for backends that do (e.g. a paper engine
|
|
381
|
+
* keyed by intent).
|
|
382
|
+
* - `'reconciliation-required'` — the outcome is unknown: the request may or
|
|
383
|
+
* may not have reached the engine. Retrying blind risks doubling the
|
|
384
|
+
* position; the caller must reconcile first (the execution manager's
|
|
385
|
+
* `reconcile()`, or `GET /fapi/v1/order` by clientOrderId).
|
|
386
|
+
* - `'never-retry'` — a definitive rejection or a programming error: the
|
|
387
|
+
* same request will fail the same way (filters, margin, permissions).
|
|
388
|
+
*/
|
|
389
|
+
type RetrySafety = 'safe' | 'idempotent' | 'reconciliation-required' | 'never-retry';
|
|
390
|
+
/** Result of classifying an error for retry semantics. */
|
|
391
|
+
interface RetryClassification {
|
|
392
|
+
safety: RetrySafety;
|
|
393
|
+
/** Human-readable justification, stable enough for dashboards. */
|
|
394
|
+
reason: string;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/** Options for {@link OrderTracker}. */
|
|
398
|
+
interface OrderTrackerOptions {
|
|
399
|
+
/** Observability bus; `execution.order.*` events are published to it. */
|
|
400
|
+
events?: EventBus;
|
|
401
|
+
/** Retained records before the oldest are evicted. Default 5000. */
|
|
402
|
+
maxRecords?: number;
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Live order-state feed.
|
|
406
|
+
*
|
|
407
|
+
* One {@link OrderRecord} per order, folded from every source that observes
|
|
408
|
+
* it: user-stream execution reports (all orders on the account — including
|
|
409
|
+
* orders placed outside this SDK), REST order views, and anything else a
|
|
410
|
+
* caller pushes through `applyUpdate`. Records hold exact decimal strings and
|
|
411
|
+
* per-trade fills deduplicated by trade id.
|
|
412
|
+
*
|
|
413
|
+
* ```ts
|
|
414
|
+
* const orders = platform.orders;
|
|
415
|
+
* orders.on('update', (record) => log(record.status));
|
|
416
|
+
*
|
|
417
|
+
* await platform.startUserSession(); // feed goes live
|
|
418
|
+
* const final = await orders.waitForTerminal('nbsdk-abc'); // promise form
|
|
419
|
+
* ```
|
|
420
|
+
*
|
|
421
|
+
* The tracker is a *view*, not a ledger: it keeps no intents and drives no
|
|
422
|
+
* requests — the execution manager owns submission/reconciliation, this owns
|
|
423
|
+
* "what does the account's order book look like right now".
|
|
424
|
+
*/
|
|
425
|
+
declare class OrderTracker extends EventEmitter {
|
|
426
|
+
private readonly records;
|
|
427
|
+
private readonly byExchangeId;
|
|
428
|
+
private readonly maxRecords;
|
|
429
|
+
private readonly events?;
|
|
430
|
+
constructor(options?: OrderTrackerOptions);
|
|
431
|
+
/**
|
|
432
|
+
* Dispatch one raw user-data frame. Recognized execution reports
|
|
433
|
+
* (`ORDER_TRADE_UPDATE`, spot `executionReport`) fold into records;
|
|
434
|
+
* everything else is ignored — the session feeds this its full event flow.
|
|
435
|
+
*/
|
|
436
|
+
applyUserEvent(event: unknown): void;
|
|
437
|
+
/** Fold a REST order view (create ack / query / cancel result). */
|
|
438
|
+
applyOrderShape(shape: OrderShape): void;
|
|
439
|
+
/** Fold one normalized observation and emit the updated record. */
|
|
440
|
+
applyUpdate(update: OrderUpdate): void;
|
|
441
|
+
/** Current record for a client order id (defensive copy). */
|
|
442
|
+
get(clientOrderId: string): OrderRecord | undefined;
|
|
443
|
+
/** Current record for an exchange order id (defensive copy). */
|
|
444
|
+
getByExchangeId(orderId: number): OrderRecord | undefined;
|
|
445
|
+
/** All tracked records (defensive copies), insertion order. */
|
|
446
|
+
all(): OrderRecord[];
|
|
447
|
+
/** Records that have not reached a terminal status. */
|
|
448
|
+
open(): OrderRecord[];
|
|
449
|
+
/**
|
|
450
|
+
* Resolve with the record once its status is terminal. Rejects if the order
|
|
451
|
+
* is unknown, or on timeout. Safe to call repeatedly for the same order.
|
|
452
|
+
*/
|
|
453
|
+
waitForTerminal(clientOrderId: string, timeoutMs?: number): Promise<OrderRecord>;
|
|
454
|
+
/** Number of tracked records (cheap). */
|
|
455
|
+
get size(): number;
|
|
456
|
+
private evictOverflow;
|
|
457
|
+
private evict;
|
|
458
|
+
private copy;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/** Options for {@link PositionTracker}. */
|
|
462
|
+
interface PositionTrackerOptions {
|
|
463
|
+
/** Observability bus; `execution.position.*` events are published to it. */
|
|
464
|
+
events?: EventBus;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Live position-state feed (USDⓈ-M).
|
|
468
|
+
*
|
|
469
|
+
* Folds `ACCOUNT_UPDATE` user-stream events into one {@link PositionRecord}
|
|
470
|
+
* per `symbol` + `positionSide` (one-way mode reports `BOTH`; hedge mode
|
|
471
|
+
* reports `LONG` and `SHORT`). Every amount is an exact decimal string — the
|
|
472
|
+
* signed position amount distinguishes direction, so a short is a negative
|
|
473
|
+
* amount, never a separate flag.
|
|
474
|
+
*
|
|
475
|
+
* ```ts
|
|
476
|
+
* const positions = platform.positions;
|
|
477
|
+
* positions.on('update', (p) => log(p.symbol, p.positionAmount));
|
|
478
|
+
* await platform.startUserSession(); // feed goes live
|
|
479
|
+
* positions.get('BTCUSDT'); // current one-way position
|
|
480
|
+
* positions.nonZero(); // exposure view
|
|
481
|
+
* ```
|
|
482
|
+
*
|
|
483
|
+
* The unrealized PnL is the stream's own mark at event time; strategies that
|
|
484
|
+
* need a current mark should combine it with the mark-price stream rather
|
|
485
|
+
* than polling this field.
|
|
486
|
+
*/
|
|
487
|
+
declare class PositionTracker extends EventEmitter {
|
|
488
|
+
private readonly positions;
|
|
489
|
+
private readonly events?;
|
|
490
|
+
constructor(options?: PositionTrackerOptions);
|
|
491
|
+
/**
|
|
492
|
+
* Dispatch one raw user-data frame. `ACCOUNT_UPDATE` position entries fold
|
|
493
|
+
* into records; everything else is ignored.
|
|
494
|
+
*/
|
|
495
|
+
applyUserEvent(event: unknown): void;
|
|
496
|
+
/** Fold one normalized position update and emit the updated record. */
|
|
497
|
+
applyUpdate(update: PositionUpdate): void;
|
|
498
|
+
/**
|
|
499
|
+
* Current position for a symbol. In one-way mode pass no side (or 'BOTH');
|
|
500
|
+
* in hedge mode pass 'LONG' or 'SHORT'. Defensive copy.
|
|
501
|
+
*/
|
|
502
|
+
get(symbol: string, positionSide?: string): PositionRecord | undefined;
|
|
503
|
+
/** All tracked positions (defensive copies), insertion order. */
|
|
504
|
+
all(): PositionRecord[];
|
|
505
|
+
/** Positions with a non-zero amount — the actual exposure view. */
|
|
506
|
+
nonZero(): PositionRecord[];
|
|
507
|
+
/** Number of tracked positions (cheap). */
|
|
508
|
+
get size(): number;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* v3 execution platform — REST reconciliation pass.
|
|
513
|
+
*
|
|
514
|
+
* The user stream sees the present and the future, never the past: an order
|
|
515
|
+
* placed before `startUserSession()` (by hand, by another bot, by a previous
|
|
516
|
+
* process of this SDK) never appears in it, and after a partition the stream
|
|
517
|
+
* resumes without replaying what was missed. `reconcile()` closes that gap
|
|
518
|
+
* the same way the execution manager does for single intents — by folding an
|
|
519
|
+
* authoritative REST snapshot into the trackers:
|
|
520
|
+
*
|
|
521
|
+
* - `GET /fapi/v1/openOrders` (USDⓈ-M, one call) / per-symbol Spot
|
|
522
|
+
* `GET /api/v3/openOrders` → `OrderTracker.applyOrderShape`
|
|
523
|
+
* - `GET /fapi/v2/positionRisk` (USDⓈ-M) → `PositionTracker.applyUpdate`
|
|
524
|
+
*
|
|
525
|
+
* Paper mode reconciles against the simulator's own book instead — the same
|
|
526
|
+
* fold, the same events, zero network. Every fold emits the identical
|
|
527
|
+
* `order.updated` / `position.updated` events a stream fold emits, so
|
|
528
|
+
* consumers cannot tell which path updated a record.
|
|
529
|
+
*/
|
|
530
|
+
/** Options for a reconciliation pass. */
|
|
531
|
+
interface ReconcileOptions {
|
|
532
|
+
/**
|
|
533
|
+
* Restrict the pass to these symbols. Required for live Spot (its
|
|
534
|
+
* `openOrders` route needs a symbol); optional for USDⓈ-M (one call covers
|
|
535
|
+
* the whole account, and the position fold filters to these symbols).
|
|
536
|
+
*/
|
|
537
|
+
symbols?: string[];
|
|
538
|
+
}
|
|
539
|
+
/** Everything a reconciliation pass needs, provided by the platform. */
|
|
540
|
+
interface ReconcileTarget {
|
|
541
|
+
product: 'usdm' | 'spot';
|
|
542
|
+
/** Shared transports — the same hosts, weight budgets and mocks as always. */
|
|
543
|
+
core: Pick<CoreContext, 'events' | 'http'>;
|
|
544
|
+
/** Paper wiring; presence switches the pass to the simulator's book. */
|
|
545
|
+
paper?: {
|
|
546
|
+
engine: PaperTradingEngine;
|
|
547
|
+
adapter: PaperExecutionAdapter;
|
|
548
|
+
};
|
|
549
|
+
orders: OrderTracker;
|
|
550
|
+
positions: PositionTracker;
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* Run one reconciliation pass — see the module doc. Returns how many views
|
|
554
|
+
* were folded; throws only on transport failure (the caller decides whether
|
|
555
|
+
* that is fatal — for a startup gap-fill it usually is not).
|
|
556
|
+
*/
|
|
557
|
+
declare function reconcileExecutionPlatform(target: ReconcileTarget, options?: ReconcileOptions): Promise<ReconciliationSummary>;
|
|
558
|
+
/**
|
|
559
|
+
* `GET /fapi/v1/openOrders` row (and the paper adapter's raw records, which
|
|
560
|
+
* mirror it) → decimal-string {@link OrderShape} for the tracker's REST fold.
|
|
561
|
+
*/
|
|
562
|
+
declare function orderShapeFromUsdmOpenOrder(raw: Record<string, unknown>): OrderShape;
|
|
563
|
+
/**
|
|
564
|
+
* `GET /fapi/v2/positionRisk` row → {@link PositionUpdate}. Binance reports
|
|
565
|
+
* one row per symbol × positionSide, including zero-size rows — folding a
|
|
566
|
+
* zero row is the correct "authoritative flat" semantics.
|
|
567
|
+
*/
|
|
568
|
+
declare function positionUpdatesFromPositionRisk(raw: Record<string, unknown>, fetchedAt: number): PositionUpdate[];
|
|
569
|
+
/** `GET /api/v3/openOrders` row → decimal-string {@link OrderShape}. */
|
|
570
|
+
declare function orderShapeFromSpotOpenOrder(raw: Record<string, unknown>): OrderShape;
|
|
571
|
+
|
|
572
|
+
/**
|
|
573
|
+
* Pure normalization for the execution platform: raw user-data frames and
|
|
574
|
+
* REST order views become decimal-string {@link OrderUpdate}s and
|
|
575
|
+
* {@link PositionUpdate}s, which the trackers fold. Product field-name
|
|
576
|
+
* differences (USDⓈ-M `ORDER_TRADE_UPDATE.o` vs Spot `executionReport`, spot
|
|
577
|
+
* `Z` cumulative quote vs none on futures) end here — nothing downstream
|
|
578
|
+
* knows which product an update came from.
|
|
579
|
+
*
|
|
580
|
+
* Normalization always reads the *raw frame* strings, never the
|
|
581
|
+
* schema-transformed numbers: Binance transmits exact decimals precisely
|
|
582
|
+
* because binary floats cannot represent them, so coercing through `Number`
|
|
583
|
+
* and back would corrupt values like `123.456` at exactly the fields money
|
|
584
|
+
* math depends on. Zod runs as a validation-only pass (see
|
|
585
|
+
* {@link createUserEventParser}); the raw string survives to `Decimal`.
|
|
586
|
+
*/
|
|
587
|
+
/** Parse one raw user-data frame — validates, then returns the frame untouched. */
|
|
588
|
+
type UserEventParser = (raw: unknown) => unknown;
|
|
589
|
+
/**
|
|
590
|
+
* Build the per-product frame parser for a user-data session. The typed Zod
|
|
591
|
+
* schemas run as a *validation* pass — a frame that parses is known-good
|
|
592
|
+
* Binance shape; the raw frame (decimal strings intact) is returned untouched
|
|
593
|
+
* so normalization keeps exact decimals. Frames with unknown-but-valid `e`
|
|
594
|
+
* types (new Binance events, e.g. `TRADE_LITE`) pass through unvalidated;
|
|
595
|
+
* shapeless frames throw and the session surfaces them as errors.
|
|
596
|
+
*/
|
|
597
|
+
declare function createUserEventParser(product: 'usdm' | 'spot'): UserEventParser;
|
|
598
|
+
|
|
599
|
+
/** Tuning for {@link UserStreamSession}. */
|
|
600
|
+
interface UserStreamSessionOptions {
|
|
601
|
+
/** Product label used in events and errors ('usdm' | 'spot' | …). */
|
|
602
|
+
product: string;
|
|
603
|
+
/** REST listen-key lifecycle the session drives. */
|
|
604
|
+
listenKeyApi: ListenKeyApi;
|
|
605
|
+
/** User-data stream base URL (the listen key is appended as the path). */
|
|
606
|
+
userStreamUrl: string;
|
|
607
|
+
/** Observability bus; `execution.session.*` events are published to it. */
|
|
608
|
+
events?: EventBus;
|
|
609
|
+
/** Connection label; defaults to `userSession:<product>`. */
|
|
610
|
+
name?: string;
|
|
611
|
+
/** Keep-alive cadence. Default 30 minutes. */
|
|
612
|
+
keepAliveIntervalMs?: number;
|
|
613
|
+
/** Consecutive keep-alive failures before key rotation. Default 3. */
|
|
614
|
+
keepAliveFailuresBeforeRotation?: number;
|
|
615
|
+
/** Reconnect attempts before key rotation. Default 6. */
|
|
616
|
+
reconnectAttemptsBeforeRotation?: number;
|
|
617
|
+
/**
|
|
618
|
+
* A connection that opens and dies within this window *without delivering
|
|
619
|
+
* a single frame* counts as a flap. Default 10s.
|
|
620
|
+
*/
|
|
621
|
+
flapWindowMs?: number;
|
|
622
|
+
/** Consecutive flaps before key rotation. Default 5. */
|
|
623
|
+
flapFailuresBeforeRotation?: number;
|
|
624
|
+
/** Frame parser; defaults to the product's (usdm → futures, else spot). */
|
|
625
|
+
parseEvent?: UserEventParser;
|
|
626
|
+
/** Injectable WebSocket factory for tests. */
|
|
627
|
+
socketFactory?: (url: string) => WebSocket;
|
|
628
|
+
/** Called once when the session is closed by the user (or `close()`). */
|
|
629
|
+
onClose?: () => void;
|
|
630
|
+
/** Connection-level passthrough (reconnect tuning, rotation window, …). */
|
|
631
|
+
connection?: Partial<Omit<WsConnectionOptions, 'buildUrl' | 'name' | 'events' | 'socketFactory'>>;
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Managed user-data stream session.
|
|
635
|
+
*
|
|
636
|
+
* v2 (and the v3 M1 product clients) left the listen-key dance to the caller:
|
|
637
|
+
* create the key, schedule the 30-minute keep-alive, notice when the key dies,
|
|
638
|
+
* restart. Every consumer reimplemented it, and the failure modes (key expiry
|
|
639
|
+
* mid-run, keep-alive failing silently, zombie sockets) were exactly the kind
|
|
640
|
+
* of thing that loses fills.
|
|
641
|
+
*
|
|
642
|
+
* The session owns the whole lifecycle over a {@link WsConnection}:
|
|
643
|
+
*
|
|
644
|
+
* - `start()` creates the key, connects, and schedules keep-alive;
|
|
645
|
+
* - keep-alive failures are counted — after 3 in a row the key is rotated
|
|
646
|
+
* (a fresh key is created and the connection swapped onto it);
|
|
647
|
+
* - a connection that cannot re-establish after 6 backoff attempts gets its
|
|
648
|
+
* key rotated too (the escape hatch for "the key died while we were
|
|
649
|
+
* partitioned");
|
|
650
|
+
* - the 24-hour server-side kill is preempted by the connection's own 23-hour
|
|
651
|
+
* rotation (same listen key, per Binance's renewal semantics);
|
|
652
|
+
* - `close()` stops everything and deletes the key server-side, best-effort.
|
|
653
|
+
*
|
|
654
|
+
* The session is also a drop-in user stream for the execution manager: it
|
|
655
|
+
* emits `userData` with each parsed (validated, raw-decimal-preserving) frame,
|
|
656
|
+
* so `executionManager.setUserStream(session)` immediately routes live
|
|
657
|
+
* execution reports into the intent ledger.
|
|
658
|
+
*
|
|
659
|
+
* ```ts
|
|
660
|
+
* const session = new UserStreamSession({
|
|
661
|
+
* product: 'usdm',
|
|
662
|
+
* listenKeyApi,
|
|
663
|
+
* userStreamUrl: core.endpoints.wsUser,
|
|
664
|
+
* events: core.events,
|
|
665
|
+
* });
|
|
666
|
+
* const key = await session.start();
|
|
667
|
+
* session.on('userData', (event) => { /* e: ORDER_TRADE_UPDATE … *\/ });
|
|
668
|
+
* session.close();
|
|
669
|
+
* ```
|
|
670
|
+
*/
|
|
671
|
+
declare class UserStreamSession extends WsConnection {
|
|
672
|
+
private readonly api;
|
|
673
|
+
private readonly userStreamUrl;
|
|
674
|
+
private readonly parse;
|
|
675
|
+
private readonly sessionProduct;
|
|
676
|
+
private readonly keepAliveIntervalMs;
|
|
677
|
+
private readonly keepAliveFailuresBeforeRotation;
|
|
678
|
+
private readonly reconnectAttemptsBeforeRotation;
|
|
679
|
+
private readonly flapWindowMs;
|
|
680
|
+
private readonly flapFailuresBeforeRotation;
|
|
681
|
+
private readonly sessionEvents?;
|
|
682
|
+
private listenKeyValue;
|
|
683
|
+
private keepAliveTimer;
|
|
684
|
+
private keepAliveFailures;
|
|
685
|
+
private rotating;
|
|
686
|
+
private sessionStateValue;
|
|
687
|
+
private startPromise;
|
|
688
|
+
private readonly onClose?;
|
|
689
|
+
/** Epoch ms of the last 'open'; flap detection. */
|
|
690
|
+
private openedAtMs;
|
|
691
|
+
/** Inbound frames since the last 'open'; flap detection. */
|
|
692
|
+
private framesSinceOpen;
|
|
693
|
+
/** Consecutive open-then-die-without-frames cycles. */
|
|
694
|
+
private flapFailures;
|
|
695
|
+
constructor(options: UserStreamSessionOptions);
|
|
696
|
+
/** Product this session serves ('usdm' | 'spot' | …). */
|
|
697
|
+
get product(): string;
|
|
698
|
+
/** Consumer-visible session state. */
|
|
699
|
+
get sessionState(): UserSessionState;
|
|
700
|
+
/** The live listen key, or null before start / after close. */
|
|
701
|
+
get listenKey(): string | null;
|
|
702
|
+
/**
|
|
703
|
+
* Start the session: create a listen key, connect, schedule keep-alive.
|
|
704
|
+
* Idempotent — concurrent or repeated calls share one start; returns the
|
|
705
|
+
* listen key.
|
|
706
|
+
*/
|
|
707
|
+
start(): Promise<string>;
|
|
708
|
+
/**
|
|
709
|
+
* Close the session: stop keep-alive, tear the connection down, delete the
|
|
710
|
+
* listen key server-side (best-effort). Safe to call repeatedly and from
|
|
711
|
+
* any state.
|
|
712
|
+
*/
|
|
713
|
+
close(): void;
|
|
714
|
+
protected handleRawMessage(text: string): void;
|
|
715
|
+
private scheduleNextKeepAlive;
|
|
716
|
+
private keepAliveTick;
|
|
717
|
+
/**
|
|
718
|
+
* Rotate onto a fresh listen key: create it, swap `listenKeyValue`, and
|
|
719
|
+
* force the connection to re-establish on the new key. Guarded so overlapping
|
|
720
|
+
* triggers (keep-alive failure + reconnect storm) rotate once.
|
|
721
|
+
*/
|
|
722
|
+
private rotateListenKey;
|
|
723
|
+
private setSessionState;
|
|
724
|
+
/** Narrowing-safe terminal check (crosses `await` boundaries). */
|
|
725
|
+
private isClosed;
|
|
726
|
+
private emitSessionEvent;
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* v3 paper backend — the simulator behind the execution-platform boundary.
|
|
731
|
+
*
|
|
732
|
+
* Milestone 3's execution platform gave live trading a managed session and
|
|
733
|
+
* order/position feeds. Milestone 4 puts the **paper simulator behind the
|
|
734
|
+
* same boundary**: a {@link PaperSession} emits the exact user-data frames a
|
|
735
|
+
* live session would (`ORDER_TRADE_UPDATE` for fills, `ACCOUNT_UPDATE` for
|
|
736
|
+
* the position the fill produced), so the platform's trackers, the
|
|
737
|
+
* `waitForTerminal()` promise API and the reconciliation semantics behave
|
|
738
|
+
* *identically* whether orders route to the exchange or to the simulator.
|
|
739
|
+
* Strategy code written against the paper platform runs unchanged live.
|
|
740
|
+
*/
|
|
741
|
+
/** Options for {@link PaperSession}. */
|
|
742
|
+
interface PaperSessionOptions {
|
|
743
|
+
/** The simulator whose fills become user-data frames. */
|
|
744
|
+
engine: PaperTradingEngine;
|
|
745
|
+
/** Report source — the adapter orders flow through (one per engine). */
|
|
746
|
+
adapter: PaperExecutionAdapter;
|
|
747
|
+
/** Observability bus; `execution.session.*` events mirror the live session. */
|
|
748
|
+
events?: EventBus;
|
|
749
|
+
/** Called once when the session is closed by the user (or `close()`). */
|
|
750
|
+
onClose?: () => void;
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* A user-data stream session with no network: simulator fills, replayed as
|
|
754
|
+
* exchange-shaped user-data frames.
|
|
755
|
+
*
|
|
756
|
+
* The contract is the one the platform (and the execution manager) already
|
|
757
|
+
* consume: `start()` goes live, `close()` is terminal, frames arrive on
|
|
758
|
+
* `userData`. There is no listen key, no keep-alive, no rotation — nothing to
|
|
759
|
+
* rotate, because the "server" is an in-process {@link PaperTradingEngine}.
|
|
760
|
+
* Fill → frame translation is synchronous: by the time `placeOrder()` resolves,
|
|
761
|
+
* the trackers have already folded the fill.
|
|
762
|
+
*
|
|
763
|
+
* ```ts
|
|
764
|
+
* const session = new PaperSession({ engine, adapter });
|
|
765
|
+
* await session.start();
|
|
766
|
+
* session.on('userData', (event) => {
|
|
767
|
+
* // { e: 'ORDER_TRADE_UPDATE', … } / { e: 'ACCOUNT_UPDATE', … }
|
|
768
|
+
* });
|
|
769
|
+
* ```
|
|
770
|
+
*/
|
|
771
|
+
declare class PaperSession extends EventEmitter implements UserStreamLike {
|
|
772
|
+
private readonly engine;
|
|
773
|
+
private readonly adapter;
|
|
774
|
+
private readonly sessionEvents?;
|
|
775
|
+
private readonly onClose?;
|
|
776
|
+
private sessionStateValue;
|
|
777
|
+
private readonly reportListener;
|
|
778
|
+
constructor(options: PaperSessionOptions);
|
|
779
|
+
/** Consumer-visible lifecycle: `idle` → `live` → `closed`. */
|
|
780
|
+
get sessionState(): UserSessionState;
|
|
781
|
+
/** Alias for {@link sessionState} — reads naturally next to book/tracker states. */
|
|
782
|
+
get state(): UserSessionState;
|
|
783
|
+
/**
|
|
784
|
+
* The listen key a live session would carry — always null here: the
|
|
785
|
+
* simulator *is* the server, there is nothing to register.
|
|
786
|
+
*/
|
|
787
|
+
get listenKey(): string | null;
|
|
788
|
+
/** True while simulator fills flow into `userData`. */
|
|
789
|
+
get isLive(): boolean;
|
|
790
|
+
/**
|
|
791
|
+
* Session-contract member: resolve once frames are being delivered. For a
|
|
792
|
+
* live paper session that is immediate — the simulator delivers
|
|
793
|
+
* synchronously, there is no socket handshake to wait for.
|
|
794
|
+
*/
|
|
795
|
+
waitForOpen(_timeoutMs?: number): Promise<void>;
|
|
796
|
+
/**
|
|
797
|
+
* Start the session: attach the fill listener. Idempotent; no I/O — the
|
|
798
|
+
* listen-key create/connect/keep-alive dance has no paper equivalent.
|
|
799
|
+
*/
|
|
800
|
+
start(): Promise<PaperSession>;
|
|
801
|
+
/** Close the session (detach the fill listener). Terminal; safe repeatedly. */
|
|
802
|
+
close(): void;
|
|
803
|
+
/** Translate one simulator fill into the frames a live session would send. */
|
|
804
|
+
private onFill;
|
|
805
|
+
}
|
|
806
|
+
/**
|
|
807
|
+
* Simulator fill → USDⓈ-M `ORDER_TRADE_UPDATE` frame. Field-for-field the
|
|
808
|
+
* live shape (`o.c`, `o.X`, `o.z`, `o.ap`, …), decimal strings everywhere —
|
|
809
|
+
* `OrderTracker.applyUserEvent` cannot tell it from a real fill.
|
|
810
|
+
*/
|
|
811
|
+
declare function paperOrderTradeUpdateFrame(report: ExecutionReportShape, atMs?: number): Record<string, unknown>;
|
|
812
|
+
/**
|
|
813
|
+
* Simulator position → USDⓈ-M `ACCOUNT_UPDATE` frame. One-way mode semantics
|
|
814
|
+
* (`ps: 'BOTH'`) with a *signed* amount, the way Binance reports it: a short
|
|
815
|
+
* is a negative `pa`, never a separate flag.
|
|
816
|
+
*/
|
|
817
|
+
declare function paperAccountUpdateFrame(symbol: string, position: {
|
|
818
|
+
side: string;
|
|
48
819
|
quantity: number;
|
|
49
|
-
|
|
50
|
-
status: 'FILLED';
|
|
51
|
-
filledQuantity: number;
|
|
52
|
-
avgFillPrice: number;
|
|
53
|
-
/** Realized PnL booked by this order, if it reduced or closed a position. */
|
|
54
|
-
realizedPnl: number;
|
|
55
|
-
createdAt: number;
|
|
56
|
-
}
|
|
57
|
-
interface PaperAccount {
|
|
58
|
-
/** Wallet balance: starts at initialBalance and moves only with realized PnL. */
|
|
59
|
-
balance: number;
|
|
60
|
-
/** Wallet balance minus margin currently locked in open positions. */
|
|
61
|
-
availableBalance: number;
|
|
62
|
-
/** balance + unrealized PnL. */
|
|
63
|
-
totalWalletBalance: number;
|
|
64
|
-
realizedPnl: number;
|
|
820
|
+
entryPrice: number;
|
|
65
821
|
unrealizedPnl: number;
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
*/
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
*
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
822
|
+
} | undefined, atMs?: number): Record<string, unknown>;
|
|
823
|
+
/**
|
|
824
|
+
* Format a simulator float as a clean decimal string. The simulator's
|
|
825
|
+
* internal arithmetic is IEEE-754 (by design — it is a model, not a ledger),
|
|
826
|
+
* so raw `String(number)` would leak representation noise (`0.1 + 0.2` →
|
|
827
|
+
* `0.30000000000000004`). Twelve significant digits strips that noise while
|
|
828
|
+
* staying far finer than any Binance tick; the boundary stays decimal-exact
|
|
829
|
+
* from here on (trackers, `Decimal`, ledger math).
|
|
830
|
+
*/
|
|
831
|
+
declare function decimalString(value: number): string;
|
|
832
|
+
|
|
833
|
+
/** Session tuning accepted by {@link ExecutionPlatform.startUserSession}. */
|
|
834
|
+
type UserSessionTuning = Pick<UserStreamSessionOptions, 'keepAliveIntervalMs' | 'keepAliveFailuresBeforeRotation' | 'reconnectAttemptsBeforeRotation' | 'connection' | 'socketFactory'>;
|
|
835
|
+
/** Paper backend wiring: which simulator sits in the "server" seat. */
|
|
836
|
+
interface PaperBackendOptions {
|
|
837
|
+
/** The simulator whose fills the session replays as user-data frames. */
|
|
838
|
+
engine: PaperTradingEngine;
|
|
839
|
+
/** Report source — the adapter orders flow through. */
|
|
840
|
+
adapter: PaperExecutionAdapter;
|
|
841
|
+
}
|
|
842
|
+
/** Either session flavor a platform can hold: live (listen key) or paper. */
|
|
843
|
+
type PlatformUserSession = UserStreamSession | PaperSession;
|
|
844
|
+
/** Options for {@link ExecutionPlatform}. */
|
|
845
|
+
interface ExecutionPlatformOptions {
|
|
846
|
+
/** Product the platform serves — decides listen-key REST routes and WS URL. */
|
|
847
|
+
product: 'usdm' | 'spot';
|
|
848
|
+
/**
|
|
849
|
+
* Shared runtime: REST hosts (listen-key calls), resolved endpoints (user
|
|
850
|
+
* WS URL) and the observability bus. Same context every product client
|
|
851
|
+
* holds — one weight budget, one event stream.
|
|
852
|
+
*/
|
|
853
|
+
core: Pick<CoreContext, 'endpoints' | 'events' | 'http'>;
|
|
854
|
+
/**
|
|
855
|
+
* Execution manager to auto-attach when a session starts: its intent ledger
|
|
856
|
+
* then tracks live fills from the stream (`setUserStream`), and detaches
|
|
857
|
+
* when the session closes.
|
|
858
|
+
*/
|
|
859
|
+
executionManager?: ExecutionManager;
|
|
860
|
+
/** Order-record retention; default 5000. */
|
|
861
|
+
maxTrackedOrders?: number;
|
|
862
|
+
/**
|
|
863
|
+
* Paper backend wiring. When set, the platform runs in paper mode:
|
|
864
|
+
* `startUserSession()` opens a {@link PaperSession} (no listen key, no
|
|
865
|
+
* socket) that replays simulator fills as user-data frames, and
|
|
866
|
+
* `reconcile()` folds the simulator's own state. See
|
|
867
|
+
* {@link createPaperExecutionPlatform} for the assembled runtime.
|
|
868
|
+
*/
|
|
869
|
+
paper?: PaperBackendOptions;
|
|
870
|
+
}
|
|
871
|
+
/**
|
|
872
|
+
* v3 execution platform — one product's live trading state.
|
|
873
|
+
*
|
|
874
|
+
* Milestone 3 composition root: a managed user-data stream session, the
|
|
875
|
+
* order/position feeds it drives, and the semantic retry classifier, all over
|
|
876
|
+
* a shared {@link CoreContext}. The raw API layer (`trading.createOrder`) and
|
|
877
|
+
* the intent layer (`execution.placeOrder`) stay where they were; the platform
|
|
878
|
+
* adds the *account view* — what is live right now, fed by the exchange
|
|
879
|
+
* itself:
|
|
880
|
+
*
|
|
881
|
+
* ```ts
|
|
882
|
+
* const usdm = new USDMClient(core);
|
|
883
|
+
* await usdm.executionPlatform.startUserSession();
|
|
884
|
+
*
|
|
885
|
+
* await usdm.execution.placeOrder({ symbol: 'BTCUSDT', side: 'BUY', … });
|
|
886
|
+
* // execution ledger now updates from the stream, and:
|
|
887
|
+
* usdm.executionPlatform.orders.get('nbsdk-…'); // live OrderRecord
|
|
888
|
+
* usdm.executionPlatform.positions.get('BTCUSDT'); // live PositionRecord
|
|
889
|
+
* usdm.executionPlatform.classify(err).safety; // 'reconciliation-required'
|
|
890
|
+
* ```
|
|
891
|
+
*
|
|
892
|
+
* Milestone 4 adds two capabilities on the same boundary:
|
|
893
|
+
* - **`reconcile()`** — a REST snapshot (open orders + position risk) folded
|
|
894
|
+
* into the trackers: the startup gap-fill for orders that existed before
|
|
895
|
+
* the stream went live, and the recovery pass after a disconnect.
|
|
896
|
+
* - **paper mode** — with the `paper` option (see
|
|
897
|
+
* {@link createPaperExecutionPlatform}) the session, trackers and
|
|
898
|
+
* reconcile semantics run against the local simulator instead.
|
|
899
|
+
*
|
|
900
|
+
* Standalone use (any product wiring, custom listen-key backends for tests):
|
|
901
|
+
*
|
|
902
|
+
* ```ts
|
|
903
|
+
* const platform = new ExecutionPlatform({ product: 'usdm', core });
|
|
904
|
+
* ```
|
|
905
|
+
*/
|
|
906
|
+
declare class ExecutionPlatform {
|
|
907
|
+
readonly product: 'usdm' | 'spot';
|
|
908
|
+
readonly orders: OrderTracker;
|
|
909
|
+
readonly positions: PositionTracker;
|
|
910
|
+
private readonly core;
|
|
911
|
+
private readonly executionManager?;
|
|
912
|
+
private readonly paperBackend?;
|
|
913
|
+
private sessionValue;
|
|
914
|
+
constructor(options: ExecutionPlatformOptions);
|
|
915
|
+
/** The live user-data stream session, or null before `startUserSession`. */
|
|
916
|
+
get userSession(): PlatformUserSession | null;
|
|
917
|
+
/**
|
|
918
|
+
* The live session when it is a listen-key session (REST-wired products).
|
|
919
|
+
* Null in paper mode — the paper session carries no listen key.
|
|
920
|
+
*/
|
|
921
|
+
get liveUserSession(): UserStreamSession | null;
|
|
922
|
+
/** True when the platform runs against the paper simulator. */
|
|
923
|
+
get isPaper(): boolean;
|
|
924
|
+
/** True once a session has been started (cheap, no I/O). */
|
|
925
|
+
hasUserSession(): boolean;
|
|
926
|
+
/**
|
|
927
|
+
* Start the managed user-data stream: create the listen key, connect, run
|
|
928
|
+
* the 30-minute keep-alive, rotate on failure. The platform's order and
|
|
929
|
+
* position feeds go live immediately, and the attached execution manager
|
|
930
|
+
* (when one was provided) begins tracking live fills.
|
|
931
|
+
*
|
|
932
|
+
* In paper mode there is nothing to connect: the returned
|
|
933
|
+
* {@link PaperSession} replays simulator fills as user-data frames through
|
|
934
|
+
* the exact same `userData` contract — the trackers go live identically.
|
|
935
|
+
*/
|
|
936
|
+
startUserSession(tuning?: UserSessionTuning): Promise<PlatformUserSession>;
|
|
937
|
+
/** Classify an error for retry semantics — see {@link classifyRetrySafety}. */
|
|
938
|
+
classify(error: unknown): RetryClassification;
|
|
939
|
+
/**
|
|
940
|
+
* Fold a REST snapshot of the account into the trackers — the gap-fill the
|
|
941
|
+
* user stream cannot provide: orders that existed *before* the session went
|
|
942
|
+
* live, and the authoritative position state after any disconnect.
|
|
943
|
+
*
|
|
944
|
+
* Live mode pulls `GET /fapi/v1/openOrders` (or per-symbol Spot
|
|
945
|
+
* `openOrders`) and `GET /fapi/v2/positionRisk` over the shared core
|
|
946
|
+
* transports; paper mode folds the simulator's own book. Returns how many
|
|
947
|
+
* views were folded; every fold emits the same `order.updated` /
|
|
948
|
+
* `position.updated` events stream folds do.
|
|
949
|
+
*
|
|
950
|
+
* ```ts
|
|
951
|
+
* await platform.startUserSession();
|
|
952
|
+
* const summary = await platform.reconcile(); // startup gap-fill
|
|
953
|
+
* summary.orders; summary.positions;
|
|
954
|
+
* ```
|
|
955
|
+
*/
|
|
956
|
+
reconcile(options?: ReconcileOptions): Promise<ReconciliationSummary>;
|
|
957
|
+
/** Close the session (stop keep-alive, disconnect, delete the key). Trackers keep their records. */
|
|
958
|
+
close(): void;
|
|
959
|
+
/** Shared wiring for both session flavors: feed the trackers, remember it. */
|
|
960
|
+
private attachSession;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
/**
|
|
964
|
+
* v3 state platform — managed local book.
|
|
965
|
+
*
|
|
966
|
+
* The pure {@link OrderBook} owns the L2 algorithm (snapshot + diff + gap
|
|
967
|
+
* detection, exact-decimal levels). {@link ManagedBook} owns the *lifecycle*
|
|
968
|
+
* around it: the canonical Binance sync dance (buffer diffs while the REST
|
|
969
|
+
* snapshot is in flight, apply the tail that overlaps the snapshot), a
|
|
970
|
+
* consumer-visible sync state, events, and the `waitForSync()` promise —
|
|
971
|
+
* the same ergonomics the execution platform's trackers have.
|
|
972
|
+
*/
|
|
973
|
+
/** Consumer-visible lifecycle of one managed book. */
|
|
974
|
+
type ManagedBookState = 'syncing' | 'live' | 'desynced' | 'closed';
|
|
975
|
+
/** Event payload for `update` — a cheap top-of-book summary. */
|
|
976
|
+
interface BookUpdateEvent {
|
|
977
|
+
symbol: string;
|
|
978
|
+
lastUpdateId: number | null;
|
|
979
|
+
bestBid: string | null;
|
|
980
|
+
bestAsk: string | null;
|
|
981
|
+
}
|
|
982
|
+
/** Event payload for `desync` / `resynced` / `synced`. */
|
|
983
|
+
interface BookSyncEvent {
|
|
984
|
+
symbol: string;
|
|
985
|
+
/** Last applied update id (null before the first snapshot). */
|
|
986
|
+
lastUpdateId: number | null;
|
|
987
|
+
/** Desync reason, when known (`sequence-gap` | `interrupted` | `snapshot-stream-race`). */
|
|
988
|
+
reason?: string;
|
|
989
|
+
/** Diffs applied straight from the pre-snapshot buffer, when > 0. */
|
|
990
|
+
buffered?: number;
|
|
991
|
+
}
|
|
992
|
+
/** Options for {@link ManagedBook}. */
|
|
993
|
+
interface ManagedBookOptions {
|
|
994
|
+
/** Diffs buffered while the snapshot is in flight before the oldest drop. */
|
|
995
|
+
maxBufferedDiffs?: number;
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* One symbol's live book, managed end to end.
|
|
999
|
+
*
|
|
1000
|
+
* ```ts
|
|
1001
|
+
* const book = await engine.watch('BTCUSDT'); // resolves once synced
|
|
1002
|
+
* book.state; // 'live'
|
|
1003
|
+
* book.bestBid; // { price: '42150.10', quantity: '2.5' } — exact
|
|
1004
|
+
* book.metrics(); // spread, microprice, imbalance, depth…
|
|
1005
|
+
* book.on('update', (e) => log(e.bestBid));
|
|
1006
|
+
* await book.waitForSync(); // after a desync, resolves on recovery
|
|
1007
|
+
* ```
|
|
1008
|
+
*
|
|
1009
|
+
* The book is a *view* over the platform's pooled WS subscription and the
|
|
1010
|
+
* REST snapshot route — it never opens a socket or issues a request itself
|
|
1011
|
+
* (the {@link BookEngine} drives it).
|
|
1012
|
+
*/
|
|
1013
|
+
declare class ManagedBook extends EventEmitter {
|
|
1014
|
+
readonly symbol: string;
|
|
1015
|
+
private readonly book;
|
|
1016
|
+
private stateValue;
|
|
1017
|
+
/** Diffs received before the first snapshot, applied (filtered) after it. */
|
|
1018
|
+
private readonly diffBuffer;
|
|
1019
|
+
private readonly maxBufferedDiffs;
|
|
1020
|
+
constructor(symbol: string, options?: ManagedBookOptions);
|
|
1021
|
+
/** Consumer-visible sync state. */
|
|
1022
|
+
get state(): ManagedBookState;
|
|
1023
|
+
/** True while the local book mirrors the exchange book. */
|
|
1024
|
+
get isSynced(): boolean;
|
|
1025
|
+
/** Best bid level (exact decimal strings) or null when the bid side is empty. */
|
|
1026
|
+
get bestBid(): BookLevel | null;
|
|
1027
|
+
/** Best ask level (exact decimal strings) or null when the ask side is empty. */
|
|
1028
|
+
get bestAsk(): BookLevel | null;
|
|
1029
|
+
/** Mid price (exact decimal string) or null when one-sided. */
|
|
1030
|
+
get midPrice(): string | null;
|
|
1031
|
+
/** Top-of-book metrics (spread, microprice, imbalance, depth, …). */
|
|
1032
|
+
metrics(depthPct?: number): OrderBookMetrics;
|
|
1033
|
+
/** Bid levels (best first), for display/snapshotting. */
|
|
1034
|
+
bids(limit: number): BookLevel[];
|
|
1035
|
+
/** Ask levels (best first), for display/snapshotting. */
|
|
1036
|
+
asks(limit: number): BookLevel[];
|
|
1037
|
+
/** Last applied update id (null before the first snapshot). */
|
|
1038
|
+
get lastUpdateId(): number | null;
|
|
1039
|
+
/**
|
|
1040
|
+
* Resolve once the book is synced. Rejects on timeout or terminal close.
|
|
1041
|
+
* Safe to call repeatedly; also the recovery signal after a desync.
|
|
1042
|
+
*/
|
|
1043
|
+
waitForSync(timeoutMs?: number): Promise<void>;
|
|
1044
|
+
/** Terminal close — the engine calls this on unwatch/engine close. */
|
|
1045
|
+
close(): void;
|
|
1046
|
+
/**
|
|
1047
|
+
* Feed one depth diff. Before the first snapshot the diff buffers (the
|
|
1048
|
+
* canonical Binance sync algorithm); after it, a failed apply means a
|
|
1049
|
+
* sequence gap — the book desyncs and the engine must resnapshot.
|
|
1050
|
+
* @internal
|
|
1051
|
+
*/
|
|
1052
|
+
receiveDiff(diff: DiffInput): void;
|
|
1053
|
+
/**
|
|
1054
|
+
* Apply a REST snapshot, then the buffered diff tail that overlaps it.
|
|
1055
|
+
* This is the only path from `syncing`/`desynced` back to `live`.
|
|
1056
|
+
* @internal
|
|
1057
|
+
*/
|
|
1058
|
+
applySnapshot(snapshot: BookSnapshotInput): void;
|
|
1059
|
+
/**
|
|
1060
|
+
* Mark the book desynced without a gap event — the engine knows the stream
|
|
1061
|
+
* was interrupted (a reconnect implies missed diffs by construction).
|
|
1062
|
+
* @internal
|
|
1063
|
+
*/
|
|
1064
|
+
markInterrupted(reason?: string): void;
|
|
1065
|
+
private applyLiveDiff;
|
|
1066
|
+
private emitUpdate;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* v3 state platform — multi-symbol local book manager.
|
|
1071
|
+
*
|
|
1072
|
+
* The v2 `OrderBookEngine` wired books to a connection-centric WS class and
|
|
1073
|
+
* re-ran the snapshot after subscribing. The v3 engine keeps the pure L2
|
|
1074
|
+
* algorithm (it lives in `OrderBook`, unchanged) and rebuilds everything
|
|
1075
|
+
* around it on the platform surfaces:
|
|
1076
|
+
*
|
|
1077
|
+
* - streams come from the **pooled WS platform** (`core.ws.usdm/spot`) —
|
|
1078
|
+
* connection placement, renewal and reconnects are the platform's problem;
|
|
1079
|
+
* - snapshots come from the **shared REST transports** (`core.http(...)`) —
|
|
1080
|
+
* same weight budget, same mocks, same environment as everything else;
|
|
1081
|
+
* - the sync algorithm is the canonical Binance one: diffs buffer while the
|
|
1082
|
+
* first snapshot is in flight, and the tail that overlaps the snapshot
|
|
1083
|
+
* applies on top of it — no blind window, no resubscribe dance;
|
|
1084
|
+
* - an interrupted subscription marks the book desynced *immediately* (the
|
|
1085
|
+
* missed-diffs fact is known before any frame says so) and a resync with
|
|
1086
|
+
* backoff repairs it.
|
|
1087
|
+
*
|
|
1088
|
+
* ```ts
|
|
1089
|
+
* const engine = new BookEngine({ core, product: 'usdm' });
|
|
1090
|
+
* const book = await engine.watch('BTCUSDT'); // resolves once synced
|
|
1091
|
+
* book.bestBid; // exact decimal strings
|
|
1092
|
+
* await engine.unwatch('BTCUSDT');
|
|
1093
|
+
* engine.close();
|
|
1094
|
+
* ```
|
|
1095
|
+
*/
|
|
1096
|
+
/** Options for {@link BookEngine}. */
|
|
1097
|
+
interface BookEngineOptions {
|
|
1098
|
+
product: 'usdm' | 'spot';
|
|
1099
|
+
/** Shared runtime: pooled WS subscriptions + REST snapshot transports. */
|
|
1100
|
+
core: Pick<CoreContext, 'http' | 'events' | 'ws'>;
|
|
1101
|
+
/** Diff update speed; default `100ms`. */
|
|
1102
|
+
updateSpeed?: '100ms' | '500ms';
|
|
1103
|
+
/** REST snapshot depth (levels); default 1000. */
|
|
1104
|
+
snapshotLimit?: number;
|
|
1105
|
+
/** Backoff between resync attempts after a desync. Default 1000ms. */
|
|
1106
|
+
resyncDelayMs?: number;
|
|
1107
|
+
/** Per-book tuning (diff buffer size). */
|
|
1108
|
+
book?: ManagedBookOptions;
|
|
1109
|
+
}
|
|
1110
|
+
/**
|
|
1111
|
+
* Manages {@link ManagedBook}s across symbols on one product's stream family.
|
|
1112
|
+
*
|
|
1113
|
+
* Books are per-engine and per-symbol (a second `watch` of a live symbol
|
|
1114
|
+
* returns the existing book). The engine holds no sockets of its own — every
|
|
1115
|
+
* subscription rides the pooled platform connection, so depth streams share
|
|
1116
|
+
* connections with everything else the context runs.
|
|
1117
|
+
*/
|
|
1118
|
+
declare class BookEngine {
|
|
1119
|
+
readonly product: 'usdm' | 'spot';
|
|
1120
|
+
private readonly core;
|
|
1121
|
+
private readonly updateSpeed;
|
|
1122
|
+
private readonly snapshotLimit;
|
|
1123
|
+
private readonly resyncDelayMs;
|
|
1124
|
+
private readonly bookOptions?;
|
|
1125
|
+
private readonly events?;
|
|
1126
|
+
private readonly books;
|
|
1127
|
+
private readonly subscriptions;
|
|
1128
|
+
/** Symbols with a resync in flight (dedup per symbol). */
|
|
1129
|
+
private readonly resyncing;
|
|
1130
|
+
private readonly messageHandler;
|
|
1131
|
+
private closed;
|
|
1132
|
+
constructor(options: BookEngineOptions);
|
|
1133
|
+
/**
|
|
1134
|
+
* Start maintaining a local book for `symbol`: subscribe the pooled depth
|
|
1135
|
+
* stream, buffer diffs, fetch the REST snapshot, apply the overlap, go
|
|
1136
|
+
* live. Resolves once the book is synced; throws (after unsubscribing)
|
|
1137
|
+
* when the snapshot cannot be fetched.
|
|
1138
|
+
*/
|
|
1139
|
+
watch(symbol: string): Promise<ManagedBook>;
|
|
1140
|
+
/**
|
|
1141
|
+
* Stop maintaining a symbol: close the book and release its subscription
|
|
1142
|
+
* (refcounted — the server-level UNSUBSCRIBE fires only for the last
|
|
1143
|
+
* consumer). Idempotent; unknown symbols are a no-op.
|
|
1144
|
+
*/
|
|
1145
|
+
unwatch(symbol: string): Promise<void>;
|
|
1146
|
+
/** The managed book for a symbol, if watching. */
|
|
1147
|
+
getBook(symbol: string): ManagedBook | undefined;
|
|
1148
|
+
/** All watched symbols (insertion order). */
|
|
1149
|
+
symbols(): string[];
|
|
1150
|
+
/** True while the engine is usable (not terminally closed). */
|
|
1151
|
+
get isClosed(): boolean;
|
|
1152
|
+
/** Close every book and release every subscription. Terminal; idempotent. */
|
|
1153
|
+
close(): void;
|
|
1154
|
+
/** The pooled stream family this product's depth streams ride on. */
|
|
1155
|
+
private family;
|
|
1156
|
+
/** Snapshot host: `fapi` (USDⓈ-M `/fapi/v1/depth`) or `spot` (`/api/v3/depth`). */
|
|
1157
|
+
private snapshotHttp;
|
|
1158
|
+
/** Fetch + apply one REST snapshot for a symbol. */
|
|
1159
|
+
private snapshot;
|
|
1160
|
+
/** One depth frame off the pooled subscription → the book for its symbol. */
|
|
1161
|
+
private onDepthFrame;
|
|
1162
|
+
/** Delayed, deduplicated resync after a desync. */
|
|
1163
|
+
private scheduleResync;
|
|
1164
|
+
}
|
|
1165
|
+
|
|
1166
|
+
/**
|
|
1167
|
+
* v3 USDⓈ-M futures product client.
|
|
1168
|
+
*
|
|
1169
|
+
* The full product surface over a shared {@link CoreContext} — identical
|
|
1170
|
+
* wiring to `client.futures` on the multi-product facade (same construction
|
|
1171
|
+
* path, same `ExecutionManager`, same WS URLs), but standing alone:
|
|
1172
|
+
*
|
|
1173
|
+
* ```ts
|
|
1174
|
+
* const usdm = new USDMClient(new CoreContext({ apiKey, apiSecret }));
|
|
1175
|
+
* await usdm.core.syncTime(); // shared clock sync
|
|
1176
|
+
* const fill = await usdm.execution.placeOrder({
|
|
1177
|
+
* symbol: 'BTCUSDT', side: 'BUY', type: 'MARKET', quantity: '0.01',
|
|
1178
|
+
* });
|
|
1179
|
+
* usdm.ws.bookTicker('BTCUSDT', handler); // product WS surface
|
|
1180
|
+
* usdm.close(); // product-scoped cleanup
|
|
1181
|
+
* ```
|
|
1182
|
+
*
|
|
1183
|
+
* What the v3 boundary adds over the v2 `createUSDMClient()` factory: the
|
|
1184
|
+
* core context is caller-visible (`usdm.core`) — transports, credentials
|
|
1185
|
+
* and the event bus are reachable without globals, and two product clients
|
|
1186
|
+
* can share one context (one weight budget, one observability stream).
|
|
1187
|
+
*/
|
|
1188
|
+
declare class USDMClient implements ProductClient {
|
|
1189
|
+
readonly product: "usdm";
|
|
1190
|
+
readonly core: CoreContext;
|
|
1191
|
+
private readonly surface;
|
|
1192
|
+
private listenKeyValue;
|
|
1193
|
+
private keepAliveInterval;
|
|
1194
|
+
private executionPlatformValue;
|
|
1195
|
+
private bookEngineValue;
|
|
1196
|
+
private closed;
|
|
1197
|
+
constructor(core: CoreContext);
|
|
1198
|
+
get market(): UsdmSurface['market'];
|
|
1199
|
+
get data(): UsdmSurface['data'];
|
|
1200
|
+
get account(): UsdmSurface['account'];
|
|
1201
|
+
get trading(): UsdmSurface['trading'];
|
|
1202
|
+
get ops(): UsdmSurface['ops'];
|
|
1203
|
+
/** Idempotent order placement with transport-failure reconciliation. */
|
|
1204
|
+
get execution(): UsdmSurface['execution'];
|
|
1205
|
+
get userStream(): UsdmSurface['userStream'];
|
|
1206
|
+
get ws(): UsdmSurface['ws'];
|
|
1207
|
+
get wsUser(): UsdmSurface['wsUser'];
|
|
1208
|
+
get wsApi(): UsdmSurface['wsApi'];
|
|
1209
|
+
/**
|
|
1210
|
+
* The v3 execution platform: managed user-data stream session, live
|
|
1211
|
+
* order/position feeds, and semantic retry classification. Lazily built —
|
|
1212
|
+
* a REST-only caller never pays for it.
|
|
1213
|
+
*
|
|
1214
|
+
* ```ts
|
|
1215
|
+
* await usdm.executionPlatform.startUserSession();
|
|
1216
|
+
* usdm.executionPlatform.orders.get('nbsdk-…');
|
|
1217
|
+
* usdm.executionPlatform.positions.get('BTCUSDT');
|
|
1218
|
+
* ```
|
|
1219
|
+
*/
|
|
1220
|
+
get executionPlatform(): ExecutionPlatform;
|
|
1221
|
+
/**
|
|
1222
|
+
* The v3 state platform for USDⓈ-M: managed local L2 books over the pooled
|
|
1223
|
+
* WS platform + shared REST snapshot transports. Lazily built — a
|
|
1224
|
+
* stream-only or REST-only caller never pays for it.
|
|
1225
|
+
*
|
|
1226
|
+
* ```ts
|
|
1227
|
+
* const book = await usdm.books.watch('BTCUSDT'); // resolves once synced
|
|
1228
|
+
* book.bestBid; // exact decimal strings
|
|
1229
|
+
* book.metrics(); // spread, imbalance, …
|
|
1230
|
+
* await usdm.books.unwatch('BTCUSDT');
|
|
1231
|
+
* ```
|
|
1232
|
+
*/
|
|
1233
|
+
get books(): BookEngine;
|
|
1234
|
+
/**
|
|
1235
|
+
* Start the listen-key user-data stream: creates the key, schedules the
|
|
1236
|
+
* 30-minute keep-alive, and opens the user WS. Scoped to this product —
|
|
1237
|
+
* unlike the multi-product facade, no other product's sockets are touched.
|
|
1238
|
+
*/
|
|
1239
|
+
startUserStream(): Promise<string>;
|
|
1240
|
+
/** Close the user stream (timer, socket, and listen-key deletion). */
|
|
1241
|
+
closeUserStream(): void;
|
|
1242
|
+
/** Close this product's resources. Safe to call more than once. */
|
|
1243
|
+
close(): void;
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
/**
|
|
1247
|
+
* v3 Spot product client.
|
|
1248
|
+
*
|
|
1249
|
+
* The full Spot product surface over a shared {@link CoreContext} — identical
|
|
1250
|
+
* wiring to `client.spot` on the multi-product facade (same construction
|
|
1251
|
+
* path, same `ExecutionManager`, same WS URLs), but standing alone:
|
|
1252
|
+
*
|
|
1253
|
+
* ```ts
|
|
1254
|
+
* const spot = new SpotClient(new CoreContext({ apiKey, apiSecret }));
|
|
1255
|
+
* await spot.core.syncTime(); // shared clock sync
|
|
1256
|
+
* const fill = await spot.execution.placeOrder({
|
|
1257
|
+
* symbol: 'BTCUSDT', side: 'BUY', type: 'MARKET', quantity: '0.01',
|
|
1258
|
+
* });
|
|
1259
|
+
* spot.ws.bookTicker('BTCUSDT', handler); // product WS surface
|
|
1260
|
+
* spot.close(); // product-scoped cleanup
|
|
1261
|
+
* ```
|
|
1262
|
+
*
|
|
1263
|
+
* What the v3 boundary adds over the v2 `createSpotClient()` factory: the
|
|
1264
|
+
* core context is caller-visible (`spot.core`) — transports, credentials
|
|
1265
|
+
* and the event bus are reachable without globals, and two product clients
|
|
1266
|
+
* can share one context (one weight budget, one observability stream). It
|
|
1267
|
+
* also owns the v3 execution platform and the v3 state engine for Spot.
|
|
1268
|
+
*/
|
|
1269
|
+
declare class SpotClient implements ProductClient {
|
|
1270
|
+
readonly product: "spot";
|
|
1271
|
+
readonly core: CoreContext;
|
|
1272
|
+
private readonly surface;
|
|
1273
|
+
private listenKeyValue;
|
|
1274
|
+
private keepAliveInterval;
|
|
1275
|
+
private executionPlatformValue;
|
|
1276
|
+
private bookEngineValue;
|
|
1277
|
+
private closed;
|
|
1278
|
+
constructor(core: CoreContext);
|
|
1279
|
+
get market(): SpotSurface['market'];
|
|
1280
|
+
get account(): SpotSurface['account'];
|
|
1281
|
+
get trading(): SpotSurface['trading'];
|
|
1282
|
+
/** Idempotent order placement with transport-failure reconciliation. */
|
|
1283
|
+
get execution(): SpotSurface['execution'];
|
|
1284
|
+
get userStream(): SpotSurface['userStream'];
|
|
1285
|
+
get ws(): SpotSurface['ws'];
|
|
1286
|
+
get wsUser(): SpotSurface['wsUser'];
|
|
1287
|
+
get wsApi(): SpotSurface['wsApi'];
|
|
1288
|
+
/**
|
|
1289
|
+
* The v3 execution platform for Spot: managed user-data stream session,
|
|
1290
|
+
* live order feeds, and semantic retry classification. Lazily built —
|
|
1291
|
+
* a REST-only caller never pays for it.
|
|
1292
|
+
*
|
|
1293
|
+
* ```ts
|
|
1294
|
+
* await spot.executionPlatform.startUserSession();
|
|
1295
|
+
* spot.executionPlatform.orders.get('nbsdk-…');
|
|
1296
|
+
* ```
|
|
1297
|
+
*/
|
|
1298
|
+
get executionPlatform(): ExecutionPlatform;
|
|
1299
|
+
/**
|
|
1300
|
+
* The v3 state platform for Spot: managed local L2 books over the pooled
|
|
1301
|
+
* WS platform + shared REST snapshot transports. Lazily built — a
|
|
1302
|
+
* stream-only or REST-only caller never pays for it.
|
|
1303
|
+
*
|
|
1304
|
+
* ```ts
|
|
1305
|
+
* const book = await spot.books.watch('BTCUSDT'); // resolves once synced
|
|
1306
|
+
* book.bestBid; // exact decimal strings
|
|
1307
|
+
* book.metrics(); // spread, imbalance, …
|
|
1308
|
+
* await spot.books.unwatch('BTCUSDT');
|
|
1309
|
+
* ```
|
|
1310
|
+
*/
|
|
1311
|
+
get books(): BookEngine;
|
|
1312
|
+
/**
|
|
1313
|
+
* Start the listen-key user-data stream: creates the key, schedules the
|
|
1314
|
+
* 30-minute keep-alive, and opens the user WS. Scoped to this product —
|
|
1315
|
+
* unlike the multi-product facade, no other product's sockets are touched.
|
|
1316
|
+
*/
|
|
1317
|
+
startUserStream(): Promise<string>;
|
|
1318
|
+
/** Close the user stream (timer, socket, and listen-key deletion). */
|
|
1319
|
+
closeUserStream(): void;
|
|
1320
|
+
/** Close this product's resources. Safe to call more than once. */
|
|
1321
|
+
close(): void;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
/**
|
|
1325
|
+
* v3 COIN-M futures product client.
|
|
1326
|
+
*
|
|
1327
|
+
* The full COIN-M product surface over a shared {@link CoreContext} —
|
|
1328
|
+
* identical wiring to `client.coinm` on the multi-product facade (same
|
|
1329
|
+
* construction path, same resources, same WS URLs), but standing alone:
|
|
1330
|
+
*
|
|
1331
|
+
* ```ts
|
|
1332
|
+
* const coinm = new CoinMClient(new CoreContext({ apiKey, apiSecret }));
|
|
1333
|
+
* await coinm.core.syncTime(); // shared clock sync
|
|
1334
|
+
* const quote = await coinm.market.markPrice('BTCUSD_PERP');
|
|
1335
|
+
* coinm.ws.bookTicker('BTCUSD_PERP', handler); // product WS surface
|
|
1336
|
+
* coinm.close(); // product-scoped cleanup
|
|
1337
|
+
* ```
|
|
1338
|
+
*
|
|
1339
|
+
* What the v3 boundary adds over the v2 `createCoinMClient()` factory: the
|
|
1340
|
+
* core context is caller-visible (`coinm.core`) — transports, credentials
|
|
1341
|
+
* and the event bus are reachable without globals, and two product clients
|
|
1342
|
+
* can share one context (one weight budget, one observability stream).
|
|
1343
|
+
*
|
|
1344
|
+
* **Note on the v3 execution platform / state engine:** COIN-M does not yet
|
|
1345
|
+
* have its own `ExecutionPlatform` or `BookEngine` — those modules are
|
|
1346
|
+
* currently wired for `'usdm' | 'spot'` only. The full surface (`market`,
|
|
1347
|
+
* `account`, `trading`, `execution`, `userStream`, `ws`, `wsUser`) is here
|
|
1348
|
+
* and runs over the shared transports; the managed user-data session and
|
|
1349
|
+
* the L2 book engine for COIN-M are the next follow-up. Until then, the
|
|
1350
|
+
* manual `startUserStream()` path (listen key + 30-minute keep-alive +
|
|
1351
|
+
* user WS) is the way to consume COIN-M user data through this client.
|
|
1352
|
+
*/
|
|
1353
|
+
declare class CoinMClient implements ProductClient {
|
|
1354
|
+
readonly product: "coinm";
|
|
1355
|
+
readonly core: CoreContext;
|
|
1356
|
+
private readonly surface;
|
|
1357
|
+
private listenKeyValue;
|
|
1358
|
+
private keepAliveInterval;
|
|
1359
|
+
private closed;
|
|
1360
|
+
constructor(core: CoreContext);
|
|
1361
|
+
get market(): CoinMSurface['market'];
|
|
1362
|
+
get account(): CoinMSurface['account'];
|
|
1363
|
+
get trading(): CoinMSurface['trading'];
|
|
1364
|
+
get userStream(): CoinMSurface['userStream'];
|
|
1365
|
+
get ws(): CoinMSurface['ws'];
|
|
1366
|
+
get wsUser(): CoinMSurface['wsUser'];
|
|
1367
|
+
/**
|
|
1368
|
+
* Start the listen-key user-data stream: creates the key, schedules the
|
|
1369
|
+
* 30-minute keep-alive, and opens the user WS. Scoped to this product —
|
|
1370
|
+
* unlike the multi-product facade, no other product's sockets are touched.
|
|
1371
|
+
*/
|
|
1372
|
+
startUserStream(): Promise<string>;
|
|
1373
|
+
/** Close the user stream (timer, socket, and listen-key deletion). */
|
|
1374
|
+
closeUserStream(): void;
|
|
1375
|
+
/** Close this product's resources. Safe to call more than once. */
|
|
1376
|
+
close(): void;
|
|
1377
|
+
}
|
|
1378
|
+
|
|
1379
|
+
/**
|
|
1380
|
+
* Endpoint registry — machine-readable map of every REST endpoint this SDK
|
|
1381
|
+
* implements, the single source of truth consumed by:
|
|
1382
|
+
*
|
|
1383
|
+
* - `docs/endpoint-map/*.md` (generated by scripts/generate-endpoint-map.ts)
|
|
1384
|
+
* - `llms.txt` / `llms-full.txt` (agent-facing docs, also generated)
|
|
1385
|
+
* - tooling/CI (coverage accounting vs. Binance's published API surface)
|
|
1386
|
+
*
|
|
1387
|
+
* Registry entries are hand-curated but mechanically *checked*: the
|
|
1388
|
+
* `scripts/generate-endpoint-map.ts` script greps `src/resources` for live
|
|
1389
|
+
* `http.<verb>(...)` calls and fails when a registered endpoint is not
|
|
1390
|
+
* actually implemented (or vice versa), so the map cannot drift silently.
|
|
1391
|
+
*/
|
|
1392
|
+
type EndpointAuth = 'public' | 'apiKey' | 'signed';
|
|
1393
|
+
type EndpointMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
|
|
1394
|
+
interface EndpointEntry {
|
|
1395
|
+
/** Binance product family. */
|
|
1396
|
+
product: 'spot' | 'usdm' | 'coinm' | 'margin' | 'wallet' | 'subaccount';
|
|
1397
|
+
/** SDK surface implementing it, e.g. `futures.trading.createOrder`. */
|
|
1398
|
+
operation: string;
|
|
1399
|
+
method: EndpointMethod;
|
|
1400
|
+
path: string;
|
|
1401
|
+
authentication: EndpointAuth;
|
|
1402
|
+
/** Documented request weight where known (IP weight unless noted). */
|
|
1403
|
+
weight?: number;
|
|
1404
|
+
/** Resource class + method implementing the call. */
|
|
1405
|
+
implementedBy: string;
|
|
1406
|
+
}
|
|
1407
|
+
/** Every implemented endpoint, across all products. */
|
|
1408
|
+
declare const ENDPOINT_REGISTRY: readonly EndpointEntry[];
|
|
1409
|
+
interface EndpointQuery {
|
|
1410
|
+
product?: EndpointEntry['product'];
|
|
1411
|
+
method?: EndpointMethod;
|
|
1412
|
+
authentication?: EndpointAuth;
|
|
1413
|
+
pathPrefix?: string;
|
|
1414
|
+
}
|
|
1415
|
+
declare function listEndpoints(query?: EndpointQuery): readonly EndpointEntry[];
|
|
1416
|
+
declare function findEndpoint(path: string, method?: EndpointMethod): EndpointEntry | undefined;
|
|
1417
|
+
/** Per-product coverage counts for docs/CI. */
|
|
1418
|
+
declare function endpointCounts(): Record<string, number>;
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* API contract layer — the machine-readable view of the Binance REST surface
|
|
1422
|
+
* this SDK implements.
|
|
1423
|
+
*
|
|
1424
|
+
* The registry (src/registry) already lists every endpoint (operation, method,
|
|
1425
|
+
* path, authentication, implementer). This layer is the *contract* consumers
|
|
1426
|
+
* build on top of that list:
|
|
1427
|
+
*
|
|
1428
|
+
* - normalized **security schemes** (`none` / `apiKey` / `signature`) instead
|
|
1429
|
+
* of ad-hoc auth strings;
|
|
1430
|
+
* - **declared request weights** for the endpoints where Binance documents a
|
|
1431
|
+
* param-independent weight — informational only, response headers
|
|
1432
|
+
* (`X-MBX-USED-WEIGHT-*`) remain the source of truth for accounting;
|
|
1433
|
+
* - **path canonicalization** so a bare resource path (`/order`) resolves to
|
|
1434
|
+
* the registry's canonical form (`/api/v3/order`) using the HttpClient's
|
|
1435
|
+
* base URL — this is what lets the observability bus attach contract
|
|
1436
|
+
* metadata to every `http.request.*` event;
|
|
1437
|
+
* - a single query API (`getContract`, `findContract`, `listContracts`) for
|
|
1438
|
+
* tooling, docs generation and agent-facing surfaces.
|
|
1439
|
+
*
|
|
1440
|
+
* This is the stepping stone toward a fully generated contract layer (OpenAPI
|
|
1441
|
+
* spec → typed clients); the query surface below is deliberately shaped so a
|
|
1442
|
+
* generated implementation can replace the hand-curated backing store without
|
|
1443
|
+
* changing any consumer.
|
|
1444
|
+
*/
|
|
1445
|
+
|
|
1446
|
+
/** Normalized security scheme an endpoint requires. */
|
|
1447
|
+
type EndpointSecurityScheme = 'none' | 'apiKey' | 'signature';
|
|
1448
|
+
/**
|
|
1449
|
+
* A single endpoint's contract: registry entry + normalized security scheme +
|
|
1450
|
+
* declared weight (where Binance documents a param-independent one).
|
|
1451
|
+
*/
|
|
1452
|
+
interface EndpointContract extends EndpointEntry {
|
|
1453
|
+
security: EndpointSecurityScheme;
|
|
1454
|
+
/** Documented request weight where param-independent; see file docs. */
|
|
1455
|
+
declaredWeight?: number;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Resolve a resource-supplied path against an HttpClient base URL to the
|
|
1459
|
+
* registry's canonical path. Bare paths (`/order`) are prefixed with the base
|
|
1460
|
+
* URL's own path segment (`https://…/api/v3` → `/api/v3/order`); paths that
|
|
1461
|
+
* already carry a version prefix pass through unchanged.
|
|
1462
|
+
*/
|
|
1463
|
+
declare function canonicalPath(baseURL: string, path: string): string;
|
|
1464
|
+
/** Contract for a canonical path + method, or undefined when unknown. */
|
|
1465
|
+
declare function findContract(canonical: string, method?: EndpointMethod): EndpointContract | undefined;
|
|
1466
|
+
/** Contract lookup by product + operation, e.g. ('spot', 'trading.createOrder'). */
|
|
1467
|
+
declare function getContract(product: EndpointEntry['product'], operation: string): EndpointContract | undefined;
|
|
1468
|
+
/** Contract metadata attached to `http.request.*` observability events. */
|
|
1469
|
+
interface HttpContractMeta {
|
|
1470
|
+
product: EndpointEntry['product'];
|
|
1471
|
+
operation: string;
|
|
1472
|
+
authentication: EndpointAuth;
|
|
1473
|
+
security: EndpointSecurityScheme;
|
|
1474
|
+
declaredWeight: number | undefined;
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Resolve the contract for a request as the HttpClient sees it: base URL +
|
|
1478
|
+
* method + resource path. Returns undefined for unregistered paths (e.g.
|
|
1479
|
+
* host-level time endpoints reused across products).
|
|
1480
|
+
*/
|
|
1481
|
+
declare function contractFor(baseURL: string, method: string, path: string): HttpContractMeta | undefined;
|
|
1482
|
+
/** One-line human description, for logs and agent-facing docs. */
|
|
1483
|
+
declare function describeContract(contract: EndpointContract): string;
|
|
1484
|
+
|
|
1485
|
+
/** The pooled connection classes the platform already ships per family. */
|
|
1486
|
+
type PooledConnection = FuturesMarketWS | SpotMarketWS | CoinMMarketWS;
|
|
1487
|
+
interface FamilyConnectionPoolOptions {
|
|
1488
|
+
family: WsFamily;
|
|
1489
|
+
/** Combined-stream base URL for the family (from core.endpoints). */
|
|
1490
|
+
baseStreamUrl: string;
|
|
1491
|
+
/** Cap on pooled connections; default is the family's documented default. */
|
|
1492
|
+
maxConnections?: number;
|
|
1493
|
+
/** Reconnect backoff; default jittered exponential. */
|
|
1494
|
+
reconnectPolicy?: ReconnectPolicy;
|
|
1495
|
+
/** Observability bus. */
|
|
1496
|
+
events?: EventBus;
|
|
1497
|
+
/** Injectable WebSocket factory for tests. */
|
|
1498
|
+
socketFactory?: (url: string) => WebSocket;
|
|
1499
|
+
/** Control-request ack timeout. Default 10s. */
|
|
1500
|
+
requestTimeoutMs?: number;
|
|
1501
|
+
/**
|
|
1502
|
+
* Called exactly once per created connection so the platform can wire
|
|
1503
|
+
* heartbeat liveness and centralized renewal (the pool itself never
|
|
1504
|
+
* schedules timers beyond the connection's own reconnect backoff).
|
|
1505
|
+
*/
|
|
1506
|
+
onConnection?: (name: string, conn: PooledConnection) => void;
|
|
1507
|
+
}
|
|
1508
|
+
/**
|
|
1509
|
+
* Capacity-aware connection pool for one market-stream family.
|
|
1510
|
+
*
|
|
1511
|
+
* Binance documents a per-connection stream ceiling (200 for futures, 1024
|
|
1512
|
+
* for spot); the pool packs streams onto the least-loaded connection with
|
|
1513
|
+
* capacity and opens another connection (up to `maxConnections`) when every
|
|
1514
|
+
* existing one is full — so a 500-stream workload spreads across 3 USDⓈ-M
|
|
1515
|
+
* connections automatically instead of failing server-side.
|
|
1516
|
+
*
|
|
1517
|
+
* Pool-managed connections:
|
|
1518
|
+
* - disable the built-in 23h rotation timer (renewal is centralized in
|
|
1519
|
+
* {@link RenewalController} for stagger and observability);
|
|
1520
|
+
* - disable the built-in liveness watchdog (pool-level {@link WsHeartbeat}
|
|
1521
|
+
* uses one timer for all connections);
|
|
1522
|
+
* - use the platform reconnect policy (jittered backoff, so a pool-wide
|
|
1523
|
+
* outage does not retry in lockstep).
|
|
1524
|
+
*/
|
|
1525
|
+
declare class FamilyConnectionPool {
|
|
1526
|
+
readonly family: WsFamily;
|
|
1527
|
+
private readonly baseStreamUrl;
|
|
1528
|
+
private readonly maxConnections;
|
|
1529
|
+
private readonly maxStreamsPerConnection;
|
|
1530
|
+
private readonly events?;
|
|
1531
|
+
private readonly socketFactory?;
|
|
1532
|
+
private readonly requestTimeoutMs;
|
|
1533
|
+
private readonly onConnection?;
|
|
1534
|
+
private readonly createConnection;
|
|
1535
|
+
private readonly pool;
|
|
1536
|
+
constructor(options: FamilyConnectionPoolOptions);
|
|
1537
|
+
/**
|
|
1538
|
+
* Pick the connection that should carry `newStreams` — the least-loaded one
|
|
1539
|
+
* with capacity, or a newly created one. Creates never perform I/O: the
|
|
1540
|
+
* returned connection connects lazily on first `subscribe()`.
|
|
1541
|
+
*
|
|
1542
|
+
* Throws when every connection is at the documented stream ceiling and the
|
|
1543
|
+
* `maxConnections` cap is reached — the caller is over the family's
|
|
1544
|
+
* capacity and must raise the cap.
|
|
1545
|
+
*/
|
|
1546
|
+
selectOrCreate(newStreams: readonly string[]): PooledConnection;
|
|
1547
|
+
/** Live pooled connections (copy). */
|
|
1548
|
+
connections(): PooledConnection[];
|
|
1549
|
+
/** Number of pooled connections. */
|
|
1550
|
+
size(): number;
|
|
1551
|
+
/** Structural snapshot (cheap, no I/O). */
|
|
1552
|
+
stats(): WsConnectionStats[];
|
|
1553
|
+
/** Close every pooled connection. Safe repeatedly. */
|
|
1554
|
+
closeAll(): void;
|
|
1555
|
+
/** Next pool-assigned connection name, e.g. `usdmMarket[3]`. */
|
|
1556
|
+
private nextName;
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/**
|
|
1560
|
+
* Stream-level routing over one family's connection pool.
|
|
1561
|
+
*
|
|
1562
|
+
* Responsibilities:
|
|
1563
|
+
*
|
|
1564
|
+
* - **Refcounting** — N `Subscription` objects for one stream name share a
|
|
1565
|
+
* single server-level subscription; the last `close()` issues the protocol
|
|
1566
|
+
* UNSUBSCRIBE.
|
|
1567
|
+
* - **Fan-out** — one `message` listener per pooled connection dispatches
|
|
1568
|
+
* frames to every live subscription for that stream (parsed payload plus
|
|
1569
|
+
* lossless raw view).
|
|
1570
|
+
* - **Reconnect transparency** — when a pooled connection drops, its
|
|
1571
|
+
* subscriptions flip to `pending`; after the connection's own repair
|
|
1572
|
+
* (reconnect or rotation, followed by its resynchronize pass) the registry
|
|
1573
|
+
* re-marks every server-confirmed stream `live`.
|
|
1574
|
+
*
|
|
1575
|
+
* The registry never subscribes the same stream on two connections of the
|
|
1576
|
+
* same family — a stream maps to exactly one connection while it has
|
|
1577
|
+
* subscribers.
|
|
1578
|
+
*/
|
|
1579
|
+
declare class FamilySubscriptionRegistry {
|
|
1580
|
+
private readonly pool;
|
|
1581
|
+
private readonly events?;
|
|
1582
|
+
private readonly confirmTimeoutMs;
|
|
1583
|
+
private readonly mappings;
|
|
1584
|
+
/** Connections whose dispatch listeners are attached (bounded by pool cap). */
|
|
1585
|
+
private readonly attached;
|
|
1586
|
+
constructor(options: {
|
|
1587
|
+
pool: FamilyConnectionPool;
|
|
1588
|
+
events?: EventBus;
|
|
1589
|
+
requestTimeoutMs?: number;
|
|
1590
|
+
});
|
|
1591
|
+
/**
|
|
1592
|
+
* Acquire a subscription for one stream, confirming it server-side. When
|
|
1593
|
+
* the stream already has subscribers this is a pure fan-out join (no I/O).
|
|
1594
|
+
*
|
|
1595
|
+
* Rejects when confirmation does not arrive within `confirmTimeoutMs`;
|
|
1596
|
+
* the subscription stays registered in that case — it flips to `live` via
|
|
1597
|
+
* the reconnect/repair path and remains reachable through
|
|
1598
|
+
* {@link FamilySubscriptionRegistry.getSubscription}.
|
|
1599
|
+
*/
|
|
1600
|
+
acquire(stream: string, options?: SubscribeOptions): Promise<Subscription>;
|
|
1601
|
+
private doAcquire;
|
|
1602
|
+
/**
|
|
1603
|
+
* Release a subscription (refcount down). The last release for a stream
|
|
1604
|
+
* removes the mapping and issues the protocol UNSSUBSCRIBE (best-effort —
|
|
1605
|
+
* desired state is already dropped, so reconnects will not resurrect it).
|
|
1606
|
+
* Invoked automatically by {@link Subscription.close}.
|
|
1607
|
+
*/
|
|
1608
|
+
release(subscription: Subscription): void;
|
|
1609
|
+
/** Active subscription object for a stream, if one is registered. */
|
|
1610
|
+
getSubscription(stream: string): Subscription | undefined;
|
|
1611
|
+
/** Streams with at least one live subscription. */
|
|
1612
|
+
activeStreams(): string[];
|
|
1613
|
+
/** Registry snapshot: streams per connection. */
|
|
1614
|
+
stats(): {
|
|
1615
|
+
streams: number;
|
|
1616
|
+
connections: number;
|
|
1617
|
+
};
|
|
1618
|
+
/** Close every subscription (platform teardown). */
|
|
1619
|
+
closeAll(): void;
|
|
1620
|
+
private attach;
|
|
1621
|
+
private markConnectionPending;
|
|
1622
|
+
private refreshConfirmed;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
/**
|
|
1626
|
+
* Minimal connection surface the heartbeat needs — implemented by
|
|
1627
|
+
* {@link WsConnection} and satisfiable by test stubs.
|
|
1628
|
+
*/
|
|
1629
|
+
interface HeartbeatWatch {
|
|
1630
|
+
/** Connection label for events. */
|
|
1631
|
+
name?: string;
|
|
1632
|
+
getState(): string;
|
|
1633
|
+
/** ms since the last inbound frame; Infinity when never opened. */
|
|
1634
|
+
msSinceLastActivity(): number;
|
|
1635
|
+
/** Tear down into the reconnect path. */
|
|
1636
|
+
reconnect(): void;
|
|
1637
|
+
}
|
|
1638
|
+
interface WsHeartbeatOptions {
|
|
1639
|
+
/** Silence threshold that declares a connection dead. Default 10 min. */
|
|
1640
|
+
staleMs?: number;
|
|
1641
|
+
/** Sampling interval. Default 30s. */
|
|
1642
|
+
intervalMs?: number;
|
|
1643
|
+
/** Observability bus. */
|
|
1644
|
+
events?: EventBus;
|
|
1645
|
+
/** Injectable clock for tests. */
|
|
1646
|
+
now?: () => number;
|
|
1647
|
+
/** Injectable timer for tests (returns an unref-able handle). */
|
|
1648
|
+
setInterval?: (fn: () => void, ms: number) => {
|
|
1649
|
+
unref?: () => void;
|
|
1650
|
+
};
|
|
1651
|
+
}
|
|
1652
|
+
/**
|
|
1653
|
+
* Pool-level liveness watchdog with a *single* timer.
|
|
1654
|
+
*
|
|
1655
|
+
* `WsConnection` already supports per-connection `staleMs`, but v3 platforms
|
|
1656
|
+
* watch dozens of sockets; one tick beats N setInterval handles, and the
|
|
1657
|
+
* judgment (server pings every 3 minutes → 10 minutes of total silence means
|
|
1658
|
+
* the path is dead) lives in exactly one place. Protocol-level activity
|
|
1659
|
+
* (pings/pongs) counts as liveness via `msSinceLastActivity`.
|
|
1660
|
+
*
|
|
1661
|
+
* When a connection goes stale the heartbeat does not wait for TCP to notice:
|
|
1662
|
+
* it calls `reconnect()` (terminate + fresh socket), which is the correct
|
|
1663
|
+
* response to a half-open path.
|
|
1664
|
+
*/
|
|
1665
|
+
declare class WsHeartbeat {
|
|
1666
|
+
private readonly staleMs;
|
|
1667
|
+
private readonly intervalMs;
|
|
1668
|
+
private readonly events?;
|
|
1669
|
+
private readonly now;
|
|
1670
|
+
private readonly timerFn;
|
|
1671
|
+
private readonly watched;
|
|
1672
|
+
private timer;
|
|
1673
|
+
constructor(options?: WsHeartbeatOptions);
|
|
1674
|
+
/** Start watching a connection under a stable key. */
|
|
1675
|
+
watch(key: string, conn: HeartbeatWatch): void;
|
|
1676
|
+
/** Stop watching (connection closed or pooled away). */
|
|
1677
|
+
unwatch(key: string): void;
|
|
1678
|
+
/** True when currently watched. */
|
|
1679
|
+
isWatching(key: string): boolean;
|
|
1680
|
+
/** Number of watched connections. */
|
|
1681
|
+
size(): number;
|
|
1682
|
+
/** Run one check immediately (also the interval body). */
|
|
1683
|
+
check(): {
|
|
1684
|
+
stale: string[];
|
|
1685
|
+
};
|
|
1686
|
+
start(): void;
|
|
1687
|
+
stop(): void;
|
|
1688
|
+
private ensureTicking;
|
|
1689
|
+
}
|
|
1690
|
+
|
|
1691
|
+
/**
|
|
1692
|
+
* Minimal connection surface the renewal controller needs — implemented by
|
|
1693
|
+
* {@link WsConnection} (an EventEmitter) and satisfiable by test stubs.
|
|
1694
|
+
*
|
|
1695
|
+
* Zero-gap rotation is transparent in the state machine: the connection stays
|
|
1696
|
+
* OPEN while the replacement connects, so completion is signalled by the
|
|
1697
|
+
* `rotated` / `rotationFailed` emitter events, not by state transitions.
|
|
1698
|
+
*/
|
|
1699
|
+
interface RenewableConnection {
|
|
1700
|
+
on(event: 'state', listener: (to: string, from: string) => void): unknown;
|
|
1701
|
+
on(event: 'rotated', listener: () => void): unknown;
|
|
1702
|
+
on(event: 'rotationFailed', listener: (info: {
|
|
1703
|
+
code: number;
|
|
1704
|
+
reason: string;
|
|
1705
|
+
}) => void): unknown;
|
|
1706
|
+
off(event: 'state', listener: (to: string, from: string) => void): unknown;
|
|
1707
|
+
off(event: 'rotated', listener: () => void): unknown;
|
|
1708
|
+
off(event: 'rotationFailed', listener: (info: {
|
|
1709
|
+
code: number;
|
|
1710
|
+
reason: string;
|
|
1711
|
+
}) => void): unknown;
|
|
1712
|
+
getState(): string;
|
|
1713
|
+
/** Attempt a zero-gap rotation; false when not applicable. */
|
|
1714
|
+
rotateNow(): boolean;
|
|
1715
|
+
}
|
|
1716
|
+
interface RenewalControllerOptions {
|
|
1717
|
+
/**
|
|
1718
|
+
* Renewal window. Binance kills stream connections at 24h; the platform
|
|
1719
|
+
* rotates at 23h so the replacement carries traffic *before* the drop.
|
|
1720
|
+
* Default 23h.
|
|
1721
|
+
*/
|
|
1722
|
+
rotationMs?: number;
|
|
1723
|
+
/**
|
|
1724
|
+
* Per-connection jitter (uniform [0, jitterMs)). Two connections opened in
|
|
1725
|
+
* the same millisecond would otherwise renew in the same millisecond — a
|
|
1726
|
+
* synchronized herd. Default 10 min.
|
|
1727
|
+
*/
|
|
1728
|
+
jitterMs?: number;
|
|
1729
|
+
/**
|
|
1730
|
+
* Platform-wide cap on simultaneous renewals; the default 1 serializes
|
|
1731
|
+
* rotations so a pool of N connections renews one at a time.
|
|
1732
|
+
*/
|
|
1733
|
+
maxConcurrent?: number;
|
|
1734
|
+
/** Observability bus. */
|
|
1735
|
+
events?: EventBus;
|
|
1736
|
+
/** Injectable randomness for tests; must return [0, 1). */
|
|
1737
|
+
random?: () => number;
|
|
1738
|
+
/** Injectable timer for tests. */
|
|
1739
|
+
setTimeout?: (fn: () => void, ms: number) => {
|
|
1740
|
+
unref?: () => void;
|
|
1741
|
+
};
|
|
1742
|
+
/** Postponement/retry interval. Default 30s. */
|
|
1743
|
+
retryMs?: number;
|
|
1744
|
+
}
|
|
1745
|
+
/**
|
|
1746
|
+
* Centralized, staggered 24h-safe renewal sequencing.
|
|
1747
|
+
*
|
|
1748
|
+
* `WsConnection` has a built-in 23h rotation timer; pool-managed connections
|
|
1749
|
+
* disable it (`rotationMs: 0`) and defer to this controller because:
|
|
1750
|
+
*
|
|
1751
|
+
* - **Decorrelation** — every connection gets a uniform jitter on top of the
|
|
1752
|
+
* 23h window, so a pool created at boot rotates across a spread, not a
|
|
1753
|
+
* synchronized herd.
|
|
1754
|
+
* - **Concurrency cap** — at most `maxConcurrent` rotations platform-wide;
|
|
1755
|
+
* a herd forced to serialize is a blip, not an event.
|
|
1756
|
+
* - **Observability** — `nextRenewalAt(name)` and `ws.renewal.*` events make
|
|
1757
|
+
* the rotation schedule inspectable instead of a hidden timer per socket.
|
|
1758
|
+
*
|
|
1759
|
+
* Renewals are zero-gap: the connection swaps sockets internally (traffic
|
|
1760
|
+
* keeps flowing on the old one until the replacement is open).
|
|
1761
|
+
*/
|
|
1762
|
+
declare class RenewalController {
|
|
1763
|
+
private readonly rotationMs;
|
|
1764
|
+
private readonly jitterMs;
|
|
1765
|
+
private readonly maxConcurrent;
|
|
1766
|
+
private readonly events?;
|
|
1767
|
+
private readonly random;
|
|
1768
|
+
private readonly setTimeoutFn;
|
|
1769
|
+
private readonly retryMs;
|
|
1770
|
+
private readonly tracked;
|
|
1771
|
+
constructor(options?: RenewalControllerOptions);
|
|
1772
|
+
/**
|
|
1773
|
+
* Start centrally managing renewal for a connection. Scheduling anchors on
|
|
1774
|
+
* the connection reaching OPEN (or immediately, if already open), so a
|
|
1775
|
+
* tracked-but-not-yet-connected socket does not burn its rotation window.
|
|
1776
|
+
*/
|
|
1777
|
+
track(name: string, conn: RenewableConnection): void;
|
|
1778
|
+
/** Stop managing a connection; safe to call for unknown names. */
|
|
1779
|
+
untrack(name: string): void;
|
|
1780
|
+
/** Epoch-ms of the next scheduled renewal, or null when idle/closed. */
|
|
1781
|
+
nextRenewalAt(name: string): number | null;
|
|
1782
|
+
/** True while a renewal is in flight for this connection. */
|
|
1783
|
+
isRenewing(name: string): boolean;
|
|
1784
|
+
/** Number of centrally managed connections. */
|
|
1785
|
+
size(): number;
|
|
1786
|
+
/** Stop managing everything. */
|
|
1787
|
+
close(): void;
|
|
1788
|
+
private scheduleRenewal;
|
|
1789
|
+
private fireRenewal;
|
|
1790
|
+
private countInFlight;
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
/**
|
|
1794
|
+
* Reconnect backoff strategies.
|
|
1795
|
+
*
|
|
1796
|
+
* v2's {@link WsConnection} hard-coded `min(base * 2^attempt, max)`. The
|
|
1797
|
+
* platform keeps that as the default shape but makes it pluggable and adds
|
|
1798
|
+
* jitter: a reconnect storm (e.g. Binance restarting an edge) hits every
|
|
1799
|
+
* connection at once, and deterministic identical delays make the herd
|
|
1800
|
+
* collide on every subsequent attempt too. *Equal jitter* — half fixed, half
|
|
1801
|
+
* random — keeps the first retry responsive while decorrelating the herd.
|
|
1802
|
+
*/
|
|
1803
|
+
interface ExponentialBackoffOptions {
|
|
1804
|
+
/** First-attempt delay. Default 1000ms. */
|
|
1805
|
+
baseMs?: number;
|
|
1806
|
+
/** Ceiling. Default 30000ms. */
|
|
1807
|
+
maxMs?: number;
|
|
1808
|
+
/** Random decorrelation; defaults on. Disable for deterministic tests. */
|
|
1809
|
+
jitter?: boolean;
|
|
1810
|
+
/** Injectable randomness for tests; must return [0, 1). */
|
|
1811
|
+
random?: () => number;
|
|
1812
|
+
}
|
|
1813
|
+
/**
|
|
1814
|
+
* Exponential backoff with equal jitter (default policy):
|
|
1815
|
+
* `raw = min(base * 2^attempt, max)`; returned delay is
|
|
1816
|
+
* `raw/2 + random() * raw/2` — always ≥ raw/2, ≤ raw.
|
|
1817
|
+
*/
|
|
1818
|
+
declare function exponentialBackoff(options?: ExponentialBackoffOptions): ReconnectPolicy;
|
|
1819
|
+
/** Constant delay — reconnects at a fixed cadence regardless of attempt. */
|
|
1820
|
+
declare function fixedBackoff(delayMs: number): ReconnectPolicy;
|
|
1821
|
+
/** Linear backoff: `base * (attempt + 1)`, capped at `maxMs`. */
|
|
1822
|
+
declare function linearBackoff(options?: {
|
|
1823
|
+
baseMs?: number;
|
|
1824
|
+
maxMs?: number;
|
|
1825
|
+
}): ReconnectPolicy;
|
|
1826
|
+
|
|
1827
|
+
/**
|
|
1828
|
+
* Semantic retry classification for order operations.
|
|
1829
|
+
*
|
|
1830
|
+
* "Can I retry this?" is the first question every caller of a trading SDK
|
|
1831
|
+
* asks after an error, and the honest answer depends on *which* failure
|
|
1832
|
+
* occurred, not on generic error taxonomy:
|
|
1833
|
+
*
|
|
1834
|
+
* - a timeout on order placement leaves the outcome unknown — the order may
|
|
1835
|
+
* be live; retrying blind can double the position (`reconciliation-required`);
|
|
1836
|
+
* - a `-2013` after an ambiguous submission *proves* the order never reached
|
|
1837
|
+
* the engine — resubmission is free of risk (`safe`);
|
|
1838
|
+
* - `-1013` filter rejections are deterministic — the identical request
|
|
1839
|
+
* fails forever (`never-retry`).
|
|
1840
|
+
*
|
|
1841
|
+
* This module makes the SDK's answer explicit and testable. The execution
|
|
1842
|
+
* manager already *embodies* this matrix (its resubmit-after-`-2013` path is
|
|
1843
|
+
* the 'safe' branch; its `ExecutionUnknownError` is 'reconciliation-required'
|
|
1844
|
+
* made fatal); the classifier exposes the same reasoning to callers holding
|
|
1845
|
+
* raw errors, so agents and tools can decide without reimplementing it.
|
|
1846
|
+
*
|
|
1847
|
+
* Note on `'idempotent'`: the live exchange never earns it — Binance does not
|
|
1848
|
+
* enforce `newClientOrderId` uniqueness, so a "retry with the same key" is not
|
|
1849
|
+
* distinguishable from a second order. The category exists for backends whose
|
|
1850
|
+
* create operation is genuinely keyed (paper engines, future brokers).
|
|
1851
|
+
*/
|
|
1852
|
+
/** Classify one error for retry semantics against the live exchange. */
|
|
1853
|
+
declare function classifyRetrySafety(error: unknown): RetryClassification;
|
|
1854
|
+
/** Narrow helper for switch-style consumers. */
|
|
1855
|
+
declare function isRetrySafety(value: unknown): value is RetrySafety;
|
|
1856
|
+
|
|
1857
|
+
/**
|
|
1858
|
+
* v3 paper execution platform — the assembled paper runtime.
|
|
1859
|
+
*
|
|
1860
|
+
* See `PaperSession.ts` for the session that replays simulator fills as
|
|
1861
|
+
* user-data frames; this module is the one-call wiring of the whole stack:
|
|
1862
|
+
* simulator + adapter + execution manager + execution platform, over one
|
|
1863
|
+
* shared {@link CoreContext}. Use {@link createPaperExecutionPlatform}.
|
|
1864
|
+
*/
|
|
1865
|
+
/** Options for {@link createPaperExecutionPlatform}. */
|
|
1866
|
+
interface PaperExecutionPlatformOptions extends PaperTradingOptions {
|
|
1867
|
+
/**
|
|
1868
|
+
* Report source wiring: pass an adapter when one already routes this engine
|
|
1869
|
+
* (e.g. an existing gateway); a fresh one is built otherwise. The platform
|
|
1870
|
+
* registers its own listener — since M4, paper reports fan out to every
|
|
1871
|
+
* listener, so an execution manager over the same adapter keeps working.
|
|
1872
|
+
*/
|
|
1873
|
+
adapter?: PaperExecutionAdapter;
|
|
1874
|
+
}
|
|
1875
|
+
/** The assembled paper execution platform. */
|
|
1876
|
+
interface PaperExecutionPlatform {
|
|
1877
|
+
/** Execution platform in paper mode — session, trackers, classify, reconcile. */
|
|
1878
|
+
platform: ExecutionPlatform;
|
|
1879
|
+
/** Paper-backed execution manager: idempotent place/cancel/reconcile. */
|
|
1880
|
+
execution: ExecutionManager;
|
|
1881
|
+
/** The simulator engine (account, positions, order history). */
|
|
1882
|
+
engine: PaperTradingEngine;
|
|
1883
|
+
/** The adapter orders flow through. */
|
|
1884
|
+
adapter: PaperExecutionAdapter;
|
|
1885
|
+
}
|
|
1886
|
+
/**
|
|
1887
|
+
* Build a complete paper trading runtime over a shared {@link CoreContext}:
|
|
1888
|
+
* simulator + adapter + execution manager + execution platform, all wired.
|
|
1889
|
+
*
|
|
1890
|
+
* This is "paper as a first-class execution backend": the same
|
|
1891
|
+
* {@link ExecutionPlatform} boundary live trading uses, with the simulator in
|
|
1892
|
+
* the server seat. Market prices come through the shared core transports
|
|
1893
|
+
* (testnet/demo envs and request mocks apply), so a paper run is the live
|
|
1894
|
+
* run with the matching engine swapped out.
|
|
1895
|
+
*
|
|
1896
|
+
* ```ts
|
|
1897
|
+
* const core = new CoreContext({ apiKey, apiSecret });
|
|
1898
|
+
* const paper = createPaperExecutionPlatform(core, { initialBalance: 25_000 });
|
|
1899
|
+
*
|
|
1900
|
+
* await paper.platform.startUserSession(); // no network — local session
|
|
1901
|
+
* const fill = await paper.execution.placeOrder({
|
|
1902
|
+
* symbol: 'BTCUSDT', side: 'BUY', type: 'MARKET', quantity: '0.01',
|
|
1903
|
+
* });
|
|
1904
|
+
* // the fill is already folded — same API as live:
|
|
1905
|
+
* paper.platform.orders.get(fill.clientOrderId); // OrderRecord (status: FILLED)
|
|
1906
|
+
* paper.platform.positions.get('BTCUSDT'); // PositionRecord (signed amount)
|
|
1907
|
+
* ```
|
|
1908
|
+
*/
|
|
1909
|
+
declare function createPaperExecutionPlatform(core: CoreContext, options?: PaperExecutionPlatformOptions): PaperExecutionPlatform;
|
|
297
1910
|
|
|
298
1911
|
declare class BinanceError extends Error {
|
|
299
1912
|
constructor(message: string);
|
|
300
1913
|
}
|
|
301
1914
|
|
|
302
1915
|
declare class BinanceAuthError extends BinanceError {
|
|
303
|
-
readonly missing: 'apiKey' | 'apiSecret';
|
|
304
|
-
constructor(missing: 'apiKey' | 'apiSecret');
|
|
1916
|
+
readonly missing: 'apiKey' | 'apiSecret' | 'privateKey';
|
|
1917
|
+
constructor(missing: 'apiKey' | 'apiSecret' | 'privateKey');
|
|
305
1918
|
}
|
|
306
1919
|
|
|
1920
|
+
interface BinanceApiErrorContext {
|
|
1921
|
+
endpoint?: string;
|
|
1922
|
+
method?: string;
|
|
1923
|
+
headers?: Record<string, string>;
|
|
1924
|
+
}
|
|
307
1925
|
declare class BinanceApiError extends BinanceError {
|
|
308
1926
|
readonly code: number;
|
|
309
1927
|
readonly status: number;
|
|
310
|
-
|
|
1928
|
+
readonly endpoint?: string;
|
|
1929
|
+
readonly method?: string;
|
|
1930
|
+
readonly headers?: Record<string, string>;
|
|
1931
|
+
constructor(message: string, code: number, status: number, context?: BinanceApiErrorContext);
|
|
1932
|
+
/** -1003 (way too many requests) or HTTP 429/418. */
|
|
1933
|
+
isRateLimitError(): boolean;
|
|
1934
|
+
/** -1021: request timestamp outside recvWindow, usually caused by local clock drift. */
|
|
1935
|
+
isTimestampError(): boolean;
|
|
1936
|
+
/** -2010: account has insufficient balance for the requested action. */
|
|
1937
|
+
isInsufficientBalance(): boolean;
|
|
311
1938
|
}
|
|
312
1939
|
|
|
313
1940
|
declare class RateLimitError extends BinanceApiError {
|
|
314
1941
|
readonly retryAfterMs?: number;
|
|
315
|
-
constructor(message: string, code: number, status: number, retryAfterMs?: number);
|
|
1942
|
+
constructor(message: string, code: number, status: number, retryAfterMs?: number, context?: BinanceApiErrorContext);
|
|
316
1943
|
}
|
|
317
1944
|
|
|
318
1945
|
declare class NetworkError extends BinanceError {
|
|
@@ -320,8 +1947,41 @@ declare class NetworkError extends BinanceError {
|
|
|
320
1947
|
constructor(message: string, cause?: unknown);
|
|
321
1948
|
}
|
|
322
1949
|
|
|
323
|
-
|
|
1950
|
+
type PolicyRule = 'readOnly' | 'symbolNotAllowed' | 'maxNotionalPerOrder' | 'notionalUndeterminable' | 'withdrawalsBlocked' | 'transfersBlocked' | 'blockedPath' | 'circuitBreaker' | 'maxLeverage' | 'maxOpenNotional' | 'maxDailyLoss';
|
|
1951
|
+
/**
|
|
1952
|
+
* Thrown when a request is refused by the client's own {@link TradingPolicy} — before it
|
|
1953
|
+
* ever reaches Binance. Distinct from BinanceApiError, which reports an exchange rejection.
|
|
1954
|
+
*/
|
|
1955
|
+
declare class PolicyViolationError extends BinanceError {
|
|
1956
|
+
readonly rule: PolicyRule;
|
|
1957
|
+
readonly method: string;
|
|
1958
|
+
readonly endpoint: string;
|
|
1959
|
+
readonly details?: Record<string, unknown>;
|
|
1960
|
+
constructor(rule: PolicyRule, message: string, method: string, endpoint: string, details?: Record<string, unknown>);
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
/**
|
|
1964
|
+
* Thrown instead of sending a mutating request when the client is in dry-run mode.
|
|
1965
|
+
*
|
|
1966
|
+
* Dry run deliberately throws rather than returning a synthetic success payload: a fabricated
|
|
1967
|
+
* orderId would lead an agent to believe an order exists and then try to manage or cancel it.
|
|
1968
|
+
* Failing loudly — while carrying the exact request that *would* have been sent — is the only
|
|
1969
|
+
* safe signal. Tool layers should catch this and surface `describe()` back to the model.
|
|
1970
|
+
*/
|
|
1971
|
+
declare class DryRunError extends BinanceError {
|
|
1972
|
+
readonly method: string;
|
|
1973
|
+
readonly endpoint: string;
|
|
1974
|
+
readonly params: Record<string, unknown>;
|
|
1975
|
+
constructor(method: string, endpoint: string, params?: Record<string, unknown>);
|
|
1976
|
+
/** Human/LLM-readable description of the suppressed request. */
|
|
1977
|
+
describe(): string;
|
|
1978
|
+
}
|
|
324
1979
|
|
|
325
|
-
|
|
1980
|
+
/**
|
|
1981
|
+
* MCP server over the toolkit. Pass `toolkit: { executionBackend: 'paper' }`
|
|
1982
|
+
* to route every order tool through the simulator — safe to expose to any
|
|
1983
|
+
* host without API-key trading risk.
|
|
1984
|
+
*/
|
|
1985
|
+
declare function createBinanceMcpServer(client: BinanceClient, options?: FuturesToolkitOptions): McpServer;
|
|
326
1986
|
|
|
327
|
-
export { BinanceApiError, BinanceAuthError, BinanceClient, BinanceError, type
|
|
1987
|
+
export { BinanceApiError, type BinanceApiErrorContext, BinanceAuthError, BinanceClient, BinanceClientOptions, BinanceError, BookEngine, type BookEngineOptions, BookLevel, BookSnapshotInput, type BookSyncEvent, type BookUpdateEvent, CoinMClient, CoinMMarketWS, CoinMSurface, CoreContext, CreateOrderParams, DiffInput, DryRunError, ENDPOINT_REGISTRY, EXECUTION_PLATFORM_DEFAULTS, type EndpointAuth, type EndpointContract, type EndpointEntry, type EndpointMethod, type EndpointQuery, type EndpointSecurityScheme, EventBus, ExecutionAdapter, ExecutionFill, ExecutionManager, ExecutionPlatform, type ExecutionPlatformOptions, ExecutionReportShape, type ExponentialBackoffOptions, FamilyConnectionPool, type FamilyConnectionPoolOptions, FamilySubscriptionRegistry, FuturesMarketWS, FuturesToolkitOptions, type HttpContractMeta, type ListenKeyApi, ManagedBook, type ManagedBookOptions, type ManagedBookState, NetworkError, OrderBookMetrics, OrderKey, type OrderRecord, OrderShape, type OrderStatus, OrderTracker, type OrderTrackerOptions, type OrderUpdate, type PaperBackendOptions, PaperExecutionAdapter, type PaperExecutionPlatform, type PaperExecutionPlatformOptions, PaperSession, type PaperSessionOptions, PaperTradingEngine, PaperTradingOptions, type PlatformUserSession, type PolicyRule, PolicyViolationError, type PooledConnection, type PositionRecord, PositionTracker, type PositionTrackerOptions, type PositionUpdate, type ProductClient, type ProductFactory, type ProductId, RateLimitError, type ReconcileOptions, type ReconcileTarget, type ReconciliationSummary, ReconnectPolicy, type RenewableConnection, RenewalController, type RenewalControllerOptions, type RetryClassification, type RetrySafety, SpotClient, SpotMarketWS, SpotSurface, type CoinMClient$1 as StandaloneCoinMClient, type SpotClient$1 as StandaloneSpotClient, type USDMClient$1 as StandaloneUSDMClient, SubscribeOptions, Subscription, USDMClient, UsdmSurface, type UserEventParser, type UserSessionState, type UserSessionTuning, UserStreamLike, UserStreamSession, type UserStreamSessionOptions, VERSION, WsConnection, WsConnectionOptions, WsConnectionStats, WsFamily, WsHeartbeat, type WsHeartbeatOptions, canonicalPath, classifyRetrySafety, contractFor, createBinanceMcpServer, createCoinMClient, createPaperExecutionPlatform, createSpotClient, createUSDMClient, createUserEventParser, decimalString, describeContract, endpointCounts, exponentialBackoff, findContract, findEndpoint, fixedBackoff, getContract, isProductClient, isRetrySafety, isTerminalOrderStatus, linearBackoff, listEndpoints, normalizeIntStrings, orderShapeFromSpotOpenOrder, orderShapeFromUsdmOpenOrder, paperAccountUpdateFrame, paperOrderTradeUpdateFrame, parseJsonLossless, parseJsonLosslessAs, positionUpdatesFromPositionRisk, reconcileExecutionPlatform };
|