@nemesis-oss/binance-sdk 1.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/dist/index.js ADDED
@@ -0,0 +1,4402 @@
1
+ // src/client/HttpClient.ts
2
+ import { createHmac } from "crypto";
3
+ import axios from "axios";
4
+ import Bottleneck from "bottleneck";
5
+
6
+ // src/errors/BinanceError.ts
7
+ var BinanceError = class extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "BinanceError";
11
+ }
12
+ };
13
+
14
+ // src/errors/BinanceAuthError.ts
15
+ var BinanceAuthError = class extends BinanceError {
16
+ constructor(missing) {
17
+ super(`API ${missing} required for authenticated requests`);
18
+ this.name = "BinanceAuthError";
19
+ this.missing = missing;
20
+ }
21
+ };
22
+
23
+ // src/errors/BinanceApiError.ts
24
+ var BinanceApiError = class extends BinanceError {
25
+ constructor(message, code, status) {
26
+ super(message);
27
+ this.name = "BinanceApiError";
28
+ this.code = code;
29
+ this.status = status;
30
+ }
31
+ };
32
+
33
+ // src/errors/RateLimitError.ts
34
+ var RateLimitError = class extends BinanceApiError {
35
+ constructor(message, code, status, retryAfterMs) {
36
+ super(message, code, status);
37
+ this.name = "RateLimitError";
38
+ this.retryAfterMs = retryAfterMs;
39
+ }
40
+ };
41
+
42
+ // src/errors/NetworkError.ts
43
+ var NetworkError = class extends BinanceError {
44
+ constructor(message, cause) {
45
+ super(message);
46
+ this.name = "NetworkError";
47
+ this.cause = cause;
48
+ }
49
+ };
50
+
51
+ // src/client/HttpClient.ts
52
+ var RETRYABLE_STATUS = /* @__PURE__ */ new Set([429, 418, 500, 502, 503, 504]);
53
+ function sleep(ms) {
54
+ return new Promise((resolve) => setTimeout(resolve, ms));
55
+ }
56
+ var HttpClient = class {
57
+ constructor(options) {
58
+ this.pendingRequests = 0;
59
+ this.apiKey = options.apiKey;
60
+ this.apiSecret = options.apiSecret;
61
+ this.recvWindow = options.recvWindow ?? 5e3;
62
+ this.timeoutMs = options.timeoutMs ?? 15e3;
63
+ this.maxRetries = options.maxRetries ?? 3;
64
+ this.minTimeMs = options.minTimeMs ?? 50;
65
+ this.retryBaseDelayMs = options.retryBaseDelayMs ?? 1e3;
66
+ this.retryMaxDelayMs = options.retryMaxDelayMs ?? 3e4;
67
+ this.retryFactor = options.retryFactor ?? 2;
68
+ this.axios = axios.create({ baseURL: options.baseURL, timeout: this.timeoutMs });
69
+ this.limiter = new Bottleneck({ minTime: this.minTimeMs });
70
+ }
71
+ async get(path, params, mode = "public") {
72
+ return this.request("GET", path, params, mode);
73
+ }
74
+ async post(path, params, mode = "public") {
75
+ return this.request("POST", path, params, mode);
76
+ }
77
+ async put(path, params, mode = "public") {
78
+ return this.request("PUT", path, params, mode);
79
+ }
80
+ async delete(path, params, mode = "public") {
81
+ return this.request("DELETE", path, params, mode);
82
+ }
83
+ getRateLimiterStatus() {
84
+ return { minTimeMs: this.minTimeMs, queueLength: this.pendingRequests };
85
+ }
86
+ configureRateLimiter(options) {
87
+ if (options.minTimeMs !== void 0) {
88
+ this.minTimeMs = options.minTimeMs;
89
+ this.limiter.updateSettings({ minTime: this.minTimeMs });
90
+ }
91
+ }
92
+ getRetryConfig() {
93
+ return {
94
+ maxRetries: this.maxRetries,
95
+ baseDelayMs: this.retryBaseDelayMs,
96
+ maxDelayMs: this.retryMaxDelayMs,
97
+ factor: this.retryFactor
98
+ };
99
+ }
100
+ configureRetry(options) {
101
+ if (options.maxRetries !== void 0) this.maxRetries = options.maxRetries;
102
+ if (options.baseDelayMs !== void 0) this.retryBaseDelayMs = options.baseDelayMs;
103
+ if (options.maxDelayMs !== void 0) this.retryMaxDelayMs = options.maxDelayMs;
104
+ if (options.factor !== void 0) this.retryFactor = options.factor;
105
+ }
106
+ async request(method, path, params, mode) {
107
+ const url = this.buildUrl(path, params, mode);
108
+ const config = { method, url, headers: this.buildHeaders(mode) };
109
+ return this.limiter.schedule(() => this.requestWithRetry(config, 0));
110
+ }
111
+ buildHeaders(mode) {
112
+ const headers = {
113
+ "Content-Type": "application/x-www-form-urlencoded",
114
+ "User-Agent": "binance-sdk"
115
+ };
116
+ if (mode !== "public") headers["X-MBX-APIKEY"] = this.requireApiKey();
117
+ return headers;
118
+ }
119
+ buildUrl(path, params, mode) {
120
+ if (mode !== "signed") return this.appendQuery(path, params);
121
+ const requestData = { ...params, timestamp: Date.now(), recvWindow: this.recvWindow };
122
+ const queryString = this.buildQueryString(requestData);
123
+ const signature = this.sign(queryString);
124
+ return `${path}?${queryString}&signature=${signature}`;
125
+ }
126
+ appendQuery(path, params) {
127
+ const queryString = this.buildQueryString(params);
128
+ return queryString ? `${path}?${queryString}` : path;
129
+ }
130
+ buildQueryString(params) {
131
+ if (!params) return "";
132
+ return Object.entries(params).filter(([, val]) => val !== void 0 && val !== null).map(([key, val]) => `${key}=${encodeURIComponent(String(val))}`).join("&");
133
+ }
134
+ sign(queryString) {
135
+ if (!this.apiSecret) throw new BinanceAuthError("apiSecret");
136
+ return createHmac("sha256", this.apiSecret).update(queryString).digest("hex");
137
+ }
138
+ requireApiKey() {
139
+ if (!this.apiKey) throw new BinanceAuthError("apiKey");
140
+ return this.apiKey;
141
+ }
142
+ async requestWithRetry(config, attempt) {
143
+ this.pendingRequests += 1;
144
+ try {
145
+ const res = await this.axios.request(config);
146
+ return res.data;
147
+ } catch (err) {
148
+ if (!axios.isAxiosError(err)) {
149
+ throw new NetworkError("Unexpected error calling Binance API", err);
150
+ }
151
+ const status = err.response?.status;
152
+ const body = err.response?.data;
153
+ if (attempt < this.maxRetries && this.shouldRetry(err)) {
154
+ await sleep(this.calculateDelay(attempt));
155
+ return this.requestWithRetry(config, attempt + 1);
156
+ }
157
+ if (status === 429 || status === 418) {
158
+ throw new RateLimitError(body?.msg ?? "Binance rate limit exceeded", body?.code ?? -1, status);
159
+ }
160
+ if (status && body) {
161
+ throw new BinanceApiError(body.msg ?? "Binance API error", body.code ?? -1, status);
162
+ }
163
+ throw new NetworkError(err.message, err);
164
+ } finally {
165
+ this.pendingRequests -= 1;
166
+ }
167
+ }
168
+ shouldRetry(err) {
169
+ if (err.response?.status !== void 0) return RETRYABLE_STATUS.has(err.response.status);
170
+ return true;
171
+ }
172
+ calculateDelay(attempt) {
173
+ const delay = Math.min(this.retryBaseDelayMs * this.retryFactor ** attempt, this.retryMaxDelayMs);
174
+ const jitter = delay * 0.1 * (Math.random() * 2 - 1);
175
+ return Math.max(0, delay + jitter);
176
+ }
177
+ };
178
+
179
+ // src/client/endpoints.ts
180
+ function resolveEnvironment(options) {
181
+ const env = options?.demo ? "demo" : options?.testnet ? "testnet" : "live";
182
+ const restHost = options?.apiBase ?? (env === "demo" ? "https://demo-fapi.binance.com" : env === "testnet" ? "https://testnet.binancefuture.com" : "https://fapi.binance.com");
183
+ const wsMarketHost = options?.wsBase ?? (env === "demo" ? "wss://demo-fstream.binance.com/stream" : env === "testnet" ? "wss://fstream.binancefuture.com/stream" : "wss://fstream.binance.com/stream");
184
+ const wsUserHost = options?.wsUserBase ?? (env === "demo" ? "wss://demo-fstream.binance.com/ws" : env === "testnet" ? "wss://fstream.binancefuture.com/ws" : "wss://fstream.binance.com/ws");
185
+ const wsApiHost = options?.wsApiBase ?? (env === "demo" ? "wss://demo-fapi.binance.com/ws-fapi/v1" : "wss://ws-fapi.binance.com/ws-fapi/v1");
186
+ const restSpotHost = options?.apiBase ?? (env === "testnet" ? "https://testnet.binance.vision" : "https://api.binance.com");
187
+ const wsSpotMarketHost = options?.wsBase ?? (env === "testnet" ? "wss://testnet.binance.vision/stream" : "wss://stream.binance.com:9443/stream");
188
+ const wsSpotUserHost = options?.wsUserBase ?? (env === "testnet" ? "wss://testnet.binance.vision/ws" : "wss://stream.binance.com:9443/ws");
189
+ return {
190
+ env,
191
+ endpoints: {
192
+ restRoot: restHost,
193
+ restFapi: `${restHost}/fapi/v1`,
194
+ restFuturesData: `${restHost}/futures/data`,
195
+ restSpot: `${restSpotHost}/api/v3`,
196
+ wsMarket: wsMarketHost,
197
+ wsUser: wsUserHost,
198
+ wsApi: wsApiHost,
199
+ wsSpotMarket: wsSpotMarketHost,
200
+ wsSpotUser: wsSpotUserHost
201
+ }
202
+ };
203
+ }
204
+
205
+ // src/types/futures.types.ts
206
+ import { z } from "zod";
207
+ var FundingRateSchema = z.object({
208
+ symbol: z.string(),
209
+ fundingTime: z.number(),
210
+ fundingRate: z.string().transform(Number),
211
+ markPrice: z.string().transform(Number).optional()
212
+ });
213
+ var FundingRateHistorySchema = z.array(FundingRateSchema);
214
+ var PremiumIndexSchema = z.object({
215
+ symbol: z.string(),
216
+ markPrice: z.string().transform(Number),
217
+ indexPrice: z.string().transform(Number),
218
+ estimatedSettlePrice: z.string().transform(Number),
219
+ lastFundingRate: z.string().transform(Number),
220
+ nextFundingTime: z.number(),
221
+ interestRate: z.string().transform(Number),
222
+ time: z.number()
223
+ });
224
+ var OpenInterestSchema = z.object({
225
+ symbol: z.string(),
226
+ openInterest: z.string().transform(Number),
227
+ time: z.number()
228
+ });
229
+ var OpenInterestHistEntrySchema = z.object({
230
+ symbol: z.string(),
231
+ sumOpenInterest: z.string().transform(Number),
232
+ sumOpenInterestValue: z.string().transform(Number),
233
+ timestamp: z.number()
234
+ });
235
+ var OpenInterestHistSchema = z.array(OpenInterestHistEntrySchema);
236
+ var LongShortRatioEntrySchema = z.object({
237
+ symbol: z.string(),
238
+ longShortRatio: z.string().transform(Number),
239
+ longAccount: z.string().transform(Number),
240
+ shortAccount: z.string().transform(Number),
241
+ timestamp: z.number()
242
+ });
243
+ var LongShortRatioSchema = z.array(LongShortRatioEntrySchema);
244
+ var TakerLongShortRatioEntrySchema = z.object({
245
+ buySellRatio: z.string().transform(Number),
246
+ buyVol: z.string().transform(Number),
247
+ sellVol: z.string().transform(Number),
248
+ timestamp: z.number()
249
+ });
250
+ var TakerLongShortRatioSchema = z.array(TakerLongShortRatioEntrySchema);
251
+
252
+ // src/resources/FuturesData.ts
253
+ var FuturesData = class {
254
+ constructor(endpoints) {
255
+ const { restFapi, restFuturesData, ...httpOptions } = endpoints ?? {};
256
+ this.http = new HttpClient({ baseURL: restFapi ?? "https://fapi.binance.com/fapi/v1", ...httpOptions });
257
+ this.dataHttp = new HttpClient({
258
+ baseURL: restFuturesData ?? "https://fapi.binance.com/futures/data",
259
+ ...httpOptions
260
+ });
261
+ }
262
+ async fundingRateHistory(symbol3, options) {
263
+ const data = await this.http.get("/fundingRate", {
264
+ symbol: symbol3,
265
+ startTime: options?.startTime,
266
+ endTime: options?.endTime,
267
+ limit: options?.limit ?? 100
268
+ });
269
+ return FundingRateHistorySchema.parse(data);
270
+ }
271
+ async premiumIndex(symbol3) {
272
+ return PremiumIndexSchema.parse(await this.http.get("/premiumIndex", { symbol: symbol3 }));
273
+ }
274
+ async openInterest(symbol3) {
275
+ return OpenInterestSchema.parse(await this.http.get("/openInterest", { symbol: symbol3 }));
276
+ }
277
+ async openInterestHist(symbol3, period2, limit4 = 30) {
278
+ const data = await this.dataHttp.get("/openInterestHist", { symbol: symbol3, period: period2, limit: limit4 });
279
+ return OpenInterestHistSchema.parse(data);
280
+ }
281
+ async topLongShortAccountRatio(symbol3, period2, limit4 = 30) {
282
+ const data = await this.dataHttp.get("/topLongShortAccountRatio", { symbol: symbol3, period: period2, limit: limit4 });
283
+ return LongShortRatioSchema.parse(data);
284
+ }
285
+ async topLongShortPositionRatio(symbol3, period2, limit4 = 30) {
286
+ const data = await this.dataHttp.get("/topLongShortPositionRatio", { symbol: symbol3, period: period2, limit: limit4 });
287
+ return LongShortRatioSchema.parse(data);
288
+ }
289
+ async globalLongShortAccountRatio(symbol3, period2, limit4 = 30) {
290
+ const data = await this.dataHttp.get("/globalLongShortAccountRatio", { symbol: symbol3, period: period2, limit: limit4 });
291
+ return LongShortRatioSchema.parse(data);
292
+ }
293
+ async takerLongShortRatio(symbol3, period2, limit4 = 30) {
294
+ const data = await this.dataHttp.get("/takerlongshortRatio", { symbol: symbol3, period: period2, limit: limit4 });
295
+ return TakerLongShortRatioSchema.parse(data);
296
+ }
297
+ async basis(symbol3, period2, options) {
298
+ return this.dataHttp.get("/basis", {
299
+ symbol: symbol3,
300
+ period: period2,
301
+ startTime: options?.startTime,
302
+ endTime: options?.endTime,
303
+ limit: options?.limit
304
+ });
305
+ }
306
+ async fundingInfo() {
307
+ return this.http.get("/fundingInfo");
308
+ }
309
+ async assetIndex(symbol3) {
310
+ const params = symbol3 ? { symbol: symbol3 } : {};
311
+ return this.http.get("/assetIndex", params);
312
+ }
313
+ async compositeIndexInfo(symbol3) {
314
+ const params = symbol3 ? { symbol: symbol3 } : {};
315
+ return this.http.get("/indexInfo", params);
316
+ }
317
+ async adlQuantile(symbol3) {
318
+ const params = symbol3 ? { symbol: symbol3 } : {};
319
+ return this.http.get("/adlQuantile", params, "signed");
320
+ }
321
+ async blvtInfo(tokenName, interval, options) {
322
+ return this.http.get("/lvtKlines", {
323
+ tokenName,
324
+ interval,
325
+ startTime: options?.startTime,
326
+ endTime: options?.endTime,
327
+ limit: options?.limit
328
+ });
329
+ }
330
+ async indexPriceConstituents(symbol3) {
331
+ return this.http.get("/constituents", { symbol: symbol3 });
332
+ }
333
+ async symbolConfig(symbol3) {
334
+ const params = symbol3 ? { symbol: symbol3 } : {};
335
+ return this.http.get("/symbolConfig", params, "signed");
336
+ }
337
+ async forceOrders(options) {
338
+ return this.http.get(
339
+ "/forceOrders",
340
+ {
341
+ symbol: options?.symbol,
342
+ autoCloseType: options?.autoCloseType,
343
+ startTime: options?.startTime,
344
+ endTime: options?.endTime,
345
+ limit: options?.limit
346
+ },
347
+ "signed"
348
+ );
349
+ }
350
+ async insuranceFundBalance(options) {
351
+ return this.dataHttp.get("/insuranceBalance", {
352
+ symbol: options?.symbol,
353
+ startTime: options?.startTime,
354
+ endTime: options?.endTime,
355
+ limit: options?.limit
356
+ });
357
+ }
358
+ async pmExchangeInfo() {
359
+ return this.http.get("/pmExchangeInfo");
360
+ }
361
+ async delistSchedule(symbol3) {
362
+ const params = symbol3 ? { symbol: symbol3 } : {};
363
+ return this.http.get("/delistSchedule", params);
364
+ }
365
+ async symbolAdlRisk(symbol3) {
366
+ const params = symbol3 ? { symbol: symbol3 } : {};
367
+ return this.http.get("/symbolAdlRisk", params, "signed");
368
+ }
369
+ async deliveryPrice(pair) {
370
+ return this.dataHttp.get("/delivery-price", { pair });
371
+ }
372
+ };
373
+
374
+ // src/types/market.types.ts
375
+ import { z as z2 } from "zod";
376
+ var KlineIntervalSchema = z2.enum([
377
+ "1m",
378
+ "3m",
379
+ "5m",
380
+ "15m",
381
+ "30m",
382
+ "1h",
383
+ "2h",
384
+ "4h",
385
+ "6h",
386
+ "8h",
387
+ "12h",
388
+ "1d",
389
+ "3d",
390
+ "1w",
391
+ "1M"
392
+ ]);
393
+ var RawKlineSchema = z2.tuple([
394
+ z2.number(),
395
+ z2.string(),
396
+ z2.string(),
397
+ z2.string(),
398
+ z2.string(),
399
+ z2.string(),
400
+ z2.number(),
401
+ z2.string(),
402
+ z2.number(),
403
+ z2.string(),
404
+ z2.string(),
405
+ z2.string()
406
+ ]);
407
+ var KlineSchema = RawKlineSchema.transform(
408
+ ([openTime, open, high, low, close, volume, closeTime, quoteAssetVolume, trades, takerBuyBaseVolume, takerBuyQuoteVolume]) => ({
409
+ openTime,
410
+ open: Number(open),
411
+ high: Number(high),
412
+ low: Number(low),
413
+ close: Number(close),
414
+ volume: Number(volume),
415
+ closeTime,
416
+ quoteAssetVolume: Number(quoteAssetVolume),
417
+ trades,
418
+ takerBuyBaseVolume: Number(takerBuyBaseVolume),
419
+ takerBuyQuoteVolume: Number(takerBuyQuoteVolume)
420
+ })
421
+ );
422
+ var KlinesResponseSchema = z2.array(KlineSchema);
423
+ var TickerPriceSchema = z2.object({
424
+ symbol: z2.string(),
425
+ price: z2.string().transform(Number),
426
+ time: z2.number().optional()
427
+ });
428
+ var Ticker24hrSchema = z2.object({
429
+ symbol: z2.string(),
430
+ priceChange: z2.string().transform(Number),
431
+ priceChangePercent: z2.string().transform(Number),
432
+ weightedAvgPrice: z2.string().transform(Number),
433
+ lastPrice: z2.string().transform(Number),
434
+ lastQty: z2.string().transform(Number),
435
+ openPrice: z2.string().transform(Number),
436
+ highPrice: z2.string().transform(Number),
437
+ lowPrice: z2.string().transform(Number),
438
+ volume: z2.string().transform(Number),
439
+ quoteVolume: z2.string().transform(Number),
440
+ openTime: z2.number(),
441
+ closeTime: z2.number(),
442
+ firstId: z2.number(),
443
+ lastId: z2.number(),
444
+ count: z2.number()
445
+ });
446
+ var BookTickerSchema = z2.object({
447
+ symbol: z2.string(),
448
+ bidPrice: z2.string().transform(Number),
449
+ bidQty: z2.string().transform(Number),
450
+ askPrice: z2.string().transform(Number),
451
+ askQty: z2.string().transform(Number),
452
+ time: z2.number().optional()
453
+ });
454
+ var DepthLevelSchema = z2.tuple([z2.string(), z2.string()]).transform(([price, qty]) => ({ price: Number(price), qty: Number(qty) }));
455
+ var DepthSnapshotSchema = z2.object({
456
+ lastUpdateId: z2.number(),
457
+ bids: z2.array(DepthLevelSchema),
458
+ asks: z2.array(DepthLevelSchema)
459
+ });
460
+ var TradeSchema = z2.object({
461
+ id: z2.number(),
462
+ price: z2.string().transform(Number),
463
+ qty: z2.string().transform(Number),
464
+ quoteQty: z2.string().transform(Number),
465
+ time: z2.number(),
466
+ isBuyerMaker: z2.boolean()
467
+ });
468
+ var TradesResponseSchema = z2.array(TradeSchema);
469
+ var AggTradeSchema = z2.object({
470
+ a: z2.number(),
471
+ p: z2.string().transform(Number),
472
+ q: z2.string().transform(Number),
473
+ f: z2.number(),
474
+ l: z2.number(),
475
+ T: z2.number(),
476
+ m: z2.boolean()
477
+ });
478
+ var AggTradesResponseSchema = z2.array(AggTradeSchema);
479
+ var AvgPriceSchema = z2.object({
480
+ mins: z2.number(),
481
+ price: z2.string().transform(Number)
482
+ });
483
+ var ExchangeSymbolSchema = z2.object({
484
+ symbol: z2.string(),
485
+ status: z2.string(),
486
+ baseAsset: z2.string(),
487
+ quoteAsset: z2.string()
488
+ }).loose();
489
+ var ExchangeInfoSchema = z2.object({
490
+ timezone: z2.string(),
491
+ serverTime: z2.number(),
492
+ symbols: z2.array(ExchangeSymbolSchema)
493
+ }).loose();
494
+
495
+ // src/resources/MarketDataBase.ts
496
+ var MarketDataBase = class {
497
+ constructor(http) {
498
+ this.http = http;
499
+ }
500
+ async exchangeInfo() {
501
+ return ExchangeInfoSchema.parse(await this.http.get("/exchangeInfo"));
502
+ }
503
+ async ping() {
504
+ return this.http.get("/ping");
505
+ }
506
+ async serverTime() {
507
+ return this.http.get("/time");
508
+ }
509
+ async historicalTrades(symbol3, options) {
510
+ const data = await this.http.get("/historicalTrades", {
511
+ symbol: symbol3,
512
+ fromId: options?.fromId,
513
+ limit: options?.limit ?? 500
514
+ });
515
+ return TradesResponseSchema.parse(data);
516
+ }
517
+ async klines(symbol3, interval, options) {
518
+ const data = await this.http.get("/klines", {
519
+ symbol: symbol3,
520
+ interval,
521
+ startTime: options?.startTime,
522
+ endTime: options?.endTime,
523
+ limit: options?.limit ?? 500
524
+ });
525
+ return KlinesResponseSchema.parse(data);
526
+ }
527
+ async tickerPrice(symbol3) {
528
+ return TickerPriceSchema.parse(await this.http.get("/ticker/price", { symbol: symbol3 }));
529
+ }
530
+ async ticker24hr(symbol3) {
531
+ return Ticker24hrSchema.parse(await this.http.get("/ticker/24hr", { symbol: symbol3 }));
532
+ }
533
+ async bookTicker(symbol3) {
534
+ return BookTickerSchema.parse(await this.http.get("/ticker/bookTicker", { symbol: symbol3 }));
535
+ }
536
+ async depth(symbol3, limit4 = 100) {
537
+ return DepthSnapshotSchema.parse(await this.http.get("/depth", { symbol: symbol3, limit: limit4 }));
538
+ }
539
+ async trades(symbol3, limit4 = 500) {
540
+ return TradesResponseSchema.parse(await this.http.get("/trades", { symbol: symbol3, limit: limit4 }));
541
+ }
542
+ async aggTrades(symbol3, options) {
543
+ const data = await this.http.get("/aggTrades", {
544
+ symbol: symbol3,
545
+ startTime: options?.startTime,
546
+ endTime: options?.endTime,
547
+ limit: options?.limit ?? 500
548
+ });
549
+ return AggTradesResponseSchema.parse(data);
550
+ }
551
+ };
552
+
553
+ // src/resources/FuturesMarket.ts
554
+ var FuturesMarket = class extends MarketDataBase {
555
+ constructor(http, restRoot) {
556
+ super(http ?? new HttpClient({ baseURL: "https://fapi.binance.com/fapi/v1" }));
557
+ this.rootHttp = new HttpClient({ baseURL: restRoot ?? "https://fapi.binance.com" });
558
+ }
559
+ async continuousKlines(pair, contractType2, interval, options) {
560
+ const data = await this.http.get("/continuousKlines", {
561
+ pair,
562
+ contractType: contractType2,
563
+ interval,
564
+ startTime: options?.startTime,
565
+ endTime: options?.endTime,
566
+ limit: options?.limit ?? 500
567
+ });
568
+ return KlinesResponseSchema.parse(data);
569
+ }
570
+ async indexPriceKlines(pair, interval, options) {
571
+ const data = await this.http.get("/indexPriceKlines", {
572
+ pair,
573
+ interval,
574
+ startTime: options?.startTime,
575
+ endTime: options?.endTime,
576
+ limit: options?.limit ?? 500
577
+ });
578
+ return KlinesResponseSchema.parse(data);
579
+ }
580
+ async markPriceKlines(symbol3, interval, options) {
581
+ const data = await this.http.get("/markPriceKlines", {
582
+ symbol: symbol3,
583
+ interval,
584
+ startTime: options?.startTime,
585
+ endTime: options?.endTime,
586
+ limit: options?.limit ?? 500
587
+ });
588
+ return KlinesResponseSchema.parse(data);
589
+ }
590
+ async premiumIndexKlines(symbol3, interval, options) {
591
+ const data = await this.http.get("/premiumIndexKlines", {
592
+ symbol: symbol3,
593
+ interval,
594
+ startTime: options?.startTime,
595
+ endTime: options?.endTime,
596
+ limit: options?.limit ?? 500
597
+ });
598
+ return KlinesResponseSchema.parse(data);
599
+ }
600
+ async rpiDepth(symbol3, limit4 = 1e3) {
601
+ return DepthSnapshotSchema.parse(await this.http.get("/rpiDepth", { symbol: symbol3, limit: limit4 }));
602
+ }
603
+ async tradingDayTicker(symbol3) {
604
+ const params = symbol3 ? { symbol: symbol3 } : {};
605
+ return this.http.get("/tradingDayTicker", params);
606
+ }
607
+ async tickerPriceV2(symbol3) {
608
+ const params = symbol3 ? { symbol: symbol3 } : {};
609
+ return TickerPriceSchema.parse(await this.rootHttp.get("/fapi/v2/ticker/price", params));
610
+ }
611
+ async bookTickerV2(symbol3) {
612
+ const params = symbol3 ? { symbol: symbol3 } : {};
613
+ return BookTickerSchema.parse(await this.rootHttp.get("/fapi/v2/ticker/bookTicker", params));
614
+ }
615
+ async instrumentDetails(symbol3) {
616
+ const info = await ExchangeInfoSchema.parse(await this.http.get("/exchangeInfo"));
617
+ const details = info.symbols.find((s) => s.symbol === symbol3);
618
+ if (!details) throw new Error(`Instrument ${symbol3} not found`);
619
+ return details;
620
+ }
621
+ };
622
+
623
+ // src/resources/SpotMarket.ts
624
+ var SpotMarket = class extends MarketDataBase {
625
+ constructor(http) {
626
+ super(http ?? new HttpClient({ baseURL: "https://api.binance.com/api/v3" }));
627
+ }
628
+ async avgPrice(symbol3) {
629
+ return AvgPriceSchema.parse(await this.http.get("/avgPrice", { symbol: symbol3 }));
630
+ }
631
+ /** Optimized klines endpoint (`/uiKlines`) with the same response shape as `/klines`. */
632
+ async uiKlines(symbol3, interval, options) {
633
+ const data = await this.http.get("/uiKlines", {
634
+ symbol: symbol3,
635
+ interval,
636
+ startTime: options?.startTime,
637
+ endTime: options?.endTime,
638
+ limit: options?.limit ?? 500
639
+ });
640
+ return KlinesResponseSchema.parse(data);
641
+ }
642
+ /** Rolling window ticker (`/ticker`). Omit `symbol` for all symbols. */
643
+ async rollingWindowTicker(symbol3, windowSize) {
644
+ const params = { symbol: symbol3, windowSize };
645
+ return this.http.get("/ticker", params);
646
+ }
647
+ /** Trading-day ticker (`/ticker/tradingDay`). Omit `symbol` for all symbols. */
648
+ async tradingDayTicker(symbol3, options) {
649
+ return this.http.get("/ticker/tradingDay", { symbol: symbol3, ...options });
650
+ }
651
+ };
652
+
653
+ // src/types/spot.types.ts
654
+ import { z as z3 } from "zod";
655
+ var SpotOrderSchema = z3.object({
656
+ symbol: z3.string(),
657
+ orderId: z3.number(),
658
+ clientOrderId: z3.string(),
659
+ status: z3.string(),
660
+ side: z3.string(),
661
+ type: z3.string(),
662
+ timeInForce: z3.string().optional(),
663
+ price: z3.string().transform(Number).optional(),
664
+ origQty: z3.string().transform(Number).optional(),
665
+ executedQty: z3.string().transform(Number).optional(),
666
+ cummulativeQuoteQty: z3.string().transform(Number).optional(),
667
+ stopPrice: z3.string().transform(Number).optional(),
668
+ icebergQty: z3.string().transform(Number).optional(),
669
+ originalQuoteOrderQty: z3.string().transform(Number).optional(),
670
+ transactTime: z3.number().optional(),
671
+ workingTime: z3.number().optional(),
672
+ orderListId: z3.number().optional(),
673
+ selfTradePreventionMode: z3.string().optional(),
674
+ fills: z3.array(z3.unknown()).optional()
675
+ }).loose();
676
+ var SpotOrderResponseSchema = SpotOrderSchema;
677
+ var SpotOrdersResponseSchema = z3.array(SpotOrderSchema);
678
+ var SpotOCOOrderAckSchema = z3.object({
679
+ orderListId: z3.number(),
680
+ contingencyType: z3.string(),
681
+ listStatusType: z3.string(),
682
+ listOrderStatus: z3.string(),
683
+ listClientOrderId: z3.string(),
684
+ transactionTime: z3.number(),
685
+ symbol: z3.string(),
686
+ orders: z3.array(SpotOrderSchema),
687
+ orderReports: z3.array(z3.unknown()).optional()
688
+ }).loose();
689
+ var SpotOCOOrderAckResponseSchema = SpotOCOOrderAckSchema;
690
+ var SpotOCOOrdersResponseSchema = z3.array(SpotOCOOrderAckSchema);
691
+ var SpotTradeSchema = z3.object({
692
+ symbol: z3.string(),
693
+ id: z3.number(),
694
+ orderId: z3.number(),
695
+ orderListId: z3.number().optional(),
696
+ price: z3.string().transform(Number),
697
+ qty: z3.string().transform(Number),
698
+ quoteQty: z3.string().transform(Number),
699
+ commission: z3.string().transform(Number),
700
+ commissionAsset: z3.string(),
701
+ time: z3.number(),
702
+ isBuyer: z3.boolean(),
703
+ isMaker: z3.boolean(),
704
+ isBestMatch: z3.boolean()
705
+ }).loose();
706
+ var SpotTradesResponseSchema = z3.array(SpotTradeSchema);
707
+ var SpotBalanceSchema = z3.object({
708
+ asset: z3.string(),
709
+ free: z3.string().transform(Number),
710
+ locked: z3.string().transform(Number)
711
+ });
712
+ var SpotAccountSchema = z3.object({
713
+ makerCommission: z3.number(),
714
+ takerCommission: z3.number(),
715
+ buyerCommission: z3.number(),
716
+ sellerCommission: z3.number(),
717
+ canTrade: z3.boolean(),
718
+ canWithdraw: z3.boolean(),
719
+ canDeposit: z3.boolean(),
720
+ updateTime: z3.number().optional(),
721
+ accountType: z3.string().optional(),
722
+ balances: z3.array(SpotBalanceSchema),
723
+ permissions: z3.array(z3.string()).optional()
724
+ }).loose();
725
+ var SpotExecutionReportSchema = z3.object({ e: z3.literal("executionReport") }).loose();
726
+ var SpotOutboundAccountPositionSchema = z3.object({ e: z3.literal("outboundAccountPosition") }).loose();
727
+ var SpotBalanceUpdateSchema = z3.object({ e: z3.literal("balanceUpdate") }).loose();
728
+ var SpotListStatusSchema = z3.object({ e: z3.literal("listStatus") }).loose();
729
+ function parseSpotUserDataEvent(raw) {
730
+ if (typeof raw !== "object" || raw === null) {
731
+ throw new Error("Invalid spot user-data payload");
732
+ }
733
+ const eventType = raw.e;
734
+ if (eventType === "executionReport") return SpotExecutionReportSchema.parse(raw);
735
+ if (eventType === "outboundAccountPosition") return SpotOutboundAccountPositionSchema.parse(raw);
736
+ if (eventType === "balanceUpdate") return SpotBalanceUpdateSchema.parse(raw);
737
+ if (eventType === "listStatus") return SpotListStatusSchema.parse(raw);
738
+ throw new Error(`Unknown spot user-data event type: ${eventType}`);
739
+ }
740
+
741
+ // src/resources/SpotTrading.ts
742
+ var SpotTrading = class {
743
+ constructor(http) {
744
+ this.http = http;
745
+ }
746
+ async createOrder(params) {
747
+ return SpotOrderSchema.parse(await this.http.post("/order", params, "signed"));
748
+ }
749
+ async testOrder(params) {
750
+ return this.http.post("/order/test", params, "signed");
751
+ }
752
+ async getOrder(symbol3, options) {
753
+ return SpotOrderSchema.parse(
754
+ await this.http.get("/order", { symbol: symbol3, ...options }, "signed")
755
+ );
756
+ }
757
+ async cancelOrder(symbol3, options) {
758
+ return SpotOrderSchema.parse(
759
+ await this.http.delete("/order", { symbol: symbol3, ...options }, "signed")
760
+ );
761
+ }
762
+ async cancelOpenOrders(symbol3) {
763
+ return SpotOrdersResponseSchema.parse(
764
+ await this.http.delete("/openOrders", { symbol: symbol3 }, "signed")
765
+ );
766
+ }
767
+ async getOpenOrders(symbol3) {
768
+ const params = symbol3 ? { symbol: symbol3 } : {};
769
+ return SpotOrdersResponseSchema.parse(await this.http.get("/openOrders", params, "signed"));
770
+ }
771
+ async getAllOrders(symbol3, options) {
772
+ return SpotOrdersResponseSchema.parse(
773
+ await this.http.get("/allOrders", { symbol: symbol3, ...options }, "signed")
774
+ );
775
+ }
776
+ async cancelReplaceOrder(params) {
777
+ return this.http.post("/order/cancelReplace", params, "signed");
778
+ }
779
+ // ---- OCO order lists ----
780
+ async createOCOOrder(params) {
781
+ return SpotOCOOrderAckResponseSchema.parse(
782
+ await this.http.post("/orderList", params, "signed")
783
+ );
784
+ }
785
+ async cancelOCOOrder(symbol3, options) {
786
+ return SpotOCOOrderAckResponseSchema.parse(
787
+ await this.http.delete("/orderList", { symbol: symbol3, ...options }, "signed")
788
+ );
789
+ }
790
+ async getOCOOrder(symbol3, options) {
791
+ return SpotOCOOrderAckResponseSchema.parse(
792
+ await this.http.get("/orderList", { symbol: symbol3, ...options }, "signed")
793
+ );
794
+ }
795
+ async getOpenOCOOrders() {
796
+ return SpotOCOOrdersResponseSchema.parse(
797
+ await this.http.get("/openOrderLists", void 0, "signed")
798
+ );
799
+ }
800
+ async getAllOCOOrders(symbol3, options) {
801
+ return SpotOCOOrdersResponseSchema.parse(
802
+ await this.http.get("/allOrderLists", { symbol: symbol3, ...options }, "signed")
803
+ );
804
+ }
805
+ async cancelOpenOCOOrders(symbol3) {
806
+ return SpotOCOOrdersResponseSchema.parse(
807
+ await this.http.delete("/openOrderLists", { symbol: symbol3 }, "signed")
808
+ );
809
+ }
810
+ };
811
+ var SpotAccount = class {
812
+ constructor(http) {
813
+ this.http = http;
814
+ }
815
+ async account() {
816
+ return SpotAccountSchema.parse(await this.http.get("/account", void 0, "signed"));
817
+ }
818
+ async myTrades(symbol3, options) {
819
+ return SpotTradesResponseSchema.parse(
820
+ await this.http.get("/myTrades", { symbol: symbol3, ...options }, "signed")
821
+ );
822
+ }
823
+ async myPreventedMatches(symbol3, options) {
824
+ return this.http.get("/myPreventedMatches", { symbol: symbol3, ...options }, "signed");
825
+ }
826
+ async accountCommission(symbol3) {
827
+ return this.http.get("/account/commission", { symbol: symbol3 }, "signed");
828
+ }
829
+ async rateLimitOrder() {
830
+ return this.http.get("/rateLimit/order", void 0, "signed");
831
+ }
832
+ };
833
+
834
+ // src/types/userdata.types.ts
835
+ import { z as z4 } from "zod";
836
+ var ListenKeySchema = z4.object({
837
+ listenKey: z4.string()
838
+ });
839
+ var UserDataAccountUpdateSchema = z4.object({
840
+ e: z4.literal("ACCOUNT_UPDATE"),
841
+ E: z4.number(),
842
+ T: z4.number(),
843
+ a: z4.object({
844
+ m: z4.string().transform(Number),
845
+ B: z4.array(
846
+ z4.object({
847
+ a: z4.string(),
848
+ wb: z4.string().transform(Number),
849
+ cw: z4.string().transform(Number),
850
+ bc: z4.string().transform(Number)
851
+ })
852
+ ),
853
+ P: z4.array(
854
+ z4.object({
855
+ s: z4.string(),
856
+ pa: z4.string().transform(Number),
857
+ ep: z4.string().transform(Number),
858
+ cr: z4.string().transform(Number),
859
+ up: z4.string().transform(Number),
860
+ mt: z4.string(),
861
+ iw: z4.string().transform(Number).optional(),
862
+ ps: z4.string()
863
+ })
864
+ )
865
+ })
866
+ });
867
+ var UserDataOrderTradeUpdateSchema = z4.object({
868
+ e: z4.literal("ORDER_TRADE_UPDATE"),
869
+ E: z4.number(),
870
+ T: z4.number(),
871
+ o: z4.object({
872
+ s: z4.string(),
873
+ c: z4.string(),
874
+ i: z4.number(),
875
+ S: z4.string(),
876
+ o: z4.string(),
877
+ f: z4.string(),
878
+ q: z4.string().transform(Number),
879
+ p: z4.string().transform(Number),
880
+ ap: z4.string().transform(Number),
881
+ sp: z4.string().transform(Number),
882
+ x: z4.string(),
883
+ X: z4.string(),
884
+ l: z4.string().transform(Number),
885
+ z: z4.string().transform(Number),
886
+ L: z4.string().transform(Number),
887
+ n: z4.string().transform(Number),
888
+ N: z4.string(),
889
+ T: z4.number(),
890
+ t: z4.number(),
891
+ b: z4.string().transform(Number),
892
+ a: z4.string().transform(Number),
893
+ m: z4.boolean(),
894
+ R: z4.boolean(),
895
+ wt: z4.string(),
896
+ ot: z4.string(),
897
+ ps: z4.string(),
898
+ cp: z4.boolean().optional(),
899
+ AP: z4.string().transform(Number).optional(),
900
+ cr: z4.string().transform(Number).optional(),
901
+ rp: z4.string().transform(Number).optional()
902
+ })
903
+ });
904
+ var UserDataMarginCallSchema = z4.object({
905
+ e: z4.literal("MARGIN_CALL"),
906
+ E: z4.number(),
907
+ cw: z4.string().transform(Number),
908
+ p: z4.array(
909
+ z4.object({
910
+ s: z4.string(),
911
+ ps: z4.string(),
912
+ pa: z4.string().transform(Number),
913
+ mt: z4.string(),
914
+ iw: z4.string().transform(Number),
915
+ mp: z4.string().transform(Number),
916
+ up: z4.string().transform(Number),
917
+ mm: z4.string().transform(Number)
918
+ })
919
+ )
920
+ });
921
+ function parseUserDataEvent(raw) {
922
+ if (typeof raw !== "object" || raw === null) {
923
+ throw new Error("Invalid user-data payload");
924
+ }
925
+ const eventType = raw.e;
926
+ if (eventType === "ACCOUNT_UPDATE") return UserDataAccountUpdateSchema.parse(raw);
927
+ if (eventType === "ORDER_TRADE_UPDATE") return UserDataOrderTradeUpdateSchema.parse(raw);
928
+ if (eventType === "MARGIN_CALL") return UserDataMarginCallSchema.parse(raw);
929
+ throw new Error(`Unknown user-data event type: ${eventType}`);
930
+ }
931
+ var WsApiResponseSchema = z4.object({
932
+ id: z4.string(),
933
+ status: z4.number(),
934
+ result: z4.unknown(),
935
+ error: z4.object({
936
+ code: z4.number(),
937
+ msg: z4.string()
938
+ }).optional()
939
+ });
940
+
941
+ // src/resources/SpotUserDataStream.ts
942
+ var SpotUserDataStream = class {
943
+ constructor(http) {
944
+ this.http = http;
945
+ }
946
+ async createListenKey() {
947
+ return ListenKeySchema.parse(await this.http.post("/userDataStream", void 0, "apiKey"));
948
+ }
949
+ async keepAliveListenKey() {
950
+ return this.http.put("/userDataStream", void 0, "apiKey");
951
+ }
952
+ async closeListenKey() {
953
+ return this.http.delete("/userDataStream", void 0, "apiKey");
954
+ }
955
+ };
956
+
957
+ // src/types/account.types.ts
958
+ import { z as z5 } from "zod";
959
+ var BalanceSchema = z5.object({
960
+ accountAlias: z5.string(),
961
+ asset: z5.string(),
962
+ balance: z5.string().transform(Number),
963
+ crossWalletBalance: z5.string().transform(Number),
964
+ crossUnPnl: z5.string().transform(Number),
965
+ availableBalance: z5.string().transform(Number),
966
+ maxWithdrawAmount: z5.string().transform(Number),
967
+ marginAvailable: z5.boolean(),
968
+ updateTime: z5.number()
969
+ });
970
+ var BalancesResponseSchema = z5.array(BalanceSchema);
971
+ var PositionRiskSchema = z5.object({
972
+ symbol: z5.string(),
973
+ positionAmt: z5.string().transform(Number),
974
+ entryPrice: z5.string().transform(Number),
975
+ markPrice: z5.string().transform(Number),
976
+ unRealizedProfit: z5.string().transform(Number),
977
+ liquidationPrice: z5.string().transform(Number),
978
+ leverage: z5.string().transform(Number),
979
+ marginType: z5.enum(["isolated", "cross"]),
980
+ positionSide: z5.string(),
981
+ isolatedWallet: z5.string().transform(Number).optional(),
982
+ isolatedMargin: z5.string().transform(Number).optional(),
983
+ notional: z5.string().transform(Number),
984
+ updateTime: z5.number().optional()
985
+ });
986
+ var PositionRiskResponseSchema = z5.array(PositionRiskSchema);
987
+ var AccountAssetSchema = z5.object({
988
+ asset: z5.string(),
989
+ walletBalance: z5.string().transform(Number),
990
+ unrealizedProfit: z5.string().transform(Number),
991
+ marginBalance: z5.string().transform(Number),
992
+ maintMargin: z5.string().transform(Number),
993
+ initialMargin: z5.string().transform(Number),
994
+ positionInitialMargin: z5.string().transform(Number),
995
+ openOrderInitialMargin: z5.string().transform(Number),
996
+ crossWalletBalance: z5.string().transform(Number),
997
+ crossUnPnl: z5.string().transform(Number),
998
+ availableBalance: z5.string().transform(Number),
999
+ maxWithdrawAmount: z5.string().transform(Number),
1000
+ marginAvailable: z5.boolean().optional(),
1001
+ updateTime: z5.number()
1002
+ });
1003
+ var AccountPositionSchema = z5.object({
1004
+ symbol: z5.string(),
1005
+ positionAmt: z5.string().transform(Number),
1006
+ initialMargin: z5.string().transform(Number),
1007
+ maintMargin: z5.string().transform(Number),
1008
+ unrealizedProfit: z5.string().transform(Number),
1009
+ positionInitialMargin: z5.string().transform(Number),
1010
+ openOrderInitialMargin: z5.string().transform(Number),
1011
+ leverage: z5.string().transform(Number),
1012
+ isolated: z5.boolean(),
1013
+ entryPrice: z5.string().transform(Number),
1014
+ positionSide: z5.string(),
1015
+ positionNotional: z5.string().transform(Number),
1016
+ isolatedWallet: z5.string().transform(Number).optional(),
1017
+ isolatedMargin: z5.string().transform(Number).optional(),
1018
+ notional: z5.string().transform(Number).optional()
1019
+ });
1020
+ var AccountSchema = z5.object({
1021
+ feeTier: z5.number(),
1022
+ canTrade: z5.boolean(),
1023
+ canDeposit: z5.boolean(),
1024
+ canWithdraw: z5.boolean(),
1025
+ updateTime: z5.number(),
1026
+ totalInitialMargin: z5.string().transform(Number),
1027
+ totalMaintMargin: z5.string().transform(Number),
1028
+ totalWalletBalance: z5.string().transform(Number),
1029
+ totalUnrealizedProfit: z5.string().transform(Number),
1030
+ totalMarginBalance: z5.string().transform(Number),
1031
+ totalPositionInitialMargin: z5.string().transform(Number),
1032
+ totalOpenOrderInitialMargin: z5.string().transform(Number),
1033
+ totalCrossWalletBalance: z5.string().transform(Number),
1034
+ totalCrossUnPnl: z5.string().transform(Number),
1035
+ availableBalance: z5.string().transform(Number),
1036
+ maxWithdrawAmount: z5.string().transform(Number),
1037
+ assets: z5.array(AccountAssetSchema),
1038
+ positions: z5.array(AccountPositionSchema)
1039
+ });
1040
+ var IncomeSchema = z5.object({
1041
+ symbol: z5.string().optional(),
1042
+ incomeType: z5.string(),
1043
+ income: z5.string().transform(Number),
1044
+ asset: z5.string(),
1045
+ time: z5.number(),
1046
+ info: z5.string().optional(),
1047
+ tranId: z5.number(),
1048
+ tradeId: z5.string().optional()
1049
+ });
1050
+ var IncomeHistoryResponseSchema = z5.array(IncomeSchema);
1051
+ var UserTradeSchema = z5.object({
1052
+ buyer: z5.boolean(),
1053
+ commission: z5.string().transform(Number),
1054
+ commissionAsset: z5.string(),
1055
+ id: z5.number(),
1056
+ maker: z5.boolean(),
1057
+ orderId: z5.number(),
1058
+ price: z5.string().transform(Number),
1059
+ qty: z5.string().transform(Number),
1060
+ quoteQty: z5.string().transform(Number),
1061
+ realizedPnl: z5.string().transform(Number),
1062
+ side: z5.string(),
1063
+ positionSide: z5.string(),
1064
+ symbol: z5.string(),
1065
+ time: z5.number()
1066
+ });
1067
+ var UserTradesResponseSchema = z5.array(UserTradeSchema);
1068
+ var CommissionRateSchema = z5.object({
1069
+ symbol: z5.string(),
1070
+ makerCommissionRate: z5.string().transform(Number),
1071
+ takerCommissionRate: z5.string().transform(Number)
1072
+ });
1073
+ var LeverageBracketSchema = z5.object({
1074
+ symbol: z5.string().optional(),
1075
+ bracketNotional: z5.string().transform(Number).optional(),
1076
+ bracket: z5.number().optional(),
1077
+ maxLeverage: z5.number().optional(),
1078
+ maintMarginRatio: z5.string().transform(Number).optional(),
1079
+ cum: z5.string().transform(Number).optional()
1080
+ });
1081
+ var LeverageBracketResponseSchema = z5.array(
1082
+ z5.object({
1083
+ symbol: z5.string(),
1084
+ brackets: z5.array(LeverageBracketSchema)
1085
+ })
1086
+ );
1087
+ var PositionModeSchema = z5.object({
1088
+ dualSidePosition: z5.boolean()
1089
+ });
1090
+ var MultiAssetsMarginSchema = z5.object({
1091
+ multiAssetsMargin: z5.boolean()
1092
+ });
1093
+ var FeeBurnStatusSchema = z5.object({
1094
+ feeBurn: z5.boolean()
1095
+ });
1096
+ var PositionMarginHistoryEntrySchema = z5.object({
1097
+ amount: z5.string().transform(Number),
1098
+ asset: z5.string(),
1099
+ symbol: z5.string(),
1100
+ time: z5.number(),
1101
+ type: z5.number(),
1102
+ positionSide: z5.string()
1103
+ });
1104
+ var PositionMarginHistorySchema = z5.array(PositionMarginHistoryEntrySchema);
1105
+ var ApiTradingStatusSchema = z5.object({
1106
+ isLocked: z5.boolean(),
1107
+ plannedRecoverTime: z5.number(),
1108
+ updateTime: z5.number(),
1109
+ triggerCondition: z5.record(z5.string(), z5.number()).optional(),
1110
+ indicators: z5.record(z5.string(), z5.unknown()).optional()
1111
+ });
1112
+ var RateLimitOrderSchema = z5.object({
1113
+ rateLimitType: z5.string(),
1114
+ interval: z5.string(),
1115
+ intervalNum: z5.number(),
1116
+ limit: z5.number(),
1117
+ count: z5.number()
1118
+ });
1119
+ var RateLimitOrderResponseSchema = z5.array(RateLimitOrderSchema);
1120
+
1121
+ // src/resources/FuturesAccount.ts
1122
+ var FuturesAccount = class {
1123
+ constructor(http) {
1124
+ this.http = http;
1125
+ }
1126
+ async balance() {
1127
+ return BalancesResponseSchema.parse(await this.http.get("/fapi/v2/balance", void 0, "signed"));
1128
+ }
1129
+ async account() {
1130
+ return AccountSchema.parse(await this.http.get("/fapi/v2/account", void 0, "signed"));
1131
+ }
1132
+ async positionRisk(symbol3) {
1133
+ const params = symbol3 ? { symbol: symbol3 } : {};
1134
+ return PositionRiskResponseSchema.parse(await this.http.get("/fapi/v2/positionRisk", params, "signed"));
1135
+ }
1136
+ async balanceV3() {
1137
+ return BalancesResponseSchema.parse(await this.http.get("/fapi/v3/balance", void 0, "signed"));
1138
+ }
1139
+ async accountV3() {
1140
+ return AccountSchema.parse(await this.http.get("/fapi/v3/account", void 0, "signed"));
1141
+ }
1142
+ async positionRiskV3(symbol3) {
1143
+ const params = symbol3 ? { symbol: symbol3 } : {};
1144
+ return PositionRiskResponseSchema.parse(await this.http.get("/fapi/v3/positionRisk", params, "signed"));
1145
+ }
1146
+ async incomeHistory(options) {
1147
+ const params = {
1148
+ symbol: options?.symbol,
1149
+ incomeType: options?.incomeType,
1150
+ startTime: options?.startTime,
1151
+ endTime: options?.endTime,
1152
+ limit: options?.limit
1153
+ };
1154
+ return IncomeHistoryResponseSchema.parse(await this.http.get("/fapi/v1/income", params, "signed"));
1155
+ }
1156
+ async userTrades(options) {
1157
+ const params = {
1158
+ symbol: options?.symbol,
1159
+ startTime: options?.startTime,
1160
+ endTime: options?.endTime,
1161
+ limit: options?.limit,
1162
+ fromId: options?.fromId
1163
+ };
1164
+ return UserTradesResponseSchema.parse(await this.http.get("/fapi/v1/userTrades", params, "signed"));
1165
+ }
1166
+ async leverageBrackets(symbol3) {
1167
+ const params = symbol3 ? { symbol: symbol3 } : {};
1168
+ return LeverageBracketResponseSchema.parse(await this.http.get("/fapi/v1/leverageBracket", params, "signed"));
1169
+ }
1170
+ async commissionRate(symbol3) {
1171
+ return CommissionRateSchema.parse(await this.http.get("/fapi/v1/commissionRate", { symbol: symbol3 }, "signed"));
1172
+ }
1173
+ async multiAssetsMargin() {
1174
+ return MultiAssetsMarginSchema.parse(await this.http.get("/fapi/v1/multiAssetsMargin", void 0, "signed"));
1175
+ }
1176
+ async setMultiAssetsMargin(multiAssetsMargin) {
1177
+ return this.http.post("/fapi/v1/multiAssetsMargin", { multiAssetsMargin }, "signed");
1178
+ }
1179
+ async feeBurnStatus() {
1180
+ return FeeBurnStatusSchema.parse(await this.http.get("/fapi/v1/feeBurn", void 0, "signed"));
1181
+ }
1182
+ async setFeeBurnStatus(feeBurn) {
1183
+ return this.http.post("/fapi/v1/feeBurn", { feeBurn }, "signed");
1184
+ }
1185
+ async positionMode() {
1186
+ return PositionModeSchema.parse(await this.http.get("/fapi/v1/positionSide/dual", void 0, "signed"));
1187
+ }
1188
+ async setPositionMode(dualSidePosition) {
1189
+ return this.http.post("/fapi/v1/positionSide/dual", { dualSidePosition }, "signed");
1190
+ }
1191
+ async apiTradingStatus() {
1192
+ return ApiTradingStatusSchema.parse(await this.http.get("/fapi/v1/apiTradingStatus", void 0, "signed"));
1193
+ }
1194
+ async positionMarginHistory(options) {
1195
+ const params = {
1196
+ symbol: options?.symbol,
1197
+ type: options?.type,
1198
+ startTime: options?.startTime,
1199
+ endTime: options?.endTime,
1200
+ limit: options?.limit
1201
+ };
1202
+ return PositionMarginHistorySchema.parse(await this.http.get("/fapi/v1/positionMargin/history", params, "signed"));
1203
+ }
1204
+ async rateLimitOrder() {
1205
+ return RateLimitOrderResponseSchema.parse(await this.http.get("/fapi/v1/rateLimit/order", void 0, "signed"));
1206
+ }
1207
+ async accountConfig() {
1208
+ return this.http.get("/fapi/v1/accountConfig", void 0, "signed");
1209
+ }
1210
+ async pmAccountInfo(asset) {
1211
+ return this.http.get("/fapi/v1/pmAccountInfo", { asset }, "signed");
1212
+ }
1213
+ async requestIncomeDownload(options) {
1214
+ return this.http.get("/fapi/v1/income/asyn", options, "signed");
1215
+ }
1216
+ async getIncomeDownloadStatus(downloadId) {
1217
+ return this.http.get("/fapi/v1/income/asyn/id", { downloadId }, "signed");
1218
+ }
1219
+ async requestOrderDownload(options) {
1220
+ const params = {
1221
+ symbol: options?.symbol,
1222
+ startTime: options?.startTime,
1223
+ endTime: options?.endTime
1224
+ };
1225
+ return this.http.get("/fapi/v1/order/asyn", params, "signed");
1226
+ }
1227
+ async getOrderDownloadStatus(downloadId) {
1228
+ return this.http.get("/fapi/v1/order/asyn/id", { downloadId }, "signed");
1229
+ }
1230
+ async requestTradeDownload(options) {
1231
+ const params = {
1232
+ symbol: options?.symbol,
1233
+ startTime: options?.startTime,
1234
+ endTime: options?.endTime
1235
+ };
1236
+ return this.http.get("/fapi/v1/trade/asyn", params, "signed");
1237
+ }
1238
+ async getTradeDownloadStatus(downloadId) {
1239
+ return this.http.get("/fapi/v1/trade/asyn/id", { downloadId }, "signed");
1240
+ }
1241
+ };
1242
+
1243
+ // src/types/trading.types.ts
1244
+ import { z as z6 } from "zod";
1245
+ var OrderSchema = z6.object({
1246
+ orderId: z6.number(),
1247
+ symbol: z6.string(),
1248
+ status: z6.string(),
1249
+ clientOrderId: z6.string(),
1250
+ price: z6.string().transform(Number),
1251
+ avgPrice: z6.string().transform(Number),
1252
+ origQty: z6.string().transform(Number),
1253
+ executedQty: z6.string().transform(Number),
1254
+ cumQuote: z6.string().transform(Number),
1255
+ type: z6.string(),
1256
+ reduceOnly: z6.boolean(),
1257
+ side: z6.string(),
1258
+ positionSide: z6.string(),
1259
+ stopPrice: z6.string().transform(Number).optional(),
1260
+ closePosition: z6.boolean().optional(),
1261
+ timeInForce: z6.string().optional(),
1262
+ origType: z6.string().optional(),
1263
+ time: z6.number(),
1264
+ updateTime: z6.number(),
1265
+ workingType: z6.string().optional(),
1266
+ priceProtect: z6.boolean().optional()
1267
+ });
1268
+ var OrderResponseSchema = OrderSchema;
1269
+ var OrdersResponseSchema = z6.array(OrderSchema);
1270
+ var CreateOrderParamsSchema = z6.object({
1271
+ symbol: z6.string(),
1272
+ side: z6.enum(["BUY", "SELL"]),
1273
+ type: z6.string(),
1274
+ quantity: z6.union([z6.string(), z6.number()]).optional(),
1275
+ price: z6.union([z6.string(), z6.number()]).optional(),
1276
+ timeInForce: z6.enum(["GTC", "IOC", "FOK", "GTX", "GTD"]).optional(),
1277
+ stopPrice: z6.union([z6.string(), z6.number()]).optional(),
1278
+ reduceOnly: z6.boolean().optional(),
1279
+ positionSide: z6.enum(["BOTH", "LONG", "SHORT"]).optional(),
1280
+ newClientOrderId: z6.string().optional(),
1281
+ closePosition: z6.boolean().optional(),
1282
+ newOrderRespType: z6.enum(["ACK", "RESULT"]).optional(),
1283
+ priceProtect: z6.boolean().optional(),
1284
+ workingType: z6.enum(["MARK_PRICE", "CONTRACT_PRICE"]).optional(),
1285
+ callbackRate: z6.union([z6.string(), z6.number()]).optional(),
1286
+ activationPrice: z6.union([z6.string(), z6.number()]).optional(),
1287
+ selfTradePreventionMode: z6.enum(["NONE", "EXPIRE_TAKER", "EXPIRE_MAKER", "EXPIRE_BOTH"]).optional(),
1288
+ goodTillDate: z6.number().optional()
1289
+ });
1290
+ var ModifyOrderParamsSchema = CreateOrderParamsSchema.extend({
1291
+ orderId: z6.number().optional(),
1292
+ origClientOrderId: z6.string().optional(),
1293
+ origType: z6.string().optional()
1294
+ });
1295
+ var AlgoOrderSchema = z6.object({
1296
+ algoId: z6.number(),
1297
+ symbol: z6.string(),
1298
+ side: z6.string(),
1299
+ positionSide: z6.string(),
1300
+ totalQty: z6.string().transform(Number).optional(),
1301
+ executedQty: z6.string().transform(Number).optional(),
1302
+ executedAmt: z6.string().transform(Number).optional(),
1303
+ avgPrice: z6.string().transform(Number).optional(),
1304
+ clientAlgoId: z6.string().optional(),
1305
+ bookTime: z6.number().optional(),
1306
+ endTime: z6.number().optional(),
1307
+ algoStatus: z6.string().optional(),
1308
+ algoType: z6.string().optional(),
1309
+ urgency: z6.string().optional()
1310
+ });
1311
+ var AlgoOrderResponseSchema = AlgoOrderSchema;
1312
+ var AlgoOrdersResponseSchema = z6.array(AlgoOrderSchema);
1313
+ var BatchOrdersItemSchema = z6.object({
1314
+ orderId: z6.number(),
1315
+ symbol: z6.string(),
1316
+ status: z6.string(),
1317
+ clientOrderId: z6.string(),
1318
+ price: z6.string().transform(Number),
1319
+ avgPrice: z6.string().transform(Number),
1320
+ origQty: z6.string().transform(Number),
1321
+ executedQty: z6.string().transform(Number),
1322
+ cumQuote: z6.string().transform(Number),
1323
+ type: z6.string(),
1324
+ reduceOnly: z6.boolean(),
1325
+ side: z6.string(),
1326
+ positionSide: z6.string(),
1327
+ time: z6.number(),
1328
+ updateTime: z6.number()
1329
+ });
1330
+ var BatchOrdersResponseSchema = z6.array(BatchOrdersItemSchema);
1331
+ var OrderModifyHistoryEntrySchema = z6.object({
1332
+ orderId: z6.number(),
1333
+ symbol: z6.string(),
1334
+ status: z6.string(),
1335
+ clientOrderId: z6.string(),
1336
+ price: z6.string().transform(Number),
1337
+ avgPrice: z6.string().transform(Number),
1338
+ origQty: z6.string().transform(Number),
1339
+ executedQty: z6.string().transform(Number),
1340
+ cumQuote: z6.string().transform(Number),
1341
+ type: z6.string(),
1342
+ reduceOnly: z6.boolean(),
1343
+ side: z6.string(),
1344
+ positionSide: z6.string(),
1345
+ origType: z6.string().optional(),
1346
+ stopPrice: z6.string().transform(Number).optional(),
1347
+ updateTime: z6.number()
1348
+ });
1349
+ var OrderModifyHistorySchema = z6.array(OrderModifyHistoryEntrySchema);
1350
+ var LeverageChangeSchema = z6.object({
1351
+ leverage: z6.number(),
1352
+ maxNotionalValue: z6.union([z6.string(), z6.number()]),
1353
+ symbol: z6.string()
1354
+ });
1355
+ var MarginTypeChangeSchema = z6.object({
1356
+ code: z6.number(),
1357
+ msg: z6.string()
1358
+ });
1359
+ var PositionMarginChangeSchema = z6.object({
1360
+ amount: z6.number(),
1361
+ code: z6.number(),
1362
+ msg: z6.string(),
1363
+ type: z6.number()
1364
+ });
1365
+ var CountdownCancelAllSchema = z6.object({
1366
+ symbol: z6.string(),
1367
+ countdownTime: z6.number()
1368
+ });
1369
+ var NewOrderAckSchema = z6.object({
1370
+ orderId: z6.number(),
1371
+ symbol: z6.string(),
1372
+ status: z6.string(),
1373
+ clientOrderId: z6.string(),
1374
+ price: z6.string(),
1375
+ avgPrice: z6.string(),
1376
+ origQty: z6.string(),
1377
+ executedQty: z6.string(),
1378
+ cumQuote: z6.string(),
1379
+ type: z6.string(),
1380
+ reduceOnly: z6.boolean(),
1381
+ side: z6.string(),
1382
+ positionSide: z6.string(),
1383
+ timeInForce: z6.string().optional(),
1384
+ time: z6.number(),
1385
+ updateTime: z6.number(),
1386
+ workingType: z6.string().optional(),
1387
+ origType: z6.string().optional(),
1388
+ closePosition: z6.boolean().optional()
1389
+ });
1390
+
1391
+ // src/resources/FuturesTrading.ts
1392
+ var FuturesTrading = class {
1393
+ constructor(http) {
1394
+ this.http = http;
1395
+ }
1396
+ async setLeverage(symbol3, leverage) {
1397
+ return LeverageChangeSchema.parse(
1398
+ await this.http.post("/fapi/v1/leverage", { symbol: symbol3, leverage }, "signed")
1399
+ );
1400
+ }
1401
+ async createOrder(params) {
1402
+ return NewOrderAckSchema.parse(await this.http.post("/fapi/v1/order", params, "signed"));
1403
+ }
1404
+ async createTestOrder(params) {
1405
+ return this.http.post("/fapi/v1/order/test", params, "signed");
1406
+ }
1407
+ async getOrder(symbol3, options) {
1408
+ return OrderResponseSchema.parse(
1409
+ await this.http.get("/fapi/v1/order", { symbol: symbol3, ...options }, "signed")
1410
+ );
1411
+ }
1412
+ async cancelOrder(symbol3, options) {
1413
+ return OrderResponseSchema.parse(
1414
+ await this.http.delete("/fapi/v1/order", { symbol: symbol3, ...options }, "signed")
1415
+ );
1416
+ }
1417
+ async getCurrentOrder(symbol3, options) {
1418
+ return OrderResponseSchema.parse(
1419
+ await this.http.get("/fapi/v1/openOrder", { symbol: symbol3, ...options }, "signed")
1420
+ );
1421
+ }
1422
+ async getOpenOrders(symbol3) {
1423
+ const params = symbol3 ? { symbol: symbol3 } : {};
1424
+ return OrdersResponseSchema.parse(await this.http.get("/fapi/v1/openOrders", params, "signed"));
1425
+ }
1426
+ async getAllOrders(symbol3, options) {
1427
+ return OrdersResponseSchema.parse(
1428
+ await this.http.get("/fapi/v1/allOrders", { symbol: symbol3, ...options }, "signed")
1429
+ );
1430
+ }
1431
+ async cancelAllOpenOrders(symbol3) {
1432
+ return this.http.delete("/fapi/v1/allOpenOrders", { symbol: symbol3 }, "signed");
1433
+ }
1434
+ async modifyOrder(params) {
1435
+ return OrderResponseSchema.parse(await this.http.put("/fapi/v1/order", params, "signed"));
1436
+ }
1437
+ async createAlgoOrder(params) {
1438
+ return AlgoOrderResponseSchema.parse(await this.http.post("/fapi/v1/algoOrder", params, "signed"));
1439
+ }
1440
+ async cancelAlgoOrder(symbol3, options) {
1441
+ return this.http.delete("/fapi/v1/algoOrder", { symbol: symbol3, ...options }, "signed");
1442
+ }
1443
+ async cancelAllOpenAlgoOrders(symbol3) {
1444
+ return this.http.delete("/fapi/v1/algoOpenOrders", { symbol: symbol3 }, "signed");
1445
+ }
1446
+ async getAlgoOrder(symbol3, options) {
1447
+ return AlgoOrderResponseSchema.parse(
1448
+ await this.http.get("/fapi/v1/algoOrder", { symbol: symbol3, ...options }, "signed")
1449
+ );
1450
+ }
1451
+ async getOpenAlgoOrders(symbol3) {
1452
+ const params = symbol3 ? { symbol: symbol3 } : {};
1453
+ return AlgoOrdersResponseSchema.parse(await this.http.get("/fapi/v1/openAlgoOrders", params, "signed"));
1454
+ }
1455
+ async getAllAlgoOrders(symbol3, options) {
1456
+ return AlgoOrdersResponseSchema.parse(
1457
+ await this.http.get("/fapi/v1/allAlgoOrders", { symbol: symbol3, ...options }, "signed")
1458
+ );
1459
+ }
1460
+ async createBatchOrders(batchOrders) {
1461
+ return BatchOrdersResponseSchema.parse(
1462
+ await this.http.post("/fapi/v1/batchOrders", { batchOrders: JSON.stringify(batchOrders) }, "signed")
1463
+ );
1464
+ }
1465
+ async modifyBatchOrders(batchOrders) {
1466
+ return BatchOrdersResponseSchema.parse(
1467
+ await this.http.put("/fapi/v1/batchOrders", { batchOrders: JSON.stringify(batchOrders) }, "signed")
1468
+ );
1469
+ }
1470
+ async cancelBatchOrders(symbol3, options) {
1471
+ const params = { symbol: symbol3 };
1472
+ if (options?.orderIdList?.length) params.orderIdList = JSON.stringify(options.orderIdList);
1473
+ if (options?.origClientOrderIdList?.length) {
1474
+ params.origClientOrderIdList = JSON.stringify(options.origClientOrderIdList);
1475
+ }
1476
+ return BatchOrdersResponseSchema.parse(await this.http.delete("/fapi/v1/batchOrders", params, "signed"));
1477
+ }
1478
+ async getOrderModifyHistory(symbol3, options) {
1479
+ return OrderModifyHistorySchema.parse(
1480
+ await this.http.get("/fapi/v1/orderAmendment", { symbol: symbol3, ...options }, "signed")
1481
+ );
1482
+ }
1483
+ async setMarginType(symbol3, marginType) {
1484
+ return MarginTypeChangeSchema.parse(
1485
+ await this.http.post("/fapi/v1/marginType", { symbol: symbol3, marginType }, "signed")
1486
+ );
1487
+ }
1488
+ async modifyPositionMargin(symbol3, amount, type) {
1489
+ return PositionMarginChangeSchema.parse(
1490
+ await this.http.post("/fapi/v1/positionMargin", { symbol: symbol3, amount, type }, "signed")
1491
+ );
1492
+ }
1493
+ async setCountdownCancelAll(symbol3, countdownTime) {
1494
+ return CountdownCancelAllSchema.parse(
1495
+ await this.http.post("/fapi/v1/countdownCancelAll", { symbol: symbol3, countdownTime }, "signed")
1496
+ );
1497
+ }
1498
+ async convertExchangeInfo(options) {
1499
+ return this.http.get("/fapi/v1/convert/exchangeInfo", options);
1500
+ }
1501
+ async convertGetQuote(params) {
1502
+ return this.http.post("/fapi/v1/convert/getQuote", params, "signed");
1503
+ }
1504
+ async convertAcceptQuote(quoteId) {
1505
+ return this.http.post("/fapi/v1/convert/acceptQuote", { quoteId }, "signed");
1506
+ }
1507
+ async convertOrderStatus(options) {
1508
+ return this.http.get("/fapi/v1/convert/orderStatus", options, "signed");
1509
+ }
1510
+ };
1511
+
1512
+ // src/types/filters.types.ts
1513
+ function filtersOf(symbol3) {
1514
+ const raw = symbol3.filters;
1515
+ return Array.isArray(raw) ? raw : [];
1516
+ }
1517
+ function filterOf(symbol3, filterType) {
1518
+ return filtersOf(symbol3).find((f) => f.filterType === filterType);
1519
+ }
1520
+ function num(value, fallback) {
1521
+ const parsed = Number(value);
1522
+ return Number.isFinite(parsed) ? parsed : fallback;
1523
+ }
1524
+ function stepDecimals(step) {
1525
+ const text = typeof step === "number" ? step.toFixed(12) : String(step).trim();
1526
+ const dot = text.indexOf(".");
1527
+ if (dot === -1) return 0;
1528
+ return text.slice(dot + 1).replace(/0+$/, "").length;
1529
+ }
1530
+ function formatToDecimals(value, decimals) {
1531
+ return value.toFixed(decimals);
1532
+ }
1533
+ function floorToStep(value, step, decimals = stepDecimals(step)) {
1534
+ if (!(step > 0)) return value;
1535
+ const steps = Math.floor(value / step + 1e-9);
1536
+ return Number((steps * step).toFixed(decimals));
1537
+ }
1538
+ function roundToStep(value, step, decimals = stepDecimals(step)) {
1539
+ if (!(step > 0)) return value;
1540
+ const steps = Math.round(value / step);
1541
+ return Number((steps * step).toFixed(decimals));
1542
+ }
1543
+ function symbolRulesFrom(symbol3) {
1544
+ const raw = symbol3;
1545
+ const price = filterOf(symbol3, "PRICE_FILTER");
1546
+ const lot = filterOf(symbol3, "LOT_SIZE");
1547
+ const marketLot = filterOf(symbol3, "MARKET_LOT_SIZE");
1548
+ const notional = filterOf(symbol3, "MIN_NOTIONAL");
1549
+ const tickSizeRaw = price?.tickSize ?? 10 ** -num(raw.pricePrecision, 2);
1550
+ const stepSizeRaw = lot?.stepSize ?? 10 ** -num(raw.quantityPrecision, 3);
1551
+ const marketStepRaw = marketLot?.stepSize ?? stepSizeRaw;
1552
+ return {
1553
+ symbol: symbol3.symbol,
1554
+ status: symbol3.status,
1555
+ baseAsset: symbol3.baseAsset,
1556
+ quoteAsset: symbol3.quoteAsset,
1557
+ tickSize: num(tickSizeRaw, 0.01),
1558
+ tickDecimals: stepDecimals(tickSizeRaw),
1559
+ stepSize: num(stepSizeRaw, 1e-3),
1560
+ stepDecimals: stepDecimals(stepSizeRaw),
1561
+ minQty: num(lot?.minQty, 0),
1562
+ maxQty: num(lot?.maxQty, Number.POSITIVE_INFINITY),
1563
+ marketStepSize: num(marketStepRaw, 1e-3),
1564
+ marketStepDecimals: stepDecimals(marketStepRaw),
1565
+ marketMinQty: num(marketLot?.minQty, num(lot?.minQty, 0)),
1566
+ marketMaxQty: num(marketLot?.maxQty, num(lot?.maxQty, Number.POSITIVE_INFINITY)),
1567
+ minNotional: num(notional?.notional, 0)
1568
+ };
1569
+ }
1570
+
1571
+ // src/resources/FuturesOps.ts
1572
+ var RULES_TTL_MS = 6e4;
1573
+ var FuturesOps = class {
1574
+ constructor(market, data, account, trading) {
1575
+ this.market = market;
1576
+ this.data = data;
1577
+ this.account = account;
1578
+ this.trading = trading;
1579
+ this.rulesCache = /* @__PURE__ */ new Map();
1580
+ }
1581
+ /** Typed tick/step/notional rules for a symbol, cached briefly (exchangeInfo is a large payload). */
1582
+ async symbolRules(symbol3, options) {
1583
+ const key = symbol3.toUpperCase();
1584
+ const cached = this.rulesCache.get(key);
1585
+ if (!options?.refresh && cached && Date.now() - cached.at < RULES_TTL_MS) return cached.rules;
1586
+ const details = await this.market.instrumentDetails(key);
1587
+ const rules = symbolRulesFrom(details);
1588
+ this.rulesCache.set(key, { rules, at: Date.now() });
1589
+ return rules;
1590
+ }
1591
+ /** Round a price to the symbol's tick and a quantity to its step, ready to send. */
1592
+ async quantize(symbol3, values) {
1593
+ const rules = await this.symbolRules(symbol3);
1594
+ const step = values.marketOrder ? rules.marketStepSize : rules.stepSize;
1595
+ const stepDp = values.marketOrder ? rules.marketStepDecimals : rules.stepDecimals;
1596
+ return {
1597
+ price: values.price === void 0 ? void 0 : formatToDecimals(roundToStep(values.price, rules.tickSize, rules.tickDecimals), rules.tickDecimals),
1598
+ quantity: values.quantity === void 0 ? void 0 : formatToDecimals(floorToStep(values.quantity, step, stepDp), stepDp),
1599
+ rules
1600
+ };
1601
+ }
1602
+ /**
1603
+ * Risk-based position sizing. Converts "risk N quote units between entry and stop"
1604
+ * into a quantity that satisfies the symbol's step size, lot bounds and min notional,
1605
+ * and reports every constraint it could not satisfy instead of silently adjusting risk.
1606
+ */
1607
+ async sizePosition(params) {
1608
+ const symbol3 = params.symbol.toUpperCase();
1609
+ const reasons = [];
1610
+ if (params.riskAmount === void 0 === (params.riskPct === void 0)) {
1611
+ throw new Error("Provide exactly one of riskAmount or riskPct");
1612
+ }
1613
+ const rules = await this.symbolRules(symbol3);
1614
+ const entryPrice = params.entryPrice ?? (await this.data.premiumIndex(symbol3)).markPrice;
1615
+ let availableBalance;
1616
+ let riskAmount = params.riskAmount ?? 0;
1617
+ if (params.riskPct !== void 0) {
1618
+ const balances = await this.account.balanceV3();
1619
+ availableBalance = balances.find((b) => b.asset === rules.quoteAsset)?.availableBalance ?? 0;
1620
+ riskAmount = availableBalance * (params.riskPct / 100);
1621
+ }
1622
+ const riskPerUnit = Math.abs(entryPrice - params.stopPrice);
1623
+ const stopOnCorrectSide = params.side === "BUY" ? params.stopPrice < entryPrice : params.stopPrice > entryPrice;
1624
+ if (!stopOnCorrectSide) {
1625
+ reasons.push(
1626
+ `stopPrice ${params.stopPrice} is on the wrong side of entry ${entryPrice} for a ${params.side}`
1627
+ );
1628
+ }
1629
+ if (riskPerUnit <= 0) reasons.push("stopPrice equals entryPrice, risk per unit is zero");
1630
+ const step = params.marketOrder ? rules.marketStepSize : rules.stepSize;
1631
+ const stepDp = params.marketOrder ? rules.marketStepDecimals : rules.stepDecimals;
1632
+ const minQty = params.marketOrder ? rules.marketMinQty : rules.minQty;
1633
+ const maxQty = params.marketOrder ? rules.marketMaxQty : rules.maxQty;
1634
+ const rawQuantity = riskPerUnit > 0 ? riskAmount / riskPerUnit : 0;
1635
+ let quantity = floorToStep(rawQuantity, step, stepDp);
1636
+ if (quantity > maxQty) {
1637
+ reasons.push(`quantity ${quantity} exceeds maxQty ${maxQty}, clamped`);
1638
+ quantity = floorToStep(maxQty, step, stepDp);
1639
+ }
1640
+ if (quantity < minQty) {
1641
+ reasons.push(`quantity ${quantity} is below minQty ${minQty} for this symbol`);
1642
+ }
1643
+ const notional = quantity * entryPrice;
1644
+ if (rules.minNotional > 0 && notional < rules.minNotional) {
1645
+ reasons.push(
1646
+ `notional ${notional.toFixed(2)} is below the ${rules.minNotional} minimum; raising risk or widening the stop is required (this tool will not increase risk for you)`
1647
+ );
1648
+ }
1649
+ if (rules.status !== "TRADING") reasons.push(`symbol status is ${rules.status}, not TRADING`);
1650
+ const leverage = params.leverage ?? 1;
1651
+ const requiredMargin = notional / leverage;
1652
+ if (availableBalance !== void 0 && requiredMargin > availableBalance) {
1653
+ reasons.push(
1654
+ `required margin ${requiredMargin.toFixed(2)} exceeds available balance ${availableBalance.toFixed(2)}`
1655
+ );
1656
+ }
1657
+ return {
1658
+ ok: reasons.length === 0 && quantity > 0,
1659
+ reasons,
1660
+ symbol: symbol3,
1661
+ side: params.side,
1662
+ entryPrice,
1663
+ stopPrice: params.stopPrice,
1664
+ riskPerUnit,
1665
+ riskAmount,
1666
+ rawQuantity,
1667
+ quantity,
1668
+ quantityStr: formatToDecimals(quantity, stepDp),
1669
+ notional,
1670
+ minNotional: rules.minNotional,
1671
+ leverage,
1672
+ requiredMargin,
1673
+ availableBalance,
1674
+ rules
1675
+ };
1676
+ }
1677
+ /**
1678
+ * Flatten an open position with a reduce-only market order.
1679
+ *
1680
+ * Picks the closing side from the sign of `positionAmt` and, critically, chooses
1681
+ * between `reduceOnly` and `positionSide`: Binance rejects `reduceOnly` in hedge
1682
+ * mode, and rejects a LONG/SHORT `positionSide` in one-way mode.
1683
+ */
1684
+ async closePosition(params) {
1685
+ const symbol3 = params.symbol.toUpperCase();
1686
+ const portion = params.portion ?? 1;
1687
+ if (!(portion > 0 && portion <= 1)) throw new Error("portion must be within (0, 1]");
1688
+ const positions = await this.account.positionRiskV3(symbol3);
1689
+ const open = positions.filter(
1690
+ (p) => p.positionAmt !== 0 && (params.positionSide === void 0 || p.positionSide === params.positionSide)
1691
+ );
1692
+ if (open.length === 0) return { closed: false, reason: `no open position on ${symbol3}` };
1693
+ if (open.length > 1) {
1694
+ return {
1695
+ closed: false,
1696
+ reason: `${open.length} positions open on ${symbol3} (hedge mode); pass positionSide to pick one`
1697
+ };
1698
+ }
1699
+ const position = open[0];
1700
+ const rules = await this.symbolRules(symbol3);
1701
+ const quantity = floorToStep(
1702
+ Math.abs(position.positionAmt) * portion,
1703
+ rules.marketStepSize,
1704
+ rules.marketStepDecimals
1705
+ );
1706
+ if (quantity <= 0) {
1707
+ return { closed: false, reason: "closing quantity rounds to zero at this symbol step size", position };
1708
+ }
1709
+ const hedgeMode = position.positionSide !== "BOTH";
1710
+ const request = {
1711
+ symbol: symbol3,
1712
+ side: position.positionAmt > 0 ? "SELL" : "BUY",
1713
+ type: "MARKET",
1714
+ quantity: formatToDecimals(quantity, rules.marketStepDecimals),
1715
+ ...hedgeMode ? { positionSide: position.positionSide } : { reduceOnly: true }
1716
+ };
1717
+ if (params.dryRun) return { closed: false, reason: "dryRun", position, request };
1718
+ const order = await this.trading.createOrder(request);
1719
+ return { closed: true, position, order, request };
1720
+ }
1721
+ /**
1722
+ * One-call market picture: 24h stats, mark/index and funding, open interest and
1723
+ * positioning. Uses allSettled so a single unavailable feed degrades one field
1724
+ * instead of failing the whole snapshot.
1725
+ */
1726
+ async marketSnapshot(symbol3, options) {
1727
+ const s = symbol3.toUpperCase();
1728
+ const period2 = options?.period ?? "1h";
1729
+ const [ticker, premium, openInterest, longShort] = await Promise.allSettled([
1730
+ this.market.ticker24hr(s),
1731
+ this.data.premiumIndex(s),
1732
+ this.data.openInterest(s),
1733
+ this.data.globalLongShortAccountRatio(s, period2, 1)
1734
+ ]);
1735
+ const settled = (r) => r.status === "fulfilled" ? r.value : { error: String(r.reason) };
1736
+ return {
1737
+ symbol: s,
1738
+ asOf: Date.now(),
1739
+ ticker24hr: settled(ticker),
1740
+ markPrice: settled(premium),
1741
+ openInterest: settled(openInterest),
1742
+ longShortAccountRatio: settled(longShort)
1743
+ };
1744
+ }
1745
+ /** Balances, open positions and working orders in one call, with the noise filtered out. */
1746
+ async accountOverview() {
1747
+ const [balances, positions, openOrders] = await Promise.allSettled([
1748
+ this.account.balanceV3(),
1749
+ this.account.positionRiskV3(),
1750
+ this.trading.getOpenOrders()
1751
+ ]);
1752
+ const fundedBalances = balances.status === "fulfilled" ? balances.value.filter((b) => b.balance !== 0) : [];
1753
+ const openPositions = positions.status === "fulfilled" ? positions.value.filter((p) => p.positionAmt !== 0) : [];
1754
+ return {
1755
+ asOf: Date.now(),
1756
+ balances: balances.status === "fulfilled" ? fundedBalances : { error: String(balances.reason) },
1757
+ positions: positions.status === "fulfilled" ? openPositions : { error: String(positions.reason) },
1758
+ openOrders: openOrders.status === "fulfilled" ? openOrders.value : { error: String(openOrders.reason) },
1759
+ totals: positions.status === "fulfilled" ? {
1760
+ openPositionCount: openPositions.length,
1761
+ unrealizedPnl: openPositions.reduce((sum, p) => sum + p.unRealizedProfit, 0),
1762
+ grossNotional: openPositions.reduce((sum, p) => sum + Math.abs(p.notional), 0)
1763
+ } : void 0
1764
+ };
1765
+ }
1766
+ /**
1767
+ * Entry order plus its protective stop-loss and optional take-profit, with every
1768
+ * price quantized to the symbol tick.
1769
+ *
1770
+ * The protective legs are placed after the entry is accepted. If a protective leg
1771
+ * fails, the failure is reported per-leg rather than unwound: the entry may already
1772
+ * have filled, so cancelling it is not a safe automatic recovery.
1773
+ */
1774
+ async placeBracketOrder(params) {
1775
+ const symbol3 = params.symbol.toUpperCase();
1776
+ const rules = await this.symbolRules(symbol3);
1777
+ const tick = (p) => formatToDecimals(roundToStep(p, rules.tickSize, rules.tickDecimals), rules.tickDecimals);
1778
+ const quantity = formatToDecimals(
1779
+ floorToStep(Number(params.quantity), rules.stepSize, rules.stepDecimals),
1780
+ rules.stepDecimals
1781
+ );
1782
+ if (Number(quantity) <= 0) throw new Error("quantity rounds to zero at this symbol step size");
1783
+ const exitSide = params.side === "BUY" ? "SELL" : "BUY";
1784
+ const hedgeMode = params.positionSide !== void 0 && params.positionSide !== "BOTH";
1785
+ const sideFields = hedgeMode ? { positionSide: params.positionSide } : { reduceOnly: true };
1786
+ const workingType = params.workingType ?? "MARK_PRICE";
1787
+ const entry = await this.trading.createOrder({
1788
+ symbol: symbol3,
1789
+ side: params.side,
1790
+ type: params.entryPrice === void 0 ? "MARKET" : "LIMIT",
1791
+ quantity,
1792
+ ...params.entryPrice === void 0 ? {} : { price: tick(params.entryPrice), timeInForce: "GTC" },
1793
+ ...hedgeMode ? { positionSide: params.positionSide } : {}
1794
+ });
1795
+ const protective = await Promise.allSettled([
1796
+ this.trading.createOrder({
1797
+ symbol: symbol3,
1798
+ side: exitSide,
1799
+ type: "STOP_MARKET",
1800
+ quantity,
1801
+ stopPrice: tick(params.stopLossPrice),
1802
+ workingType,
1803
+ ...sideFields
1804
+ }),
1805
+ ...params.takeProfitPrice === void 0 ? [] : [
1806
+ this.trading.createOrder({
1807
+ symbol: symbol3,
1808
+ side: exitSide,
1809
+ type: "TAKE_PROFIT_MARKET",
1810
+ quantity,
1811
+ stopPrice: tick(params.takeProfitPrice),
1812
+ workingType,
1813
+ ...sideFields
1814
+ })
1815
+ ]
1816
+ ]);
1817
+ const [stopLoss, takeProfit] = protective;
1818
+ const leg = (r) => r === void 0 ? void 0 : r.status === "fulfilled" ? r.value : { error: String(r.reason) };
1819
+ const failures = protective.filter((r) => r.status === "rejected").length;
1820
+ return {
1821
+ symbol: symbol3,
1822
+ quantity,
1823
+ entry,
1824
+ stopLoss: leg(stopLoss),
1825
+ takeProfit: leg(takeProfit),
1826
+ protectionComplete: failures === 0,
1827
+ ...failures > 0 ? {
1828
+ warning: "Entry was submitted but at least one protective order failed. The position may be unprotected \u2014 review and place the missing leg manually."
1829
+ } : {}
1830
+ };
1831
+ }
1832
+ };
1833
+
1834
+ // src/resources/UserDataStream.ts
1835
+ var UserDataStream = class {
1836
+ constructor(http) {
1837
+ this.http = http;
1838
+ }
1839
+ async createListenKey() {
1840
+ return ListenKeySchema.parse(await this.http.post("/fapi/v1/listenKey", void 0, "apiKey"));
1841
+ }
1842
+ async keepAliveListenKey() {
1843
+ return this.http.put("/fapi/v1/listenKey", void 0, "apiKey");
1844
+ }
1845
+ async closeListenKey() {
1846
+ return this.http.delete("/fapi/v1/listenKey", void 0, "apiKey");
1847
+ }
1848
+ };
1849
+
1850
+ // src/ws/BaseWS.ts
1851
+ import { EventEmitter } from "events";
1852
+ import WebSocket from "ws";
1853
+
1854
+ // src/types/ws.types.ts
1855
+ import { z as z7 } from "zod";
1856
+ var WsKlinePayloadSchema = z7.object({
1857
+ e: z7.literal("kline"),
1858
+ E: z7.number(),
1859
+ s: z7.string(),
1860
+ k: z7.object({
1861
+ t: z7.number(),
1862
+ T: z7.number(),
1863
+ s: z7.string(),
1864
+ i: z7.string(),
1865
+ o: z7.string().transform(Number),
1866
+ c: z7.string().transform(Number),
1867
+ h: z7.string().transform(Number),
1868
+ l: z7.string().transform(Number),
1869
+ v: z7.string().transform(Number),
1870
+ n: z7.number(),
1871
+ x: z7.boolean(),
1872
+ q: z7.string().transform(Number),
1873
+ V: z7.string().transform(Number),
1874
+ Q: z7.string().transform(Number)
1875
+ })
1876
+ });
1877
+ var WsAggTradePayloadSchema = z7.object({
1878
+ e: z7.literal("aggTrade"),
1879
+ E: z7.number(),
1880
+ s: z7.string(),
1881
+ a: z7.number(),
1882
+ p: z7.string().transform(Number),
1883
+ q: z7.string().transform(Number),
1884
+ f: z7.number(),
1885
+ l: z7.number(),
1886
+ T: z7.number(),
1887
+ m: z7.boolean()
1888
+ });
1889
+ var WsTradePayloadSchema = z7.object({
1890
+ e: z7.literal("trade"),
1891
+ E: z7.number(),
1892
+ s: z7.string(),
1893
+ t: z7.number(),
1894
+ p: z7.string().transform(Number),
1895
+ q: z7.string().transform(Number),
1896
+ T: z7.number(),
1897
+ m: z7.boolean()
1898
+ });
1899
+ var WsDepthUpdatePayloadSchema = z7.object({
1900
+ e: z7.literal("depthUpdate"),
1901
+ E: z7.number(),
1902
+ s: z7.string(),
1903
+ U: z7.number(),
1904
+ u: z7.number(),
1905
+ T: z7.number().optional(),
1906
+ pu: z7.number().optional(),
1907
+ b: z7.array(z7.tuple([z7.string(), z7.string()])),
1908
+ a: z7.array(z7.tuple([z7.string(), z7.string()]))
1909
+ });
1910
+ var WsTicker24hrPayloadSchema = z7.object({
1911
+ e: z7.literal("24hrTicker"),
1912
+ E: z7.number(),
1913
+ s: z7.string(),
1914
+ p: z7.string().transform(Number),
1915
+ P: z7.string().transform(Number),
1916
+ c: z7.string().transform(Number),
1917
+ o: z7.string().transform(Number),
1918
+ h: z7.string().transform(Number),
1919
+ l: z7.string().transform(Number),
1920
+ v: z7.string().transform(Number),
1921
+ q: z7.string().transform(Number)
1922
+ });
1923
+ var WsBookTickerPayloadSchema = z7.object({
1924
+ u: z7.number(),
1925
+ s: z7.string(),
1926
+ b: z7.string().transform(Number),
1927
+ B: z7.string().transform(Number),
1928
+ a: z7.string().transform(Number),
1929
+ A: z7.string().transform(Number)
1930
+ });
1931
+ var WsMarkPricePayloadSchema = z7.object({
1932
+ e: z7.literal("markPriceUpdate"),
1933
+ E: z7.number(),
1934
+ s: z7.string(),
1935
+ p: z7.string().transform(Number),
1936
+ i: z7.string().transform(Number),
1937
+ P: z7.string().transform(Number),
1938
+ r: z7.string().transform(Number),
1939
+ T: z7.number()
1940
+ });
1941
+ var WsMiniTickerPayloadSchema = z7.object({
1942
+ e: z7.literal("24hrMiniTicker"),
1943
+ E: z7.number(),
1944
+ s: z7.string(),
1945
+ c: z7.string().transform(Number),
1946
+ o: z7.string().transform(Number),
1947
+ h: z7.string().transform(Number),
1948
+ l: z7.string().transform(Number),
1949
+ v: z7.string().transform(Number),
1950
+ q: z7.string().transform(Number)
1951
+ });
1952
+ var WsForceOrderPayloadSchema = z7.object({
1953
+ e: z7.literal("forceOrder"),
1954
+ E: z7.number(),
1955
+ o: z7.object({
1956
+ s: z7.string(),
1957
+ S: z7.string(),
1958
+ o: z7.string(),
1959
+ f: z7.string(),
1960
+ q: z7.string().transform(Number),
1961
+ p: z7.string().transform(Number),
1962
+ ap: z7.string().transform(Number),
1963
+ X: z7.string(),
1964
+ l: z7.string().transform(Number),
1965
+ z: z7.string().transform(Number),
1966
+ T: z7.number()
1967
+ })
1968
+ });
1969
+ var WsCompositeIndexPayloadSchema = z7.object({
1970
+ e: z7.literal("compositeIndex"),
1971
+ E: z7.number(),
1972
+ s: z7.string(),
1973
+ p: z7.string().transform(Number),
1974
+ c: z7.array(
1975
+ z7.object({
1976
+ b: z7.string(),
1977
+ q: z7.string(),
1978
+ w: z7.string().transform(Number),
1979
+ p: z7.string().transform(Number)
1980
+ })
1981
+ )
1982
+ });
1983
+ var WsAssetIndexPayloadSchema = z7.object({
1984
+ e: z7.literal("assetIndex"),
1985
+ E: z7.number(),
1986
+ s: z7.string(),
1987
+ i: z7.string().transform(Number),
1988
+ b: z7.string().transform(Number),
1989
+ a: z7.string().transform(Number)
1990
+ });
1991
+ var WsRollingWindowTickerPayloadSchema = z7.object({
1992
+ e: z7.enum(["1hTicker", "4hTicker", "1dTicker", "7dTicker", "30dTicker"]),
1993
+ E: z7.number(),
1994
+ s: z7.string(),
1995
+ p: z7.string().transform(Number),
1996
+ P: z7.string().transform(Number),
1997
+ o: z7.string().transform(Number),
1998
+ h: z7.string().transform(Number),
1999
+ l: z7.string().transform(Number),
2000
+ c: z7.string().transform(Number),
2001
+ v: z7.string().transform(Number),
2002
+ q: z7.string().transform(Number),
2003
+ O: z7.number(),
2004
+ C: z7.number()
2005
+ });
2006
+ var WsSpotAvgPricePayloadSchema = z7.object({
2007
+ e: z7.literal("avgPrice"),
2008
+ E: z7.number(),
2009
+ s: z7.string(),
2010
+ i: z7.string(),
2011
+ w: z7.string().transform(Number),
2012
+ T: z7.number()
2013
+ });
2014
+ function parseWsPayload(streamName, raw) {
2015
+ if (streamName.includes("@kline_")) return WsKlinePayloadSchema.parse(raw);
2016
+ if (streamName.includes("@continuousKline_") || streamName.includes("@indexPriceKline_") || streamName.includes("@markPriceKline_")) {
2017
+ return WsKlinePayloadSchema.parse(raw);
2018
+ }
2019
+ if (streamName.includes("@aggTrade")) return WsAggTradePayloadSchema.parse(raw);
2020
+ if (streamName.includes("@trade")) return WsTradePayloadSchema.parse(raw);
2021
+ if (streamName.includes("@depth")) return WsDepthUpdatePayloadSchema.parse(raw);
2022
+ if (streamName.includes("@bookTicker")) return WsBookTickerPayloadSchema.parse(raw);
2023
+ if (streamName.includes("@markPrice")) return WsMarkPricePayloadSchema.parse(raw);
2024
+ if (streamName.includes("@miniTicker")) return WsMiniTickerPayloadSchema.parse(raw);
2025
+ if (streamName.includes("@forceOrder")) return WsForceOrderPayloadSchema.parse(raw);
2026
+ if (streamName.includes("@compositeIndex")) return WsCompositeIndexPayloadSchema.parse(raw);
2027
+ if (streamName.includes("@assetIndex")) return WsAssetIndexPayloadSchema.parse(raw);
2028
+ if (streamName.includes("@avgPrice")) return WsSpotAvgPricePayloadSchema.parse(raw);
2029
+ if (streamName.includes("@ticker_")) return WsRollingWindowTickerPayloadSchema.parse(raw);
2030
+ if (streamName.includes("@ticker")) return WsTicker24hrPayloadSchema.parse(raw);
2031
+ throw new Error(`Unknown WS stream type: ${streamName}`);
2032
+ }
2033
+
2034
+ // src/ws/BaseWS.ts
2035
+ var BaseWS = class extends EventEmitter {
2036
+ constructor(options) {
2037
+ super();
2038
+ this.options = options;
2039
+ this.ws = null;
2040
+ this.streams = /* @__PURE__ */ new Set();
2041
+ this.reconnectAttempt = 0;
2042
+ this.closedByUser = false;
2043
+ }
2044
+ subscribe(streams) {
2045
+ streams.forEach((s) => this.streams.add(s));
2046
+ if (this.isOpen()) {
2047
+ this.send({ method: "SUBSCRIBE", params: streams, id: Date.now() });
2048
+ } else {
2049
+ this.connect();
2050
+ }
2051
+ }
2052
+ unsubscribe(streams) {
2053
+ streams.forEach((s) => this.streams.delete(s));
2054
+ if (this.isOpen()) {
2055
+ this.send({ method: "UNSUBSCRIBE", params: streams, id: Date.now() });
2056
+ }
2057
+ }
2058
+ close() {
2059
+ this.closedByUser = true;
2060
+ this.ws?.close();
2061
+ }
2062
+ reconnect() {
2063
+ if (this.ws?.readyState === WebSocket.OPEN) this.ws.close();
2064
+ this.reconnectAttempt = 0;
2065
+ if (!this.closedByUser) this.connect();
2066
+ }
2067
+ resetReconnectAttempts() {
2068
+ this.reconnectAttempt = 0;
2069
+ }
2070
+ isOpen() {
2071
+ return this.ws?.readyState === WebSocket.OPEN;
2072
+ }
2073
+ send(payload) {
2074
+ this.ws?.send(JSON.stringify(payload));
2075
+ }
2076
+ connect() {
2077
+ this.closedByUser = false;
2078
+ const url = `${this.options.baseStreamUrl}?streams=${[...this.streams].join("/")}`;
2079
+ this.ws = new WebSocket(url);
2080
+ this.ws.on("open", () => {
2081
+ this.reconnectAttempt = 0;
2082
+ this.emit("open");
2083
+ });
2084
+ this.ws.on("message", (raw) => {
2085
+ this.handleMessage(raw.toString());
2086
+ });
2087
+ this.ws.on("close", () => {
2088
+ this.emit("close");
2089
+ if (!this.closedByUser) this.scheduleReconnect();
2090
+ });
2091
+ this.ws.on("error", (err) => {
2092
+ if (this.closedByUser) return;
2093
+ this.emit("error", err);
2094
+ });
2095
+ }
2096
+ handleMessage(raw) {
2097
+ let parsed;
2098
+ try {
2099
+ parsed = JSON.parse(raw);
2100
+ } catch {
2101
+ return;
2102
+ }
2103
+ if (!parsed.stream || parsed.data === void 0) return;
2104
+ try {
2105
+ const payload = parseWsPayload(parsed.stream, parsed.data);
2106
+ this.emit("message", parsed.stream, payload);
2107
+ this.emit(parsed.stream, payload);
2108
+ } catch (err) {
2109
+ this.emit("error", err);
2110
+ }
2111
+ }
2112
+ scheduleReconnect() {
2113
+ const base = this.options.reconnectDelayMs ?? 1e3;
2114
+ const max = this.options.maxReconnectDelayMs ?? 3e4;
2115
+ const delay = Math.min(base * 2 ** this.reconnectAttempt, max);
2116
+ this.reconnectAttempt += 1;
2117
+ setTimeout(() => {
2118
+ if (!this.closedByUser) this.connect();
2119
+ }, delay);
2120
+ }
2121
+ };
2122
+
2123
+ // src/ws/FuturesMarketWS.ts
2124
+ var FuturesMarketWS = class extends BaseWS {
2125
+ constructor(baseStreamUrl = "wss://fstream.binance.com/stream") {
2126
+ super({ baseStreamUrl });
2127
+ }
2128
+ kline(symbol3, interval) {
2129
+ return `${symbol3.toLowerCase()}@kline_${interval}`;
2130
+ }
2131
+ continuousKline(symbol3, contractType2, interval) {
2132
+ return `${symbol3.toLowerCase()}@continuousKline_${contractType2}_${interval}`;
2133
+ }
2134
+ indexPriceKline(symbol3, interval) {
2135
+ return `${symbol3.toLowerCase()}@indexPriceKline_${interval}`;
2136
+ }
2137
+ markPriceKline(symbol3, interval) {
2138
+ return `${symbol3.toLowerCase()}@markPriceKline_${interval}`;
2139
+ }
2140
+ aggTrade(symbol3) {
2141
+ return `${symbol3.toLowerCase()}@aggTrade`;
2142
+ }
2143
+ trade(symbol3) {
2144
+ return `${symbol3.toLowerCase()}@trade`;
2145
+ }
2146
+ depth(symbol3, level = 20) {
2147
+ return `${symbol3.toLowerCase()}@depth${level}`;
2148
+ }
2149
+ depthDiff(symbol3) {
2150
+ return `${symbol3.toLowerCase()}@depth`;
2151
+ }
2152
+ depthDiffSpeed(symbol3, updateSpeed) {
2153
+ return `${symbol3.toLowerCase()}@depth@${updateSpeed}`;
2154
+ }
2155
+ ticker(symbol3) {
2156
+ return `${symbol3.toLowerCase()}@ticker`;
2157
+ }
2158
+ rollingWindowTicker(symbol3, window = "1h") {
2159
+ return `${symbol3.toLowerCase()}@ticker_${window}`;
2160
+ }
2161
+ allRollingWindowTickers(window = "1h") {
2162
+ return `!ticker_${window}@arr`;
2163
+ }
2164
+ allMarketTickers() {
2165
+ return "!ticker@arr";
2166
+ }
2167
+ allBookTickers() {
2168
+ return "!bookTicker";
2169
+ }
2170
+ miniTicker(symbol3) {
2171
+ return `${symbol3.toLowerCase()}@miniTicker`;
2172
+ }
2173
+ allMiniTickers() {
2174
+ return "!miniTicker@arr";
2175
+ }
2176
+ markPrice(symbol3, updateSpeed = "3s") {
2177
+ return `${symbol3.toLowerCase()}@markPrice@${updateSpeed}`;
2178
+ }
2179
+ allMarkPrices() {
2180
+ return "!markPrice@arr";
2181
+ }
2182
+ bookTicker(symbol3) {
2183
+ return `${symbol3.toLowerCase()}@bookTicker`;
2184
+ }
2185
+ liquidationOrder(symbol3) {
2186
+ return `${symbol3.toLowerCase()}@forceOrder`;
2187
+ }
2188
+ allLiquidationOrders() {
2189
+ return "!forceOrder@arr";
2190
+ }
2191
+ compositeIndex(symbol3) {
2192
+ return `${symbol3.toLowerCase()}@compositeIndex`;
2193
+ }
2194
+ assetIndex(symbol3) {
2195
+ return `${symbol3.toLowerCase()}@assetIndex`;
2196
+ }
2197
+ allAssetIndices() {
2198
+ return "!assetIndex@arr";
2199
+ }
2200
+ };
2201
+
2202
+ // src/ws/SpotMarketWS.ts
2203
+ var SpotMarketWS = class extends BaseWS {
2204
+ constructor(baseStreamUrl = "wss://stream.binance.com:9443/stream") {
2205
+ super({ baseStreamUrl });
2206
+ }
2207
+ kline(symbol3, interval) {
2208
+ return `${symbol3.toLowerCase()}@kline_${interval}`;
2209
+ }
2210
+ aggTrade(symbol3) {
2211
+ return `${symbol3.toLowerCase()}@aggTrade`;
2212
+ }
2213
+ trade(symbol3) {
2214
+ return `${symbol3.toLowerCase()}@trade`;
2215
+ }
2216
+ depth(symbol3, level = 20) {
2217
+ return `${symbol3.toLowerCase()}@depth${level}`;
2218
+ }
2219
+ depthDiff(symbol3) {
2220
+ return `${symbol3.toLowerCase()}@depth`;
2221
+ }
2222
+ depthDiffSpeed(symbol3, updateSpeed) {
2223
+ return `${symbol3.toLowerCase()}@depth@${updateSpeed}`;
2224
+ }
2225
+ avgPrice(symbol3) {
2226
+ return `${symbol3.toLowerCase()}@avgPrice`;
2227
+ }
2228
+ ticker(symbol3) {
2229
+ return `${symbol3.toLowerCase()}@ticker`;
2230
+ }
2231
+ rollingWindowTicker(symbol3, window = "1h") {
2232
+ return `${symbol3.toLowerCase()}@ticker_${window}`;
2233
+ }
2234
+ allRollingWindowTickers(window = "1h") {
2235
+ return `!ticker_${window}@arr`;
2236
+ }
2237
+ miniTicker(symbol3) {
2238
+ return `${symbol3.toLowerCase()}@miniTicker`;
2239
+ }
2240
+ allMarketTickers() {
2241
+ return "!ticker@arr";
2242
+ }
2243
+ allMiniTickers() {
2244
+ return "!miniTicker@arr";
2245
+ }
2246
+ bookTicker(symbol3) {
2247
+ return `${symbol3.toLowerCase()}@bookTicker`;
2248
+ }
2249
+ allBookTickers() {
2250
+ return "!bookTicker";
2251
+ }
2252
+ };
2253
+
2254
+ // src/ws/SpotUserWS.ts
2255
+ import { EventEmitter as EventEmitter2 } from "events";
2256
+ import WebSocket2 from "ws";
2257
+ var SpotUserWS = class extends EventEmitter2 {
2258
+ constructor(options) {
2259
+ super();
2260
+ this.options = options;
2261
+ this.ws = null;
2262
+ this.reconnectAttempt = 0;
2263
+ this.closedByUser = false;
2264
+ }
2265
+ connect() {
2266
+ const listenKey = this.options.getListenKey();
2267
+ if (!listenKey) {
2268
+ this.emit("error", new Error("No spot listenKey available for user data stream"));
2269
+ return;
2270
+ }
2271
+ this.closedByUser = false;
2272
+ const url = `${this.options.baseUserUrl}/${listenKey}`;
2273
+ this.ws = new WebSocket2(url);
2274
+ this.ws.on("open", () => {
2275
+ this.reconnectAttempt = 0;
2276
+ this.emit("open");
2277
+ });
2278
+ this.ws.on("message", (raw) => {
2279
+ this.handleMessage(raw.toString());
2280
+ });
2281
+ this.ws.on("close", () => {
2282
+ this.emit("close");
2283
+ if (!this.closedByUser) this.scheduleReconnect();
2284
+ });
2285
+ this.ws.on("error", (err) => {
2286
+ this.emit("error", err);
2287
+ });
2288
+ }
2289
+ close() {
2290
+ this.closedByUser = true;
2291
+ this.ws?.close();
2292
+ }
2293
+ reconnect() {
2294
+ if (this.ws?.readyState === WebSocket2.OPEN) this.ws.close();
2295
+ this.reconnectAttempt = 0;
2296
+ if (!this.closedByUser) this.connect();
2297
+ }
2298
+ resetReconnectAttempts() {
2299
+ this.reconnectAttempt = 0;
2300
+ }
2301
+ handleMessage(raw) {
2302
+ let parsed;
2303
+ try {
2304
+ parsed = JSON.parse(raw);
2305
+ } catch {
2306
+ return;
2307
+ }
2308
+ try {
2309
+ const event = parseSpotUserDataEvent(parsed);
2310
+ this.emit(event.e, event);
2311
+ this.emit("userData", event);
2312
+ } catch (err) {
2313
+ this.emit("error", err);
2314
+ }
2315
+ }
2316
+ scheduleReconnect() {
2317
+ const base = this.options.reconnectDelayMs ?? 1e3;
2318
+ const max = this.options.maxReconnectDelayMs ?? 3e4;
2319
+ const delay = Math.min(base * 2 ** this.reconnectAttempt, max);
2320
+ this.reconnectAttempt += 1;
2321
+ setTimeout(() => {
2322
+ if (!this.closedByUser) this.connect();
2323
+ }, delay);
2324
+ }
2325
+ };
2326
+
2327
+ // src/ws/FuturesUserWS.ts
2328
+ import { EventEmitter as EventEmitter3 } from "events";
2329
+ import WebSocket3 from "ws";
2330
+ var FuturesUserWS = class extends EventEmitter3 {
2331
+ constructor(options) {
2332
+ super();
2333
+ this.options = options;
2334
+ this.ws = null;
2335
+ this.reconnectAttempt = 0;
2336
+ this.closedByUser = false;
2337
+ }
2338
+ connect() {
2339
+ const listenKey = this.options.getListenKey();
2340
+ if (!listenKey) {
2341
+ this.emit("error", new Error("No listenKey available for user data stream"));
2342
+ return;
2343
+ }
2344
+ this.closedByUser = false;
2345
+ const url = `${this.options.baseUserUrl}/${listenKey}`;
2346
+ this.ws = new WebSocket3(url);
2347
+ this.ws.on("open", () => {
2348
+ this.reconnectAttempt = 0;
2349
+ this.emit("open");
2350
+ });
2351
+ this.ws.on("message", (raw) => {
2352
+ this.handleMessage(raw.toString());
2353
+ });
2354
+ this.ws.on("close", () => {
2355
+ this.emit("close");
2356
+ if (!this.closedByUser) this.scheduleReconnect();
2357
+ });
2358
+ this.ws.on("error", (err) => {
2359
+ this.emit("error", err);
2360
+ });
2361
+ }
2362
+ close() {
2363
+ this.closedByUser = true;
2364
+ this.ws?.close();
2365
+ }
2366
+ reconnect() {
2367
+ if (this.ws?.readyState === WebSocket3.OPEN) this.ws.close();
2368
+ this.reconnectAttempt = 0;
2369
+ if (!this.closedByUser) this.connect();
2370
+ }
2371
+ resetReconnectAttempts() {
2372
+ this.reconnectAttempt = 0;
2373
+ }
2374
+ handleMessage(raw) {
2375
+ let parsed;
2376
+ try {
2377
+ parsed = JSON.parse(raw);
2378
+ } catch {
2379
+ return;
2380
+ }
2381
+ try {
2382
+ const event = parseUserDataEvent(parsed);
2383
+ this.emit(event.e, event);
2384
+ this.emit("userData", event);
2385
+ } catch (err) {
2386
+ this.emit("error", err);
2387
+ }
2388
+ }
2389
+ scheduleReconnect() {
2390
+ const base = this.options.reconnectDelayMs ?? 1e3;
2391
+ const max = this.options.maxReconnectDelayMs ?? 3e4;
2392
+ const delay = Math.min(base * 2 ** this.reconnectAttempt, max);
2393
+ this.reconnectAttempt += 1;
2394
+ setTimeout(() => {
2395
+ if (!this.closedByUser) this.connect();
2396
+ }, delay);
2397
+ }
2398
+ };
2399
+
2400
+ // src/ws/WsApi.ts
2401
+ import { createHmac as createHmac2, randomUUID } from "crypto";
2402
+ import WebSocket4 from "ws";
2403
+ function buildQueryString(data) {
2404
+ return Object.entries(data).filter(([, val]) => val !== void 0 && val !== null).map(([key, val]) => `${key}=${encodeURIComponent(String(val))}`).join("&");
2405
+ }
2406
+ var WsApi = class {
2407
+ constructor(options) {
2408
+ this.options = options;
2409
+ }
2410
+ async request(method, params = {}, options) {
2411
+ const { apiKey, apiSecret } = this.options;
2412
+ const signed = options?.signed ?? true;
2413
+ const { id = randomUUID(), ...methodParams } = params;
2414
+ const requestParams = { ...methodParams };
2415
+ if (signed) {
2416
+ if (!apiKey || !apiSecret) {
2417
+ throw new Error("API key and secret required for signed WebSocket API requests");
2418
+ }
2419
+ requestParams.apiKey = apiKey;
2420
+ requestParams.timestamp = Date.now();
2421
+ requestParams.recvWindow = this.options.recvWindow ?? 5e3;
2422
+ const queryString = buildQueryString(requestParams);
2423
+ requestParams.signature = createHmac2("sha256", apiSecret).update(queryString).digest("hex");
2424
+ }
2425
+ return new Promise((resolve, reject) => {
2426
+ const ws = new WebSocket4(this.options.baseUrl);
2427
+ const timeout = setTimeout(() => {
2428
+ ws.close();
2429
+ reject(new NetworkError(`WebSocket API request timed out: ${method}`));
2430
+ }, this.options.timeoutMs ?? 15e3);
2431
+ ws.on("open", () => ws.send(JSON.stringify({ id, method, params: requestParams })));
2432
+ ws.on("message", (raw) => {
2433
+ clearTimeout(timeout);
2434
+ ws.close();
2435
+ try {
2436
+ const parsed = JSON.parse(raw.toString());
2437
+ if (parsed.status >= 400) {
2438
+ reject(new Error(parsed.error?.msg ?? `WebSocket API error: ${method}`));
2439
+ } else {
2440
+ resolve(parsed);
2441
+ }
2442
+ } catch (err) {
2443
+ reject(new NetworkError("Invalid WebSocket API JSON payload", err));
2444
+ }
2445
+ });
2446
+ ws.on("error", (err) => {
2447
+ clearTimeout(timeout);
2448
+ reject(new NetworkError(err.message, err));
2449
+ });
2450
+ });
2451
+ }
2452
+ placeOrder(params) {
2453
+ return this.request("order.place", params);
2454
+ }
2455
+ cancelOrder(params) {
2456
+ return this.request("order.cancel", params);
2457
+ }
2458
+ modifyOrder(params) {
2459
+ return this.request("order.modify", params);
2460
+ }
2461
+ placeAlgoOrder(params) {
2462
+ return this.request("algoOrder.place", params);
2463
+ }
2464
+ cancelAlgoOrder(params) {
2465
+ return this.request("algoOrder.cancel", params);
2466
+ }
2467
+ orderStatus(params) {
2468
+ return this.request("order.status", params);
2469
+ }
2470
+ placeOrderList(params) {
2471
+ return this.request("orderList.place", params);
2472
+ }
2473
+ cancelOrderList(params) {
2474
+ return this.request("orderList.cancel", params);
2475
+ }
2476
+ orderListStatus(params) {
2477
+ return this.request("orderList.status", params);
2478
+ }
2479
+ accountStatus() {
2480
+ return this.request("account.status", {});
2481
+ }
2482
+ accountPosition(params = {}) {
2483
+ return this.request("account.position", params);
2484
+ }
2485
+ userDataStreamStart() {
2486
+ return this.request("userDataStream.start", {});
2487
+ }
2488
+ userDataStreamPing(listenKey) {
2489
+ return this.request("userDataStream.ping", { listenKey });
2490
+ }
2491
+ userDataStreamStop(listenKey) {
2492
+ return this.request("userDataStream.stop", { listenKey });
2493
+ }
2494
+ // ---- Public market data (no signature required) ----
2495
+ time() {
2496
+ return this.request("time", {}, { signed: false });
2497
+ }
2498
+ exchangeInfo(params = {}) {
2499
+ return this.request("exchangeInfo", params, { signed: false });
2500
+ }
2501
+ klines(params) {
2502
+ return this.request("klines", params, { signed: false });
2503
+ }
2504
+ aggTrades(params) {
2505
+ return this.request("aggTrades", params, { signed: false });
2506
+ }
2507
+ trades(params) {
2508
+ return this.request("trades", params, { signed: false });
2509
+ }
2510
+ depth(params) {
2511
+ return this.request("depth", params, { signed: false });
2512
+ }
2513
+ avgPrice(params) {
2514
+ return this.request("avgPrice", params, { signed: false });
2515
+ }
2516
+ tickerPrice(params = {}) {
2517
+ return this.request("ticker.price", params, { signed: false });
2518
+ }
2519
+ tickerBookTicker(params = {}) {
2520
+ return this.request("ticker.bookTicker", params, { signed: false });
2521
+ }
2522
+ ticker24hr(params = {}) {
2523
+ return this.request("ticker.24hr", params, { signed: false });
2524
+ }
2525
+ };
2526
+
2527
+ // src/client/BinanceClient.ts
2528
+ var BinanceClient = class {
2529
+ constructor(options = {}) {
2530
+ this.listenKeyValue = null;
2531
+ this.keepAliveInterval = null;
2532
+ this.spotListenKeyValue = null;
2533
+ this.spotKeepAliveInterval = null;
2534
+ const { endpoints } = resolveEnvironment(options);
2535
+ const httpOptions = {
2536
+ apiKey: options.apiKey,
2537
+ apiSecret: options.apiSecret,
2538
+ recvWindow: options.recvWindow ?? 5e3,
2539
+ timeoutMs: options.timeoutMs ?? 15e3,
2540
+ maxRetries: options.maxRetries ?? 3,
2541
+ retryBaseDelayMs: options.retryBaseDelayMs,
2542
+ retryMaxDelayMs: options.retryMaxDelayMs
2543
+ };
2544
+ this.authHttp = new HttpClient({ baseURL: endpoints.restRoot, ...httpOptions });
2545
+ const spotHttp = new HttpClient({ baseURL: endpoints.restSpot, ...httpOptions });
2546
+ this.spot = {
2547
+ market: new SpotMarket(spotHttp),
2548
+ account: new SpotAccount(spotHttp),
2549
+ trading: new SpotTrading(spotHttp),
2550
+ userStream: new SpotUserDataStream(spotHttp),
2551
+ ws: new SpotMarketWS(endpoints.wsSpotMarket),
2552
+ wsUser: new SpotUserWS({
2553
+ baseUserUrl: endpoints.wsSpotUser,
2554
+ getListenKey: () => this.spotListenKeyValue
2555
+ })
2556
+ };
2557
+ const futuresMarket = new FuturesMarket(
2558
+ new HttpClient({ baseURL: endpoints.restFapi, ...httpOptions }),
2559
+ endpoints.restRoot
2560
+ );
2561
+ const futuresData = new FuturesData({
2562
+ restFapi: endpoints.restFapi,
2563
+ restFuturesData: endpoints.restFuturesData,
2564
+ ...httpOptions
2565
+ });
2566
+ const futuresAccount = new FuturesAccount(this.authHttp);
2567
+ const futuresTrading = new FuturesTrading(this.authHttp);
2568
+ this.futures = {
2569
+ market: futuresMarket,
2570
+ data: futuresData,
2571
+ account: futuresAccount,
2572
+ trading: futuresTrading,
2573
+ ops: new FuturesOps(futuresMarket, futuresData, futuresAccount, futuresTrading),
2574
+ userStream: new UserDataStream(this.authHttp),
2575
+ ws: new FuturesMarketWS(endpoints.wsMarket),
2576
+ wsUser: new FuturesUserWS({
2577
+ baseUserUrl: endpoints.wsUser,
2578
+ getListenKey: () => this.listenKeyValue
2579
+ }),
2580
+ wsApi: new WsApi({
2581
+ baseUrl: endpoints.wsApi,
2582
+ apiKey: options.apiKey,
2583
+ apiSecret: options.apiSecret,
2584
+ recvWindow: options.recvWindow
2585
+ })
2586
+ };
2587
+ }
2588
+ static nowSeconds() {
2589
+ return Math.floor(Date.now() / 1e3);
2590
+ }
2591
+ static buildPair(base, target) {
2592
+ return `${base}${target}`.toUpperCase();
2593
+ }
2594
+ static parsePair(pair) {
2595
+ const match = pair.match(/^([A-Z0-9]+)(USDT|USDC|BUSD|BTC|ETH|BNB)$/);
2596
+ if (!match) return null;
2597
+ return { base: match[1], target: match[2] };
2598
+ }
2599
+ static calculateLiquidationPrice(entryPrice, leverage, side3, mm = 5e-3) {
2600
+ const dir = side3 === "buy" ? 1 : -1;
2601
+ return dir === 1 ? entryPrice * (1 - 1 / leverage + mm) : entryPrice * (1 + 1 / leverage - mm);
2602
+ }
2603
+ async startUserStream() {
2604
+ const { listenKey } = await this.futures.userStream.createListenKey();
2605
+ this.listenKeyValue = listenKey;
2606
+ this.keepAliveInterval = setInterval(() => {
2607
+ this.futures.userStream.keepAliveListenKey().catch(() => {
2608
+ });
2609
+ }, 30 * 60 * 1e3);
2610
+ this.futures.wsUser.connect();
2611
+ return listenKey;
2612
+ }
2613
+ closeUserStream() {
2614
+ if (this.keepAliveInterval) clearInterval(this.keepAliveInterval);
2615
+ this.keepAliveInterval = null;
2616
+ this.futures.wsUser.close();
2617
+ this.futures.userStream.closeListenKey().catch(() => {
2618
+ });
2619
+ this.listenKeyValue = null;
2620
+ }
2621
+ async startSpotUserStream() {
2622
+ const { listenKey } = await this.spot.userStream.createListenKey();
2623
+ this.spotListenKeyValue = listenKey;
2624
+ this.spotKeepAliveInterval = setInterval(() => {
2625
+ this.spot.userStream.keepAliveListenKey().catch(() => {
2626
+ });
2627
+ }, 30 * 60 * 1e3);
2628
+ this.spot.wsUser.connect();
2629
+ return listenKey;
2630
+ }
2631
+ closeSpotUserStream() {
2632
+ if (this.spotKeepAliveInterval) clearInterval(this.spotKeepAliveInterval);
2633
+ this.spotKeepAliveInterval = null;
2634
+ this.spot.wsUser.close();
2635
+ this.spot.userStream.closeListenKey().catch(() => {
2636
+ });
2637
+ this.spotListenKeyValue = null;
2638
+ }
2639
+ closeAllWebSockets() {
2640
+ this.futures.ws.close();
2641
+ this.futures.wsUser.close();
2642
+ this.spot.ws.close();
2643
+ this.spot.wsUser.close();
2644
+ this.closeUserStream();
2645
+ this.closeSpotUserStream();
2646
+ }
2647
+ reconnectWebSocket(target) {
2648
+ if (target === "ws") this.futures.ws.reconnect();
2649
+ else if (target === "wsUser") this.futures.wsUser.reconnect();
2650
+ else if (target === "spot") this.spot.ws.reconnect();
2651
+ else this.spot.wsUser.reconnect();
2652
+ }
2653
+ resetReconnectAttempts() {
2654
+ this.futures.ws.resetReconnectAttempts();
2655
+ this.futures.wsUser.resetReconnectAttempts();
2656
+ this.spot.ws.resetReconnectAttempts();
2657
+ this.spot.wsUser.resetReconnectAttempts();
2658
+ }
2659
+ };
2660
+
2661
+ // src/paper/PaperTradingEngine.ts
2662
+ function emptyPosition(symbol3) {
2663
+ return {
2664
+ symbol: symbol3,
2665
+ side: "NONE",
2666
+ entryPrice: 0,
2667
+ quantity: 0,
2668
+ leverage: 1,
2669
+ margin: 0,
2670
+ unrealizedPnl: 0,
2671
+ openedAt: 0
2672
+ };
2673
+ }
2674
+ var PaperTradingEngine = class {
2675
+ constructor(options = {}) {
2676
+ this.orderIdCounter = 1e6;
2677
+ this.market = options.market ?? new FuturesMarket();
2678
+ const initialBalance = options.initialBalance ?? 1e4;
2679
+ this.account = {
2680
+ balance: initialBalance,
2681
+ availableBalance: initialBalance,
2682
+ totalWalletBalance: initialBalance,
2683
+ realizedPnl: 0,
2684
+ unrealizedPnl: 0,
2685
+ positions: {},
2686
+ orders: []
2687
+ };
2688
+ }
2689
+ /** Current mark price for any tradeable symbol. */
2690
+ async getMarketPrice(symbol3) {
2691
+ return (await this.market.tickerPrice(symbol3.toUpperCase())).price;
2692
+ }
2693
+ positionFor(symbol3) {
2694
+ const existing = this.account.positions[symbol3];
2695
+ if (existing) return existing;
2696
+ const created = emptyPosition(symbol3);
2697
+ this.account.positions[symbol3] = created;
2698
+ return created;
2699
+ }
2700
+ async placeOrder(params) {
2701
+ const symbol3 = params.symbol.toUpperCase();
2702
+ const { side: side3, type, quantity } = params;
2703
+ const leverage = params.leverage ?? 1;
2704
+ if (!(quantity > 0)) throw new Error("Quantity must be positive");
2705
+ if (leverage < 1 || leverage > 125) throw new Error("Leverage must be 1-125");
2706
+ if (type === "LIMIT" && !(params.price > 0)) throw new Error("Price required for LIMIT orders");
2707
+ const marketPrice = await this.getMarketPrice(symbol3);
2708
+ const fillPrice = type === "MARKET" ? marketPrice : params.price;
2709
+ const position = this.positionFor(symbol3);
2710
+ const opposing = side3 === "SELL" && position.side === "LONG" || side3 === "BUY" && position.side === "SHORT";
2711
+ const openingQty = opposing ? Math.max(0, quantity - position.quantity) : quantity;
2712
+ const requiredMargin = openingQty * fillPrice / leverage;
2713
+ if (requiredMargin > this.account.availableBalance + 1e-9) {
2714
+ throw new Error(
2715
+ `Insufficient balance. Required: ${requiredMargin.toFixed(2)}, Available: ${this.account.availableBalance.toFixed(2)}`
2716
+ );
2717
+ }
2718
+ const realizedPnl = this.applyFill(position, side3, quantity, fillPrice, leverage);
2719
+ this.markToMarket(symbol3, marketPrice);
2720
+ this.recomputeTotals();
2721
+ const order = {
2722
+ orderId: ++this.orderIdCounter,
2723
+ symbol: symbol3,
2724
+ side: side3,
2725
+ type,
2726
+ quantity,
2727
+ price: fillPrice,
2728
+ status: "FILLED",
2729
+ filledQuantity: quantity,
2730
+ avgFillPrice: fillPrice,
2731
+ realizedPnl,
2732
+ createdAt: Date.now()
2733
+ };
2734
+ this.account.orders.push(order);
2735
+ return order;
2736
+ }
2737
+ /**
2738
+ * Apply a fill to a position, returning the realized PnL it booked.
2739
+ * Handles extend, partial reduce, full close, and a flip through zero.
2740
+ */
2741
+ applyFill(position, side3, quantity, fillPrice, leverage) {
2742
+ const incoming = side3 === "BUY" ? "LONG" : "SHORT";
2743
+ if (position.side === "NONE" || position.quantity === 0) {
2744
+ this.openExposure(position, incoming, quantity, fillPrice, leverage);
2745
+ return 0;
2746
+ }
2747
+ if (position.side === incoming) {
2748
+ this.openExposure(position, incoming, quantity, fillPrice, leverage);
2749
+ return 0;
2750
+ }
2751
+ const closeQty = Math.min(quantity, position.quantity);
2752
+ const realized = this.reduceExposure(position, closeQty, fillPrice);
2753
+ const remainder = quantity - closeQty;
2754
+ if (remainder > 0) this.openExposure(position, incoming, remainder, fillPrice, leverage);
2755
+ return realized;
2756
+ }
2757
+ openExposure(position, side3, quantity, fillPrice, leverage) {
2758
+ const previousQty = position.quantity;
2759
+ const totalQty = previousQty + quantity;
2760
+ position.entryPrice = totalQty > 0 ? (position.entryPrice * previousQty + fillPrice * quantity) / totalQty : fillPrice;
2761
+ position.quantity = totalQty;
2762
+ position.side = side3;
2763
+ position.leverage = leverage;
2764
+ if (previousQty === 0) position.openedAt = Date.now();
2765
+ const margin = quantity * fillPrice / leverage;
2766
+ position.margin += margin;
2767
+ this.account.availableBalance -= margin;
2768
+ }
2769
+ /** Reduce a position, releasing margin pro-rata and booking realized PnL. */
2770
+ reduceExposure(position, closeQty, fillPrice) {
2771
+ const direction = position.side === "LONG" ? 1 : -1;
2772
+ const realized = (fillPrice - position.entryPrice) * closeQty * direction;
2773
+ const releasedMargin = position.quantity > 0 ? position.margin * (closeQty / position.quantity) : 0;
2774
+ position.margin -= releasedMargin;
2775
+ position.quantity -= closeQty;
2776
+ this.account.balance += realized;
2777
+ this.account.realizedPnl += realized;
2778
+ this.account.availableBalance += releasedMargin + realized;
2779
+ if (position.quantity <= 1e-12) {
2780
+ position.quantity = 0;
2781
+ position.side = "NONE";
2782
+ position.entryPrice = 0;
2783
+ position.unrealizedPnl = 0;
2784
+ this.account.availableBalance += position.margin;
2785
+ position.margin = 0;
2786
+ }
2787
+ return realized;
2788
+ }
2789
+ markToMarket(symbol3, price) {
2790
+ const position = this.account.positions[symbol3];
2791
+ if (!position || position.side === "NONE" || position.quantity === 0) {
2792
+ if (position) position.unrealizedPnl = 0;
2793
+ return;
2794
+ }
2795
+ const direction = position.side === "LONG" ? 1 : -1;
2796
+ position.unrealizedPnl = (price - position.entryPrice) * position.quantity * direction;
2797
+ }
2798
+ recomputeTotals() {
2799
+ this.account.unrealizedPnl = Object.values(this.account.positions).reduce(
2800
+ (sum, p) => sum + p.unrealizedPnl,
2801
+ 0
2802
+ );
2803
+ this.account.totalWalletBalance = this.account.balance + this.account.unrealizedPnl;
2804
+ }
2805
+ /** Refresh unrealized PnL for every open position against live prices. */
2806
+ async updatePositions() {
2807
+ const open = Object.values(this.account.positions).filter((p) => p.side !== "NONE");
2808
+ await Promise.all(
2809
+ open.map(async (position) => {
2810
+ try {
2811
+ this.markToMarket(position.symbol, await this.getMarketPrice(position.symbol));
2812
+ } catch {
2813
+ }
2814
+ })
2815
+ );
2816
+ this.recomputeTotals();
2817
+ }
2818
+ getAccountInfo() {
2819
+ return {
2820
+ ...this.account,
2821
+ positions: this.getAllPositions(),
2822
+ orders: [...this.account.orders]
2823
+ };
2824
+ }
2825
+ getPosition(symbol3) {
2826
+ const position = this.account.positions[symbol3.toUpperCase()];
2827
+ return position ? { ...position } : void 0;
2828
+ }
2829
+ getAllPositions() {
2830
+ return Object.fromEntries(
2831
+ Object.entries(this.account.positions).map(([symbol3, p]) => [symbol3, { ...p }])
2832
+ );
2833
+ }
2834
+ /** Open positions only — the zero-size entries left behind by closed trades are filtered out. */
2835
+ getOpenPositions() {
2836
+ return Object.values(this.account.positions).filter((p) => p.side !== "NONE").map((p) => ({ ...p }));
2837
+ }
2838
+ getOrderHistory(symbol3) {
2839
+ const orders = symbol3 ? this.account.orders.filter((o) => o.symbol === symbol3.toUpperCase()) : this.account.orders;
2840
+ return [...orders];
2841
+ }
2842
+ };
2843
+
2844
+ // src/tools/account.tools.ts
2845
+ import { z as z9 } from "zod";
2846
+
2847
+ // src/tools/types.ts
2848
+ import { z as z8 } from "zod";
2849
+ function normalizeSymbol(value) {
2850
+ return String(value ?? "").trim().toUpperCase();
2851
+ }
2852
+ function toJsonSchema(inputSchema) {
2853
+ return z8.toJSONSchema(inputSchema, { target: "json" });
2854
+ }
2855
+ function toOpenAITool(tool) {
2856
+ return {
2857
+ type: "function",
2858
+ function: {
2859
+ name: tool.name,
2860
+ description: tool.description,
2861
+ parameters: toJsonSchema(tool.inputSchema)
2862
+ }
2863
+ };
2864
+ }
2865
+ function toAnthropicTool(tool) {
2866
+ return {
2867
+ name: tool.name,
2868
+ description: tool.description,
2869
+ input_schema: toJsonSchema(tool.inputSchema)
2870
+ };
2871
+ }
2872
+ function toMCPTool(tool) {
2873
+ return {
2874
+ name: tool.name,
2875
+ description: tool.description,
2876
+ inputSchema: toJsonSchema(tool.inputSchema)
2877
+ };
2878
+ }
2879
+ function toToolList(tools, format) {
2880
+ const adapter = format === "openai" ? toOpenAITool : format === "anthropic" ? toAnthropicTool : toMCPTool;
2881
+ return tools.map(adapter);
2882
+ }
2883
+ function textResult(data) {
2884
+ return JSON.stringify(data, null, 2);
2885
+ }
2886
+
2887
+ // src/tools/account.tools.ts
2888
+ var optionalSymbol = z9.string().min(1).describe("USD-M pair, e.g. BTCUSDT").optional();
2889
+ var limit = z9.number().int().positive().max(1e3).default(100);
2890
+ var startTime = z9.number().int().positive().optional();
2891
+ var endTime = z9.number().int().positive().optional();
2892
+ function accountTools(client) {
2893
+ const a = client.futures.account;
2894
+ return [
2895
+ {
2896
+ name: "futures_balance",
2897
+ description: "Get futures account balances (v3): asset, wallet balance, unrealized profit, margin balance, available balance (signed).",
2898
+ inputSchema: z9.object({}),
2899
+ handler: async () => textResult(await a.balanceV3())
2900
+ },
2901
+ {
2902
+ name: "futures_account",
2903
+ description: "Get full account information (v3): total wallet/margin balance, unrealized profit, available balance, assets & positions (signed).",
2904
+ inputSchema: z9.object({}),
2905
+ handler: async () => textResult(await a.accountV3())
2906
+ },
2907
+ {
2908
+ name: "futures_balance_v2",
2909
+ description: "Get futures account balance (v2 legacy) per asset (signed).",
2910
+ inputSchema: z9.object({}),
2911
+ handler: async () => textResult(await a.balance())
2912
+ },
2913
+ {
2914
+ name: "futures_account_v2",
2915
+ description: "Get futures account information (v2 legacy) (signed).",
2916
+ inputSchema: z9.object({}),
2917
+ handler: async () => textResult(await a.account())
2918
+ },
2919
+ {
2920
+ name: "futures_position_risk",
2921
+ description: "Get current open positions (v3): symbol, side, size, entry price, mark price, liquidation price, leverage, unrealized PnL, margin (signed).",
2922
+ inputSchema: z9.object({ symbol: optionalSymbol }),
2923
+ handler: async ({ symbol: symbol3 }) => textResult(await a.positionRiskV3(symbol3 ? normalizeSymbol(symbol3) : void 0))
2924
+ },
2925
+ {
2926
+ name: "futures_income_history",
2927
+ description: "Get futures income history (realized PnL, funding fees, commissions, transfers, insurance clearing). incomeType examples: TRANSFER, REALIZED_PNL, FUNDING_FEE, COMMISSION, INSURANCE_CLEAR, REFERRAL_KICKBACK (signed).",
2928
+ inputSchema: z9.object({ symbol: optionalSymbol, incomeType: z9.string().optional().describe("e.g. TRANSFER, REALIZED_PNL, FUNDING_FEE, COMMISSION"), startTime, endTime, limit: limit.default(100) }),
2929
+ handler: async ({ symbol: symbol3, incomeType, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await a.incomeHistory({ symbol: symbol3 ? normalizeSymbol(symbol3) : void 0, incomeType, startTime: startTime5, endTime: endTime5, limit: Number(limit4) }))
2930
+ },
2931
+ {
2932
+ name: "futures_user_trades",
2933
+ description: "Get account trade list (filled trades) for a symbol with optional time range (signed).",
2934
+ inputSchema: z9.object({ symbol: z9.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), startTime, endTime, limit: limit.default(100), fromId: z9.number().int().positive().optional() }),
2935
+ handler: async ({ symbol: symbol3, startTime: startTime5, endTime: endTime5, limit: limit4, fromId }) => textResult(await a.userTrades({ symbol: normalizeSymbol(symbol3), startTime: startTime5, endTime: endTime5, limit: Number(limit4), fromId }))
2936
+ },
2937
+ {
2938
+ name: "futures_leverage_brackets",
2939
+ description: "Get notional and leverage brackets for one symbol or all: tier caps, maintenance margin rate, max leverage (signed).",
2940
+ inputSchema: z9.object({ symbol: optionalSymbol }),
2941
+ handler: async ({ symbol: symbol3 }) => textResult(await a.leverageBrackets(symbol3 ? normalizeSymbol(symbol3) : void 0))
2942
+ },
2943
+ {
2944
+ name: "futures_commission_rate",
2945
+ description: "Get user commission rate (taker & maker fee) for a symbol (signed).",
2946
+ inputSchema: z9.object({ symbol: z9.string().min(1).describe("USD-M pair, e.g. ETHUSDT") }),
2947
+ handler: async ({ symbol: symbol3 }) => textResult(await a.commissionRate(normalizeSymbol(symbol3)))
2948
+ },
2949
+ {
2950
+ name: "futures_multi_assets_mode",
2951
+ description: "Get current multi-assets margin mode status (signed).",
2952
+ inputSchema: z9.object({}),
2953
+ handler: async () => textResult(await a.multiAssetsMargin())
2954
+ },
2955
+ {
2956
+ name: "futures_set_multi_assets_mode",
2957
+ description: "Enable or disable multi-assets mode (signed; 7-day cooldown between toggles).",
2958
+ inputSchema: z9.object({ enabled: z9.boolean().describe("true to enable, false to disable") }),
2959
+ handler: async ({ enabled }) => textResult(await a.setMultiAssetsMargin(enabled))
2960
+ },
2961
+ {
2962
+ name: "futures_fee_burn_status",
2963
+ description: "Get BNB fee burn status for futures trades (signed).",
2964
+ inputSchema: z9.object({}),
2965
+ handler: async () => textResult(await a.feeBurnStatus())
2966
+ },
2967
+ {
2968
+ name: "futures_set_fee_burn",
2969
+ description: "Toggle BNB fee burn on futures trades (signed).",
2970
+ inputSchema: z9.object({ enabled: z9.boolean().describe("true to enable fee burn") }),
2971
+ handler: async ({ enabled }) => textResult(await a.setFeeBurnStatus(enabled))
2972
+ },
2973
+ {
2974
+ name: "futures_position_mode",
2975
+ description: "Get current position mode: one-way (BOTH) or hedge mode (LONG/SHORT) (signed).",
2976
+ inputSchema: z9.object({}),
2977
+ handler: async () => textResult(await a.positionMode())
2978
+ },
2979
+ {
2980
+ name: "futures_set_position_mode",
2981
+ description: "Change position mode: hedgeMode true = dual side (hedge), false = one-way (signed; 30-day cooldown).",
2982
+ inputSchema: z9.object({ hedgeMode: z9.boolean().describe("true = hedge mode, false = one-way") }),
2983
+ handler: async ({ hedgeMode }) => textResult(await a.setPositionMode(hedgeMode))
2984
+ },
2985
+ {
2986
+ name: "futures_api_trading_status",
2987
+ description: "Get futures trading quantitative rules indicators and disable intervals (signed).",
2988
+ inputSchema: z9.object({}),
2989
+ handler: async () => textResult(await a.apiTradingStatus())
2990
+ },
2991
+ {
2992
+ name: "futures_position_margin_history",
2993
+ description: "Get isolated position margin change history. type: 1 = add margin, 2 = reduce margin (signed).",
2994
+ inputSchema: z9.object({ symbol: z9.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), type: z9.enum(["1", "2"]).describe("1 = add margin, 2 = reduce margin"), startTime, endTime, limit: limit.default(50) }),
2995
+ handler: async ({ symbol: symbol3, type, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await a.positionMarginHistory({ symbol: normalizeSymbol(symbol3), type: Number(type), startTime: startTime5, endTime: endTime5, limit: Number(limit4) }))
2996
+ },
2997
+ {
2998
+ name: "futures_rate_limit_order",
2999
+ description: "Query the current user order rate limit usage (signed).",
3000
+ inputSchema: z9.object({}),
3001
+ handler: async () => textResult(await a.rateLimitOrder())
3002
+ },
3003
+ {
3004
+ name: "futures_request_order_download",
3005
+ description: "Request a futures order-history download (async, 7-day window). Returns a downloadId (signed).",
3006
+ inputSchema: z9.object({ symbol: optionalSymbol, startTime, endTime }),
3007
+ handler: async ({ symbol: symbol3, startTime: startTime5, endTime: endTime5 }) => textResult(await a.requestOrderDownload({ symbol: symbol3 ? normalizeSymbol(symbol3) : void 0, startTime: startTime5, endTime: endTime5 }))
3008
+ },
3009
+ {
3010
+ name: "futures_order_download_status",
3011
+ description: "Get the futures order-history download link by downloadId (signed).",
3012
+ inputSchema: z9.object({ downloadId: z9.string().min(1) }),
3013
+ handler: async ({ downloadId }) => textResult(await a.getOrderDownloadStatus(downloadId))
3014
+ },
3015
+ {
3016
+ name: "futures_request_trade_download",
3017
+ description: "Request a futures trade-history download (async, 7-day window). Returns a downloadId (signed).",
3018
+ inputSchema: z9.object({ symbol: optionalSymbol, startTime, endTime }),
3019
+ handler: async ({ symbol: symbol3, startTime: startTime5, endTime: endTime5 }) => textResult(await a.requestTradeDownload({ symbol: symbol3 ? normalizeSymbol(symbol3) : void 0, startTime: startTime5, endTime: endTime5 }))
3020
+ },
3021
+ {
3022
+ name: "futures_trade_download_status",
3023
+ description: "Get the futures trade-history download link by downloadId (signed).",
3024
+ inputSchema: z9.object({ downloadId: z9.string().min(1) }),
3025
+ handler: async ({ downloadId }) => textResult(await a.getTradeDownloadStatus(downloadId))
3026
+ },
3027
+ {
3028
+ name: "futures_account_config",
3029
+ description: "Get futures account configuration: fee tier, trading permissions, dual-side position setting (signed).",
3030
+ inputSchema: z9.object({}),
3031
+ handler: async () => textResult(await a.accountConfig())
3032
+ },
3033
+ {
3034
+ name: "futures_portfolio_margin_account_info",
3035
+ description: "Get Portfolio Margin account info for an asset: cross margin balance and liability (signed).",
3036
+ inputSchema: z9.object({ asset: z9.string().min(1).describe("Asset, e.g. USDT") }),
3037
+ handler: async ({ asset }) => textResult(await a.pmAccountInfo(asset))
3038
+ },
3039
+ {
3040
+ name: "futures_request_income_download",
3041
+ description: "Request a futures income/transaction-history download (async). Returns a downloadId (signed).",
3042
+ inputSchema: z9.object({ startTime: startTime.unwrap(), endTime: endTime.unwrap() }),
3043
+ handler: async ({ startTime: startTime5, endTime: endTime5 }) => textResult(await a.requestIncomeDownload({ startTime: startTime5, endTime: endTime5 }))
3044
+ },
3045
+ {
3046
+ name: "futures_income_download_status",
3047
+ description: "Get the futures income/transaction-history download link by downloadId (signed).",
3048
+ inputSchema: z9.object({ downloadId: z9.string().min(1) }),
3049
+ handler: async ({ downloadId }) => textResult(await a.getIncomeDownloadStatus(downloadId))
3050
+ }
3051
+ ];
3052
+ }
3053
+
3054
+ // src/tools/derived.tools.ts
3055
+ import { z as z10 } from "zod";
3056
+ var symbol = z10.string().min(1).describe("USD-M pair, e.g. BTCUSDT");
3057
+ function derivedTools(client) {
3058
+ const ops = client.futures.ops;
3059
+ return [
3060
+ {
3061
+ name: "futures_symbol_rules",
3062
+ description: "Get the tradeable rules for a symbol in one call: tick size, step size, min/max quantity, market-order lot bounds and minimum notional, flattened out of exchangeInfo filters. Call this before sizing or pricing any order.",
3063
+ inputSchema: z10.object({ symbol, refresh: z10.boolean().default(false).describe("Bypass the 60s cache") }),
3064
+ handler: async ({ symbol: s, refresh }) => textResult(await ops.symbolRules(normalizeSymbol(s), { refresh }))
3065
+ },
3066
+ {
3067
+ name: "futures_quantize",
3068
+ description: "Round a price to the symbol's tick size and a quantity to its step size, returning exchange-ready strings. Use this instead of rounding numbers yourself \u2014 Binance rejects off-tick prices (-1111).",
3069
+ inputSchema: z10.object({
3070
+ symbol,
3071
+ price: z10.number().positive().optional(),
3072
+ quantity: z10.number().positive().optional(),
3073
+ marketOrder: z10.boolean().default(false).describe("Use MARKET_LOT_SIZE bounds instead of LOT_SIZE")
3074
+ }),
3075
+ handler: async ({ symbol: s, price, quantity, marketOrder }) => textResult(await ops.quantize(normalizeSymbol(s), { price, quantity, marketOrder }))
3076
+ },
3077
+ {
3078
+ name: "futures_size_position",
3079
+ description: "Convert a risk budget into an exchange-ready order quantity. Given a stop price and either riskAmount (quote currency) or riskPct (percent of available balance), returns the quantity rounded down to the symbol step size, plus the notional, required margin and every constraint that failed (min notional, lot bounds, wrong-side stop). Never increases risk to satisfy a minimum \u2014 it reports instead.",
3080
+ inputSchema: z10.object({
3081
+ symbol,
3082
+ side: z10.enum(["BUY", "SELL"]),
3083
+ stopPrice: z10.number().positive().describe("Protective stop price"),
3084
+ entryPrice: z10.number().positive().optional().describe("Defaults to current mark price"),
3085
+ riskAmount: z10.number().positive().optional().describe("Quote-currency amount to risk, e.g. 50 USDT"),
3086
+ riskPct: z10.number().positive().max(100).optional().describe("Percent of available balance to risk (signed call)"),
3087
+ leverage: z10.number().int().min(1).max(125).optional().describe("Used to report required margin"),
3088
+ marketOrder: z10.boolean().default(false)
3089
+ }),
3090
+ handler: async (args) => textResult(await ops.sizePosition({ ...args, symbol: normalizeSymbol(args.symbol) }))
3091
+ },
3092
+ {
3093
+ name: "futures_close_position",
3094
+ description: "Flatten an open position with a reduce-only market order. Reads the current position, derives the closing side from its sign, and automatically uses reduceOnly in one-way mode vs positionSide in hedge mode (Binance rejects the wrong one). Set dryRun to preview the order without sending it. Signed, destructive.",
3095
+ inputSchema: z10.object({
3096
+ symbol,
3097
+ positionSide: z10.enum(["BOTH", "LONG", "SHORT"]).optional().describe("Required when hedge mode has both sides open"),
3098
+ portion: z10.number().positive().max(1).default(1).describe("Fraction of the position to close, e.g. 0.5 for half"),
3099
+ dryRun: z10.boolean().default(false)
3100
+ }),
3101
+ handler: async (args) => textResult(await ops.closePosition({ ...args, symbol: normalizeSymbol(args.symbol) }))
3102
+ },
3103
+ {
3104
+ name: "futures_market_snapshot",
3105
+ description: "One-call market picture for a symbol: 24h stats, mark/index price with funding, open interest and long/short positioning. Replaces four separate primitive calls; individual feeds degrade to an error field rather than failing the whole snapshot.",
3106
+ inputSchema: z10.object({
3107
+ symbol,
3108
+ period: z10.enum(["5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "1d"]).default("1h")
3109
+ }),
3110
+ handler: async ({ symbol: s, period: period2 }) => textResult(await ops.marketSnapshot(normalizeSymbol(s), { period: period2 }))
3111
+ },
3112
+ {
3113
+ name: "futures_account_overview",
3114
+ description: "One-call account picture: funded balances, open positions only (zero-size entries filtered out), working orders, and totals for unrealized PnL and gross notional. Signed.",
3115
+ inputSchema: z10.object({}),
3116
+ handler: async () => textResult(await ops.accountOverview())
3117
+ },
3118
+ {
3119
+ name: "futures_place_bracket_order",
3120
+ description: "Place an entry order together with its protective stop-loss and optional take-profit, with all prices quantized to the symbol tick. Returns each leg separately; if a protective leg fails the response is flagged protectionComplete=false and the entry is NOT auto-cancelled (it may already have filled). Signed, destructive.",
3121
+ inputSchema: z10.object({
3122
+ symbol,
3123
+ side: z10.enum(["BUY", "SELL"]),
3124
+ quantity: z10.union([z10.string(), z10.number()]),
3125
+ entryPrice: z10.number().positive().optional().describe("Omit for a MARKET entry"),
3126
+ stopLossPrice: z10.number().positive(),
3127
+ takeProfitPrice: z10.number().positive().optional(),
3128
+ positionSide: z10.enum(["BOTH", "LONG", "SHORT"]).optional(),
3129
+ workingType: z10.enum(["MARK_PRICE", "CONTRACT_PRICE"]).default("MARK_PRICE")
3130
+ }),
3131
+ handler: async (args) => textResult(await ops.placeBracketOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3132
+ }
3133
+ ];
3134
+ }
3135
+
3136
+ // src/tools/market-data.tools.ts
3137
+ import { z as z11 } from "zod";
3138
+ var u = (s) => s.optional();
3139
+ var optSymbol = u(z11.string().min(1)).describe("USD-M pair, e.g. BTCUSDT");
3140
+ var limitDefault = z11.number().int().positive().max(1500).default(100);
3141
+ var startTime2 = z11.number().int().positive().optional();
3142
+ var endTime2 = z11.number().int().positive().optional();
3143
+ var period = z11.enum(["5m", "15m", "30m", "1h", "2h", "4h", "6h", "12h", "1d"]).default("1h");
3144
+ var contractType = z11.enum(["perpetual", "current_quarter", "next_quarter"]).default("perpetual");
3145
+ function marketDataTools(client) {
3146
+ const m = client.futures.market;
3147
+ const d = client.futures.data;
3148
+ const tools = [
3149
+ {
3150
+ name: "futures_ping",
3151
+ description: "Test connectivity to the Binance USD-M futures REST API. Returns empty object {} on success.",
3152
+ inputSchema: z11.object({}),
3153
+ handler: async () => textResult(await m.ping())
3154
+ },
3155
+ {
3156
+ name: "futures_server_time",
3157
+ description: "Get the current Binance USD-M futures server time in milliseconds since epoch.",
3158
+ inputSchema: z11.object({}),
3159
+ handler: async () => textResult(await m.serverTime())
3160
+ },
3161
+ {
3162
+ name: "futures_exchange_info",
3163
+ description: "Get exchange trading rules and symbol metadata: contract type, status, base/quote assets, price/quantity filters, tick size, step size, notional and leverage brackets, delivery dates.",
3164
+ inputSchema: z11.object({}),
3165
+ handler: async () => textResult(await m.exchangeInfo())
3166
+ },
3167
+ {
3168
+ name: "futures_ticker_price",
3169
+ description: "Get latest price for one symbol (v1).",
3170
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3171
+ handler: async ({ symbol: symbol3 }) => textResult(await m.tickerPrice(normalizeSymbol(symbol3)))
3172
+ },
3173
+ {
3174
+ name: "futures_ticker_price_v2",
3175
+ description: "Get latest price for one symbol, or for all symbols when symbol is omitted (v2 endpoint). Use all-symbols mode for market overviews.",
3176
+ inputSchema: z11.object({ symbol: optSymbol }),
3177
+ handler: async ({ symbol: symbol3 }) => textResult(await m.tickerPriceV2(symbol3 ? normalizeSymbol(symbol3) : void 0))
3178
+ },
3179
+ {
3180
+ name: "futures_ticker_24hr",
3181
+ description: "Get 24-hour rolling price change statistics for a symbol: price change %, high, low, volume, quote volume, open interest.",
3182
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3183
+ handler: async ({ symbol: symbol3 }) => textResult(await m.ticker24hr(normalizeSymbol(symbol3)))
3184
+ },
3185
+ {
3186
+ name: "futures_book_ticker",
3187
+ description: "Get best bid/ask (top of book) for a single symbol (v1).",
3188
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3189
+ handler: async ({ symbol: symbol3 }) => textResult(await m.bookTicker(normalizeSymbol(symbol3)))
3190
+ },
3191
+ {
3192
+ name: "futures_book_ticker_v2",
3193
+ description: "Get best bid/ask (top of book) for one symbol, or all symbols when omitted (v2 endpoint).",
3194
+ inputSchema: z11.object({ symbol: optSymbol }),
3195
+ handler: async ({ symbol: symbol3 }) => textResult(await m.bookTickerV2(symbol3 ? normalizeSymbol(symbol3) : void 0))
3196
+ },
3197
+ {
3198
+ name: "futures_order_book",
3199
+ description: "Get order book depth for a symbol. Limits: 5, 10, 20, 50, 100, 500, 1000.",
3200
+ inputSchema: z11.object({
3201
+ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
3202
+ limit: z11.enum(["5", "10", "20", "50", "100", "500", "1000"]).default("100")
3203
+ }),
3204
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await m.depth(normalizeSymbol(symbol3), Number(limit4)))
3205
+ },
3206
+ {
3207
+ name: "futures_recent_trades",
3208
+ description: "Get the most recent executed trades for a symbol (public endpoint).",
3209
+ inputSchema: z11.object({
3210
+ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
3211
+ limit: limitDefault.default(500)
3212
+ }),
3213
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await m.trades(normalizeSymbol(symbol3), Number(limit4)))
3214
+ },
3215
+ {
3216
+ name: "futures_historical_trades",
3217
+ description: "Get older trades for a symbol by fromId (older than recent).",
3218
+ inputSchema: z11.object({
3219
+ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
3220
+ limit: limitDefault.default(500),
3221
+ fromId: z11.number().int().positive().optional()
3222
+ }),
3223
+ handler: async ({ symbol: symbol3, limit: limit4, fromId }) => textResult(await m.historicalTrades(normalizeSymbol(symbol3), { limit: Number(limit4), fromId }))
3224
+ },
3225
+ {
3226
+ name: "futures_agg_trades",
3227
+ description: "Get compressed/aggregate trades for a symbol within an optional time window. Each entry aggregates trades with the same price and side.",
3228
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), limit: limitDefault.default(500), startTime: startTime2, endTime: endTime2 }),
3229
+ handler: async ({ symbol: symbol3, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.aggTrades(normalizeSymbol(symbol3), { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3230
+ },
3231
+ {
3232
+ name: "futures_klines",
3233
+ description: "Get candlestick (OHLCV) data for a symbol. Intervals: 1m,3m,5m,15m,30m,1h,2h,4h,6h,8h,12h,1d,3d,1w,1M. Max 1500 per request.",
3234
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), interval: KlineIntervalSchema, limit: limitDefault.default(500), startTime: startTime2, endTime: endTime2 }),
3235
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.klines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3236
+ },
3237
+ {
3238
+ name: "futures_continuous_klines",
3239
+ description: "Get klines for a continuous contract (perpetual, current_quarter, next_quarter) identified by base pair (e.g. BTCUSDT).",
3240
+ inputSchema: z11.object({
3241
+ symbol: z11.string().min(1).describe("Base pair, e.g. BTCUSDT"),
3242
+ contractType,
3243
+ interval: KlineIntervalSchema,
3244
+ limit: limitDefault.default(500),
3245
+ startTime: startTime2,
3246
+ endTime: endTime2
3247
+ }),
3248
+ handler: async ({ symbol: symbol3, contractType: contractType2, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.continuousKlines(normalizeSymbol(symbol3), contractType2, interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3249
+ },
3250
+ {
3251
+ name: "futures_index_price_klines",
3252
+ description: "Get index price candlesticks for a pair (e.g. BTCUSDT index).",
3253
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("Base pair, e.g. BTCUSDT"), interval: KlineIntervalSchema, limit: limitDefault.default(500), startTime: startTime2, endTime: endTime2 }),
3254
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.indexPriceKlines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3255
+ },
3256
+ {
3257
+ name: "futures_mark_price_klines",
3258
+ description: "Get mark price candlesticks for a symbol.",
3259
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), interval: KlineIntervalSchema, limit: limitDefault.default(500), startTime: startTime2, endTime: endTime2 }),
3260
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.markPriceKlines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3261
+ },
3262
+ {
3263
+ name: "futures_trading_day_ticker",
3264
+ description: "Get the trading-day ticker (v1: rolling window stats by symbol, including open, close, high, low, volume, closeTime).",
3265
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3266
+ handler: async ({ symbol: symbol3 }) => textResult(await m.tradingDayTicker(normalizeSymbol(symbol3)))
3267
+ },
3268
+ {
3269
+ name: "futures_mark_price",
3270
+ description: "Get mark price, index price, estimated settle price, last funding rate, next funding time and interest rate for a symbol.",
3271
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3272
+ handler: async ({ symbol: symbol3 }) => textResult(await d.premiumIndex(normalizeSymbol(symbol3)))
3273
+ },
3274
+ {
3275
+ name: "futures_funding_rate_history",
3276
+ description: "Get historical funding rates for a symbol.",
3277
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), limit: limitDefault.default(100), startTime: startTime2, endTime: endTime2 }),
3278
+ handler: async ({ symbol: symbol3, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await d.fundingRateHistory(normalizeSymbol(symbol3), { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3279
+ },
3280
+ {
3281
+ name: "futures_funding_info",
3282
+ description: "Get funding rate configuration for all symbols with adjusted funding cap/floor or interval (e.g. under extreme volatility).",
3283
+ inputSchema: z11.object({}),
3284
+ handler: async () => textResult(await d.fundingInfo())
3285
+ },
3286
+ {
3287
+ name: "futures_open_interest",
3288
+ description: "Get the current total open interest for a symbol.",
3289
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3290
+ handler: async ({ symbol: symbol3 }) => textResult(await d.openInterest(normalizeSymbol(symbol3)))
3291
+ },
3292
+ {
3293
+ name: "futures_open_interest_hist",
3294
+ description: "Get historical open interest statistics per period.",
3295
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), period, limit: limitDefault.default(30) }),
3296
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4 }) => textResult(await d.openInterestHist(normalizeSymbol(symbol3), period2, Number(limit4)))
3297
+ },
3298
+ {
3299
+ name: "futures_top_long_short_account_ratio",
3300
+ description: "Get top trader long/short account ratio (number of accounts) for a symbol.",
3301
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), period, limit: limitDefault.default(30) }),
3302
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4 }) => textResult(await d.topLongShortAccountRatio(normalizeSymbol(symbol3), period2, Number(limit4)))
3303
+ },
3304
+ {
3305
+ name: "futures_top_long_short_position_ratio",
3306
+ description: "Get top trader long/short position ratio (accounts with net positions) for a symbol.",
3307
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), period, limit: limitDefault.default(30) }),
3308
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4 }) => textResult(await d.topLongShortPositionRatio(normalizeSymbol(symbol3), period2, Number(limit4)))
3309
+ },
3310
+ {
3311
+ name: "futures_global_long_short_ratio",
3312
+ description: "Get global long/short account ratio for a symbol (all accounts).",
3313
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), period, limit: limitDefault.default(30) }),
3314
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4 }) => textResult(await d.globalLongShortAccountRatio(normalizeSymbol(symbol3), period2, Number(limit4)))
3315
+ },
3316
+ {
3317
+ name: "futures_taker_long_short_ratio",
3318
+ description: "Get taker buy/sell volume ratio for a symbol.",
3319
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), period, limit: limitDefault.default(30) }),
3320
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4 }) => textResult(await d.takerLongShortRatio(normalizeSymbol(symbol3), period2, Number(limit4)))
3321
+ },
3322
+ {
3323
+ name: "futures_basis",
3324
+ description: "Get basis data (spot-futures spread, mark price - index price) for a pair and contract type.",
3325
+ inputSchema: z11.object({
3326
+ symbol: z11.string().min(1).describe("Base pair, e.g. BTCUSDT"),
3327
+ period,
3328
+ limit: limitDefault.default(30),
3329
+ startTime: startTime2,
3330
+ endTime: endTime2
3331
+ }),
3332
+ handler: async ({ symbol: symbol3, period: period2, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await d.basis(normalizeSymbol(symbol3), period2, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3333
+ },
3334
+ {
3335
+ name: "futures_asset_index",
3336
+ description: "Get multi-assets mode asset index (collateral price) for one asset or all assets.",
3337
+ inputSchema: z11.object({ symbol: optSymbol.describe("Asset index symbol, e.g. ADAUSD") }),
3338
+ handler: async ({ symbol: symbol3 }) => textResult(await d.assetIndex(symbol3 ? normalizeSymbol(symbol3) : void 0))
3339
+ },
3340
+ {
3341
+ name: "futures_composite_index_info",
3342
+ description: "Get composite index symbol information (e.g. DEFIUSDT) with constituent weights.",
3343
+ inputSchema: z11.object({ symbol: optSymbol }),
3344
+ handler: async ({ symbol: symbol3 }) => textResult(await d.compositeIndexInfo(symbol3 ? normalizeSymbol(symbol3) : void 0))
3345
+ },
3346
+ {
3347
+ name: "futures_insurance_balance",
3348
+ description: "Get insurance fund balance snapshots for a symbol.",
3349
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BNBUSDT"), limit: limitDefault.default(100), startTime: startTime2, endTime: endTime2 }),
3350
+ handler: async ({ symbol: symbol3, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await d.insuranceFundBalance({ symbol: normalizeSymbol(symbol3), limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3351
+ },
3352
+ {
3353
+ name: "futures_index_price_constituents",
3354
+ description: "Get the index price constituents (exchange weights) for a symbol.",
3355
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT") }),
3356
+ handler: async ({ symbol: symbol3 }) => textResult(await d.indexPriceConstituents(normalizeSymbol(symbol3)))
3357
+ },
3358
+ {
3359
+ name: "futures_premium_index_klines",
3360
+ description: "Get premium index candlesticks for a symbol (mark price premium over index price).",
3361
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), interval: KlineIntervalSchema, limit: limitDefault.default(500), startTime: startTime2, endTime: endTime2 }),
3362
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.premiumIndexKlines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3363
+ },
3364
+ {
3365
+ name: "futures_rpi_depth",
3366
+ description: "Get the RPI (Retail Price Improvement) order book depth for a symbol.",
3367
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("USD-M pair, e.g. BTCUSDT"), limit: z11.number().int().positive().default(1e3) }),
3368
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await m.rpiDepth(normalizeSymbol(symbol3), Number(limit4)))
3369
+ },
3370
+ {
3371
+ name: "futures_delivery_price",
3372
+ description: "Get historical quarterly contract settlement/delivery prices for a pair.",
3373
+ inputSchema: z11.object({ symbol: z11.string().min(1).describe("Base pair, e.g. BTCUSDT") }),
3374
+ handler: async ({ symbol: symbol3 }) => textResult(await d.deliveryPrice(normalizeSymbol(symbol3)))
3375
+ },
3376
+ {
3377
+ name: "futures_symbol_adl_risk",
3378
+ description: "Get the auto-deleveraging (ADL) risk level for one symbol or all symbols on the account (signed).",
3379
+ inputSchema: z11.object({ symbol: optSymbol }),
3380
+ handler: async ({ symbol: symbol3 }) => textResult(await d.symbolAdlRisk(symbol3 ? normalizeSymbol(symbol3) : void 0))
3381
+ },
3382
+ {
3383
+ name: "futures_adl_quantile",
3384
+ description: "Get the position ADL (auto-deleveraging) quantile estimation for one symbol or all symbols (signed).",
3385
+ inputSchema: z11.object({ symbol: optSymbol }),
3386
+ handler: async ({ symbol: symbol3 }) => textResult(await d.adlQuantile(symbol3 ? normalizeSymbol(symbol3) : void 0))
3387
+ },
3388
+ {
3389
+ name: "futures_force_orders",
3390
+ description: "Get the user's force-liquidation orders (LIQUIDATION) and ADL orders for a symbol or all symbols (signed).",
3391
+ inputSchema: z11.object({
3392
+ symbol: optSymbol,
3393
+ autoCloseType: z11.enum(["LIQUIDATION", "ADL"]).optional(),
3394
+ startTime: startTime2,
3395
+ endTime: endTime2,
3396
+ limit: limitDefault.default(50)
3397
+ }),
3398
+ handler: async ({ symbol: symbol3, autoCloseType, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(
3399
+ await d.forceOrders({
3400
+ symbol: symbol3 ? normalizeSymbol(symbol3) : void 0,
3401
+ autoCloseType,
3402
+ startTime: startTime5,
3403
+ endTime: endTime5,
3404
+ limit: Number(limit4)
3405
+ })
3406
+ )
3407
+ }
3408
+ ];
3409
+ return tools;
3410
+ }
3411
+
3412
+ // src/tools/paper.tools.ts
3413
+ import { z as z12 } from "zod";
3414
+ function createPaperContext(client, initialState = {}) {
3415
+ return {
3416
+ client,
3417
+ state: { balance: initialState.balance ?? 1e4, positions: initialState.positions ?? [], history: initialState.history ?? [] }
3418
+ };
3419
+ }
3420
+ function pnl(position, price) {
3421
+ const direction = position.side === "LONG" ? 1 : -1;
3422
+ return (price - position.entryPrice) * position.quantity * direction;
3423
+ }
3424
+ function paperTools(ctx) {
3425
+ const s = ctx.state;
3426
+ const t = ctx.client.futures.market;
3427
+ return [
3428
+ {
3429
+ name: "paper_init",
3430
+ description: "Initialize paper-trading state (reset balances, positions, history). No API keys required.",
3431
+ inputSchema: z12.object({ balance: z12.number().positive().default(1e4).describe("Starting USDT balance") }),
3432
+ handler: async ({ balance }) => {
3433
+ s.balance = balance;
3434
+ s.positions = [];
3435
+ s.history = [];
3436
+ return textResult(s);
3437
+ }
3438
+ },
3439
+ {
3440
+ name: "paper_balance",
3441
+ description: "Get current paper-trading balance and open-position equity (uses live market prices if an open position exists). No API keys required.",
3442
+ inputSchema: z12.object({}),
3443
+ handler: async () => textResult(s)
3444
+ },
3445
+ {
3446
+ name: "paper_open_position",
3447
+ description: "Open an in-memory paper position at the live ticker price (or a fixed price). No API keys required.",
3448
+ inputSchema: z12.object({
3449
+ symbol: z12.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
3450
+ side: z12.enum(["LONG", "SHORT"]),
3451
+ quantity: z12.number().positive(),
3452
+ price: z12.number().positive().optional().describe("If omitted, uses live ticker price")
3453
+ }),
3454
+ handler: async ({ symbol: symbol3, side: side3, quantity, price }) => {
3455
+ const sym = normalizeSymbol(symbol3);
3456
+ const market = price ?? Number((await t.tickerPrice(sym)).price);
3457
+ const position = { id: Date.now().toString(), symbol: sym, side: side3, quantity, entryPrice: market, openedAt: (/* @__PURE__ */ new Date()).toISOString() };
3458
+ s.positions.push(position);
3459
+ s.history.push({ type: "open", ...position });
3460
+ return textResult(position);
3461
+ }
3462
+ },
3463
+ {
3464
+ name: "paper_close_position",
3465
+ description: "Close a paper position by id (or symbol) and realize PnL at the live/last price.",
3466
+ inputSchema: z12.object({
3467
+ id: z12.string().optional().describe("Position id from paper_open_position"),
3468
+ symbol: z12.string().min(1).optional().describe("Close the position for this symbol"),
3469
+ price: z12.number().positive().optional().describe("If omitted, uses live ticker price")
3470
+ }),
3471
+ handler: async ({ id, symbol: symbol3, price }) => {
3472
+ const idx = s.positions.findIndex((p) => id ? p.id === id : normalizeSymbol(p.symbol) === normalizeSymbol(symbol3 ?? ""));
3473
+ if (idx === -1) throw new Error("paper position not found");
3474
+ const position = s.positions[idx];
3475
+ const market = price ?? Number((await t.tickerPrice(position.symbol)).price);
3476
+ const realizedPnl = pnl(position, market);
3477
+ s.balance += realizedPnl;
3478
+ const closed = { type: "close", ...position, exitPrice: market, realizedPnl, closedAt: (/* @__PURE__ */ new Date()).toISOString() };
3479
+ s.positions.splice(idx, 1);
3480
+ s.history.push(closed);
3481
+ return textResult(closed);
3482
+ }
3483
+ },
3484
+ {
3485
+ name: "paper_positions",
3486
+ description: "List open paper positions with live unrealized PnL (marked to market).",
3487
+ inputSchema: z12.object({}),
3488
+ handler: async () => {
3489
+ const out = await Promise.all(
3490
+ s.positions.map(async (p) => {
3491
+ const price = Number((await t.tickerPrice(p.symbol)).price);
3492
+ return { ...p, markPrice: price, unrealizedPnl: pnl(p, price) };
3493
+ })
3494
+ );
3495
+ return textResult(out);
3496
+ }
3497
+ },
3498
+ {
3499
+ name: "paper_history",
3500
+ description: "List paper-trading history of open/close events with realized PnL.",
3501
+ inputSchema: z12.object({}),
3502
+ handler: async () => textResult(s.history)
3503
+ },
3504
+ {
3505
+ name: "paper_summary",
3506
+ description: "Summarize paper-trading state: balance, open positions, trade count.",
3507
+ inputSchema: z12.object({}),
3508
+ handler: async () => textResult({ balance: s.balance, openPositions: s.positions.length, trades: s.history.length })
3509
+ }
3510
+ ];
3511
+ }
3512
+
3513
+ // src/tools/spot.tools.ts
3514
+ import { z as z13 } from "zod";
3515
+ var spotSymbol = z13.string().min(1).describe("Spot pair, e.g. BTCUSDT");
3516
+ var optionalSymbol2 = z13.string().min(1).describe("Spot pair, e.g. BTCUSDT").optional();
3517
+ var limit2 = z13.number().int().positive().max(1e3).default(100);
3518
+ var startTime3 = z13.number().int().positive().optional();
3519
+ var endTime3 = z13.number().int().positive().optional();
3520
+ var side = z13.enum(["BUY", "SELL"]);
3521
+ var spotOrderParamsSchema = z13.object({
3522
+ symbol: spotSymbol,
3523
+ side,
3524
+ type: z13.enum(["LIMIT", "MARKET", "LIMIT_MAKER", "STOP_LOSS", "STOP_LOSS_LIMIT", "TAKE_PROFIT", "TAKE_PROFIT_LIMIT"]),
3525
+ quantity: z13.union([z13.string(), z13.number()]).optional().describe("Base-asset quantity; omit if using quoteOrderQty"),
3526
+ quoteOrderQty: z13.union([z13.string(), z13.number()]).optional().describe("Quote-asset quantity (e.g. spend 100 USDT on a MARKET order)"),
3527
+ price: z13.union([z13.string(), z13.number()]).optional().describe("Required for LIMIT / STOP_LOSS_LIMIT / TAKE_PROFIT_LIMIT"),
3528
+ stopPrice: z13.union([z13.string(), z13.number()]).optional().describe("Trigger price for STOP_LOSS / TAKE_PROFIT variants"),
3529
+ timeInForce: z13.enum(["GTC", "IOC", "FOK"]).optional().describe("Required for LIMIT"),
3530
+ newClientOrderId: z13.string().optional(),
3531
+ newOrderRespType: z13.enum(["ACK", "RESULT", "FULL"]).optional(),
3532
+ icebergQty: z13.union([z13.string(), z13.number()]).optional(),
3533
+ selfTradePreventionMode: z13.enum(["NONE", "EXPIRE_TAKER", "EXPIRE_MAKER", "EXPIRE_BOTH"]).optional()
3534
+ });
3535
+ function spotTools(client) {
3536
+ const m = client.spot.market;
3537
+ const a = client.spot.account;
3538
+ const t = client.spot.trading;
3539
+ const tools = [
3540
+ // ---- Market data (public) ----
3541
+ {
3542
+ name: "spot_ping",
3543
+ description: "Test connectivity to the Binance Spot REST API.",
3544
+ inputSchema: z13.object({}),
3545
+ handler: async () => textResult(await m.ping())
3546
+ },
3547
+ {
3548
+ name: "spot_server_time",
3549
+ description: "Get the current Binance Spot server time in ms since epoch.",
3550
+ inputSchema: z13.object({}),
3551
+ handler: async () => textResult(await m.serverTime())
3552
+ },
3553
+ {
3554
+ name: "spot_exchange_info",
3555
+ description: "Get Spot exchange trading rules and symbol metadata (status, base/quote assets, filters, permissions).",
3556
+ inputSchema: z13.object({}),
3557
+ handler: async () => textResult(await m.exchangeInfo())
3558
+ },
3559
+ {
3560
+ name: "spot_ticker_price",
3561
+ description: "Get the latest price for a Spot symbol.",
3562
+ inputSchema: z13.object({ symbol: spotSymbol }),
3563
+ handler: async ({ symbol: symbol3 }) => textResult(await m.tickerPrice(normalizeSymbol(symbol3)))
3564
+ },
3565
+ {
3566
+ name: "spot_ticker_24hr",
3567
+ description: "Get 24-hour rolling price change statistics for a Spot symbol.",
3568
+ inputSchema: z13.object({ symbol: spotSymbol }),
3569
+ handler: async ({ symbol: symbol3 }) => textResult(await m.ticker24hr(normalizeSymbol(symbol3)))
3570
+ },
3571
+ {
3572
+ name: "spot_book_ticker",
3573
+ description: "Get the best bid/ask (top of book) for a Spot symbol.",
3574
+ inputSchema: z13.object({ symbol: spotSymbol }),
3575
+ handler: async ({ symbol: symbol3 }) => textResult(await m.bookTicker(normalizeSymbol(symbol3)))
3576
+ },
3577
+ {
3578
+ name: "spot_order_book",
3579
+ description: "Get the Spot order book depth for a symbol (limits 5/10/20/50/100/500/1000/5000).",
3580
+ inputSchema: z13.object({
3581
+ symbol: spotSymbol,
3582
+ limit: z13.enum(["5", "10", "20", "50", "100", "500", "1000", "5000"]).default("100")
3583
+ }),
3584
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await m.depth(normalizeSymbol(symbol3), Number(limit4)))
3585
+ },
3586
+ {
3587
+ name: "spot_recent_trades",
3588
+ description: "Get the most recent executed trades for a Spot symbol.",
3589
+ inputSchema: z13.object({ symbol: spotSymbol, limit: limit2.default(500) }),
3590
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await m.trades(normalizeSymbol(symbol3), Number(limit4)))
3591
+ },
3592
+ {
3593
+ name: "spot_historical_trades",
3594
+ description: "Get older Spot trades for a symbol by fromId.",
3595
+ inputSchema: z13.object({ symbol: spotSymbol, limit: limit2.default(500), fromId: z13.number().int().positive().optional() }),
3596
+ handler: async ({ symbol: symbol3, limit: limit4, fromId }) => textResult(await m.historicalTrades(normalizeSymbol(symbol3), { limit: Number(limit4), fromId }))
3597
+ },
3598
+ {
3599
+ name: "spot_agg_trades",
3600
+ description: "Get compressed/aggregate Spot trades for a symbol within an optional time window.",
3601
+ inputSchema: z13.object({ symbol: spotSymbol, limit: limit2.default(500), startTime: startTime3, endTime: endTime3 }),
3602
+ handler: async ({ symbol: symbol3, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.aggTrades(normalizeSymbol(symbol3), { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3603
+ },
3604
+ {
3605
+ name: "spot_klines",
3606
+ description: "Get Spot candlesticks (OHLCV). Intervals 1m\u20261M; max 1000 per request.",
3607
+ inputSchema: z13.object({ symbol: spotSymbol, interval: KlineIntervalSchema, limit: limit2.default(500), startTime: startTime3, endTime: endTime3 }),
3608
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.klines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3609
+ },
3610
+ {
3611
+ name: "spot_ui_klines",
3612
+ description: "Get Spot candlesticks via the optimized uiKlines endpoint (same shape as spot_klines).",
3613
+ inputSchema: z13.object({ symbol: spotSymbol, interval: KlineIntervalSchema, limit: limit2.default(500), startTime: startTime3, endTime: endTime3 }),
3614
+ handler: async ({ symbol: symbol3, interval, limit: limit4, startTime: startTime5, endTime: endTime5 }) => textResult(await m.uiKlines(normalizeSymbol(symbol3), interval, { limit: Number(limit4), startTime: startTime5, endTime: endTime5 }))
3615
+ },
3616
+ {
3617
+ name: "spot_avg_price",
3618
+ description: "Get the current average price (5-minute rolling average) for a Spot symbol.",
3619
+ inputSchema: z13.object({ symbol: spotSymbol }),
3620
+ handler: async ({ symbol: symbol3 }) => textResult(await m.avgPrice(normalizeSymbol(symbol3)))
3621
+ },
3622
+ {
3623
+ name: "spot_rolling_window_ticker",
3624
+ description: "Get rolling-window price change statistics (1d/7d/30d). Omit symbol for all symbols.",
3625
+ inputSchema: z13.object({
3626
+ symbol: optionalSymbol2,
3627
+ windowSize: z13.enum(["1d", "7d", "30d"]).optional()
3628
+ }),
3629
+ handler: async ({ symbol: symbol3, windowSize }) => textResult(await m.rollingWindowTicker(symbol3 ? normalizeSymbol(symbol3) : void 0, windowSize))
3630
+ },
3631
+ {
3632
+ name: "spot_trading_day_ticker",
3633
+ description: "Get the Spot trading-day ticker (open/close/high/low/volume by UTC day). Omit symbol for all symbols.",
3634
+ inputSchema: z13.object({ symbol: optionalSymbol2, timeZone: z13.string().optional(), type: z13.enum(["FULL", "MINI"]).optional() }),
3635
+ handler: async ({ symbol: symbol3, timeZone, type }) => textResult(await m.tradingDayTicker(symbol3 ? normalizeSymbol(symbol3) : void 0, { timeZone, type }))
3636
+ },
3637
+ // ---- Account (signed) ----
3638
+ {
3639
+ name: "spot_account",
3640
+ description: "Get the Spot account: balances (free/locked), commissions, permissions (signed).",
3641
+ inputSchema: z13.object({}),
3642
+ handler: async () => textResult(await a.account())
3643
+ },
3644
+ {
3645
+ name: "spot_my_trades",
3646
+ description: "Get the Spot account trade list (filled trades) for a symbol with optional time range (signed).",
3647
+ inputSchema: z13.object({ symbol: spotSymbol, orderId: z13.number().int().positive().optional(), startTime: startTime3, endTime: endTime3, fromId: z13.number().int().positive().optional(), limit: limit2.default(500) }),
3648
+ handler: async ({ symbol: symbol3, orderId, startTime: startTime5, endTime: endTime5, fromId, limit: limit4 }) => textResult(await a.myTrades(normalizeSymbol(symbol3), { orderId, startTime: startTime5, endTime: endTime5, fromId, limit: Number(limit4) }))
3649
+ },
3650
+ {
3651
+ name: "spot_my_prevented_matches",
3652
+ description: "Get Spot orders that were expired by the self-trade-prevention (STP) mechanism (signed).",
3653
+ inputSchema: z13.object({ symbol: spotSymbol, preventedMatchId: z13.number().int().positive().optional(), orderId: z13.number().int().positive().optional(), limit: limit2.default(500) }),
3654
+ handler: async ({ symbol: symbol3, preventedMatchId, orderId, limit: limit4 }) => textResult(await a.myPreventedMatches(normalizeSymbol(symbol3), { preventedMatchId, orderId, limit: Number(limit4) }))
3655
+ },
3656
+ {
3657
+ name: "spot_account_commission",
3658
+ description: "Get the Spot account commission rates for a symbol (signed).",
3659
+ inputSchema: z13.object({ symbol: spotSymbol }),
3660
+ handler: async ({ symbol: symbol3 }) => textResult(await a.accountCommission(normalizeSymbol(symbol3)))
3661
+ },
3662
+ {
3663
+ name: "spot_rate_limit_order",
3664
+ description: "Query the current Spot order rate limit usage (signed).",
3665
+ inputSchema: z13.object({}),
3666
+ handler: async () => textResult(await a.rateLimitOrder())
3667
+ },
3668
+ // ---- Trading (signed) ----
3669
+ {
3670
+ name: "spot_new_order",
3671
+ description: "Place a new Spot order. Types: LIMIT, MARKET, LIMIT_MAKER, STOP_LOSS, STOP_LOSS_LIMIT, TAKE_PROFIT, TAKE_PROFIT_LIMIT. For MARKET orders use quantity OR quoteOrderQty. Signed.",
3672
+ inputSchema: spotOrderParamsSchema,
3673
+ handler: async (args) => textResult(await t.createOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3674
+ },
3675
+ {
3676
+ name: "spot_test_order",
3677
+ description: "Validate a Spot order without submitting it. Signed.",
3678
+ inputSchema: spotOrderParamsSchema,
3679
+ handler: async (args) => textResult(await t.testOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3680
+ },
3681
+ {
3682
+ name: "spot_get_order",
3683
+ description: "Query a specific Spot order by orderId or client order id. Signed.",
3684
+ inputSchema: z13.object({ symbol: spotSymbol, orderId: z13.number().int().positive().optional(), origClientOrderId: z13.string().optional() }),
3685
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId }) => textResult(await t.getOrder(normalizeSymbol(symbol3), { orderId, origClientOrderId }))
3686
+ },
3687
+ {
3688
+ name: "spot_cancel_order",
3689
+ description: "Cancel a specific open Spot order by orderId or client order id. Signed.",
3690
+ inputSchema: z13.object({ symbol: spotSymbol, orderId: z13.number().int().positive().optional(), origClientOrderId: z13.string().optional(), newClientOrderId: z13.string().optional() }),
3691
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId, newClientOrderId }) => textResult(await t.cancelOrder(normalizeSymbol(symbol3), { orderId, origClientOrderId, newClientOrderId }))
3692
+ },
3693
+ {
3694
+ name: "spot_open_orders",
3695
+ description: "Get all current open Spot orders, for one symbol or all symbols. Signed.",
3696
+ inputSchema: z13.object({ symbol: optionalSymbol2 }),
3697
+ handler: async ({ symbol: symbol3 }) => textResult(await t.getOpenOrders(symbol3 ? normalizeSymbol(symbol3) : void 0))
3698
+ },
3699
+ {
3700
+ name: "spot_all_orders",
3701
+ description: "Get historical Spot orders for a symbol with optional time range. Signed.",
3702
+ inputSchema: z13.object({ symbol: spotSymbol, orderId: z13.number().int().positive().optional(), startTime: startTime3, endTime: endTime3, limit: limit2 }),
3703
+ handler: async ({ symbol: symbol3, orderId, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await t.getAllOrders(normalizeSymbol(symbol3), { orderId, startTime: startTime5, endTime: endTime5, limit: limit4 }))
3704
+ },
3705
+ {
3706
+ name: "spot_cancel_open_orders",
3707
+ description: "Cancel all open Spot orders for a symbol. Signed.",
3708
+ inputSchema: z13.object({ symbol: spotSymbol }),
3709
+ handler: async ({ symbol: symbol3 }) => textResult(await t.cancelOpenOrders(normalizeSymbol(symbol3)))
3710
+ },
3711
+ {
3712
+ name: "spot_cancel_replace_order",
3713
+ description: "Cancel an existing Spot order and place a new one on the same symbol (cancel-replace). Signed.",
3714
+ inputSchema: z13.object({
3715
+ symbol: spotSymbol,
3716
+ side,
3717
+ type: z13.enum(["LIMIT", "MARKET", "STOP_LOSS", "STOP_LOSS_LIMIT", "TAKE_PROFIT", "TAKE_PROFIT_LIMIT"]),
3718
+ cancelReplaceMode: z13.enum(["STOP_ON_FAILURE", "ALLOW_FAILURE"]).default("STOP_ON_FAILURE"),
3719
+ cancelOrderId: z13.number().int().positive().optional(),
3720
+ cancelOrigClientOrderId: z13.string().optional(),
3721
+ cancelNewClientOrderId: z13.string().optional(),
3722
+ quantity: z13.union([z13.string(), z13.number()]).optional(),
3723
+ price: z13.union([z13.string(), z13.number()]).optional(),
3724
+ timeInForce: z13.enum(["GTC", "IOC", "FOK"]).optional(),
3725
+ newClientOrderId: z13.string().optional()
3726
+ }),
3727
+ handler: async (args) => textResult(await t.cancelReplaceOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3728
+ },
3729
+ {
3730
+ name: "spot_new_oco_order",
3731
+ description: "Place a new Spot OCO (one-cancels-other) order: a stop-loss limit + a limit-maker take-profit. Signed.",
3732
+ inputSchema: z13.object({
3733
+ symbol: spotSymbol,
3734
+ side,
3735
+ quantity: z13.union([z13.string(), z13.number()]).describe("Total base-asset quantity for both legs"),
3736
+ price: z13.union([z13.string(), z13.number()]).describe("Limit (take-profit) price"),
3737
+ stopPrice: z13.union([z13.string(), z13.number()]).describe("Stop-loss trigger price"),
3738
+ stopLimitPrice: z13.union([z13.string(), z13.number()]).optional().describe("Stop-loss limit price (defaults to stopPrice)"),
3739
+ stopLimitTimeInForce: z13.enum(["GTC", "IOC", "FOK"]).optional(),
3740
+ listClientOrderId: z13.string().optional(),
3741
+ limitClientOrderId: z13.string().optional(),
3742
+ stopClientOrderId: z13.string().optional()
3743
+ }),
3744
+ handler: async (args) => textResult(await t.createOCOOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3745
+ },
3746
+ {
3747
+ name: "spot_cancel_oco_order",
3748
+ description: "Cancel an entire Spot OCO order list by orderListId or listClientOrderId. Signed.",
3749
+ inputSchema: z13.object({ symbol: spotSymbol, orderListId: z13.number().int().positive().optional(), listClientOrderId: z13.string().optional(), newClientOrderId: z13.string().optional() }),
3750
+ handler: async ({ symbol: symbol3, orderListId, listClientOrderId, newClientOrderId }) => textResult(await t.cancelOCOOrder(normalizeSymbol(symbol3), { orderListId, listClientOrderId, newClientOrderId }))
3751
+ },
3752
+ {
3753
+ name: "spot_get_oco_order",
3754
+ description: "Query a Spot OCO order list by orderListId or listClientOrderId. Signed.",
3755
+ inputSchema: z13.object({ symbol: spotSymbol, orderListId: z13.number().int().positive().optional(), origClientOrderId: z13.string().optional() }),
3756
+ handler: async ({ symbol: symbol3, orderListId, origClientOrderId }) => textResult(await t.getOCOOrder(normalizeSymbol(symbol3), { orderListId, origClientOrderId }))
3757
+ },
3758
+ {
3759
+ name: "spot_open_oco_orders",
3760
+ description: "Get all current open Spot OCO order lists. Signed.",
3761
+ inputSchema: z13.object({}),
3762
+ handler: async () => textResult(await t.getOpenOCOOrders())
3763
+ },
3764
+ {
3765
+ name: "spot_all_oco_orders",
3766
+ description: "Get historical Spot OCO order lists for a symbol with optional time range. Signed.",
3767
+ inputSchema: z13.object({ symbol: spotSymbol, fromId: z13.number().int().positive().optional(), startTime: startTime3, endTime: endTime3, limit: limit2 }),
3768
+ handler: async ({ symbol: symbol3, fromId, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await t.getAllOCOOrders(normalizeSymbol(symbol3), { fromId, startTime: startTime5, endTime: endTime5, limit: limit4 }))
3769
+ },
3770
+ {
3771
+ name: "spot_cancel_open_oco_orders",
3772
+ description: "Cancel all open Spot OCO order lists for a symbol. Signed.",
3773
+ inputSchema: z13.object({ symbol: spotSymbol }),
3774
+ handler: async ({ symbol: symbol3 }) => textResult(await t.cancelOpenOCOOrders(normalizeSymbol(symbol3)))
3775
+ },
3776
+ // ---- User data stream (signed) ----
3777
+ {
3778
+ name: "spot_ws_start_user_stream",
3779
+ description: "Start the Spot user data stream: creates a listenKey, connects the private WebSocket, and begins keep-alive. Emits executionReport, outboundAccountPosition, balanceUpdate, listStatus.",
3780
+ inputSchema: z13.object({}),
3781
+ handler: async () => textResult({ listenKey: await client.startSpotUserStream() })
3782
+ },
3783
+ {
3784
+ name: "spot_ws_stop_user_stream",
3785
+ description: "Stop the Spot user data stream: closes the private WebSocket and invalidates the listenKey.",
3786
+ inputSchema: z13.object({}),
3787
+ handler: async () => {
3788
+ client.closeSpotUserStream();
3789
+ return textResult({ stopped: true });
3790
+ }
3791
+ }
3792
+ ];
3793
+ return tools;
3794
+ }
3795
+
3796
+ // src/tools/trading.tools.ts
3797
+ import { z as z14 } from "zod";
3798
+ var symbol2 = z14.string().min(1).describe("USD-M pair, e.g. BTCUSDT");
3799
+ var optionalSymbol3 = z14.string().min(1).describe("USD-M pair, e.g. BTCUSDT").optional();
3800
+ var limit3 = z14.number().int().positive().max(1e3).default(100);
3801
+ var startTime4 = z14.number().int().positive().optional();
3802
+ var endTime4 = z14.number().int().positive().optional();
3803
+ var side2 = z14.enum(["BUY", "SELL"]);
3804
+ var positionSide = z14.enum(["BOTH", "LONG", "SHORT"]).optional();
3805
+ var orderParamsSchema = z14.object({
3806
+ symbol: symbol2,
3807
+ side: side2,
3808
+ type: z14.enum(["LIMIT", "MARKET", "STOP", "STOP_MARKET", "TAKE_PROFIT", "TAKE_PROFIT_MARKET", "TRAILING_STOP_MARKET"]),
3809
+ quantity: z14.union([z14.string(), z14.number()]).describe("Order quantity in base asset"),
3810
+ price: z14.union([z14.string(), z14.number()]).optional().describe("Required for LIMIT and STOP/TAKE_PROFIT (limit-triggered) types"),
3811
+ timeInForce: z14.enum(["GTC", "IOC", "FOK", "GTX"]).optional(),
3812
+ stopPrice: z14.union([z14.string(), z14.number()]).optional().describe("Trigger price for STOP/STOP_MARKET/TAKE_PROFIT/TAKE_PROFIT_MARKET"),
3813
+ reduceOnly: z14.boolean().optional().describe("true = only reduces the position, never opens"),
3814
+ positionSide,
3815
+ newClientOrderId: z14.string().optional(),
3816
+ closePosition: z14.boolean().optional().describe("Close the full position on trigger (mutually exclusive with quantity)"),
3817
+ newOrderRespType: z14.enum(["ACK", "RESULT"]).optional(),
3818
+ priceProtect: z14.boolean().optional(),
3819
+ workingType: z14.enum(["MARK_PRICE", "CONTRACT_PRICE"]).optional().describe("Trigger price basis; default CONTRACT_PRICE"),
3820
+ callbackRate: z14.union([z14.string(), z14.number()]).optional().describe("Percent callback for TRAILING_STOP_MARKET, e.g. 0.5"),
3821
+ activationPrice: z14.union([z14.string(), z14.number()]).optional().describe("Activation price for TRAILING_STOP_MARKET"),
3822
+ selfTradePreventionMode: z14.enum(["NONE", "EXPIRE_TAKER", "EXPIRE_MAKER", "EXPIRE_BOTH"]).optional(),
3823
+ goodTillDate: z14.number().int().optional().describe("Timestamp when a GTD order expires")
3824
+ });
3825
+ function tradingTools(client) {
3826
+ const t = client.futures.trading;
3827
+ return [
3828
+ {
3829
+ name: "futures_new_order",
3830
+ description: "Place a new futures order. Types: LIMIT, MARKET, STOP, STOP_MARKET, TAKE_PROFIT, TAKE_PROFIT_MARKET, TRAILING_STOP_MARKET. Use STOP_MARKET as the take-profit/stop-loss trigger and LIMIT for the actual fill. Signed.",
3831
+ inputSchema: orderParamsSchema,
3832
+ handler: async (args) => textResult(await t.createOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3833
+ },
3834
+ {
3835
+ name: "futures_test_order",
3836
+ description: "Validate a new order without submitting it. Returns 200 on success; use to validate fields and margin before real orders. Signed.",
3837
+ inputSchema: orderParamsSchema,
3838
+ handler: async (args) => textResult(await t.createTestOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3839
+ },
3840
+ {
3841
+ name: "futures_get_order",
3842
+ description: "Query a specific order by orderId or client order id. Signed.",
3843
+ inputSchema: z14.object({ symbol: symbol2, orderId: z14.number().int().positive().optional(), origClientOrderId: z14.string().optional() }),
3844
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId }) => textResult(await t.getOrder(normalizeSymbol(symbol3), { orderId, origClientOrderId }))
3845
+ },
3846
+ {
3847
+ name: "futures_cancel_order",
3848
+ description: "Cancel a specific open order by orderId or client order id. Signed.",
3849
+ inputSchema: z14.object({ symbol: symbol2, orderId: z14.number().int().positive().optional(), origClientOrderId: z14.string().optional() }),
3850
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId }) => textResult(await t.cancelOrder(normalizeSymbol(symbol3), { orderId, origClientOrderId }))
3851
+ },
3852
+ {
3853
+ name: "futures_get_open_order",
3854
+ description: "Query a current open order (must exist in the open order book). Signed.",
3855
+ inputSchema: z14.object({ symbol: symbol2, orderId: z14.number().int().positive().optional(), origClientOrderId: z14.string().optional() }),
3856
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId }) => textResult(await t.getCurrentOrder(normalizeSymbol(symbol3), { orderId, origClientOrderId }))
3857
+ },
3858
+ {
3859
+ name: "futures_open_orders",
3860
+ description: "Get all current open orders, for one symbol or all symbols. Signed.",
3861
+ inputSchema: z14.object({ symbol: optionalSymbol3 }),
3862
+ handler: async ({ symbol: symbol3 }) => textResult(await t.getOpenOrders(symbol3 ? normalizeSymbol(symbol3) : void 0))
3863
+ },
3864
+ {
3865
+ name: "futures_all_orders",
3866
+ description: "Get historical orders for a symbol with optional time range (last 7 days by default). Signed.",
3867
+ inputSchema: z14.object({ symbol: symbol2, orderId: z14.number().int().positive().optional(), startTime: startTime4, endTime: endTime4, limit: limit3 }),
3868
+ handler: async ({ symbol: symbol3, orderId, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await t.getAllOrders(normalizeSymbol(symbol3), { orderId, startTime: startTime5, endTime: endTime5, limit: limit4 }))
3869
+ },
3870
+ {
3871
+ name: "futures_cancel_all_orders",
3872
+ description: "Cancel all open orders for a symbol. Signed.",
3873
+ inputSchema: z14.object({ symbol: symbol2 }),
3874
+ handler: async ({ symbol: symbol3 }) => textResult(await t.cancelAllOpenOrders(normalizeSymbol(symbol3)))
3875
+ },
3876
+ {
3877
+ name: "futures_modify_order",
3878
+ description: "Modify an open order (price/quantity/stopPrice). Cannot modify when position side is BOTH and closePosition is true. Signed.",
3879
+ inputSchema: z14.object({
3880
+ symbol: symbol2,
3881
+ side: side2,
3882
+ quantity: z14.union([z14.string(), z14.number()]),
3883
+ price: z14.union([z14.string(), z14.number()]).optional(),
3884
+ orderId: z14.number().int().positive().optional(),
3885
+ origClientOrderId: z14.string().optional(),
3886
+ stopPrice: z14.union([z14.string(), z14.number()]).optional(),
3887
+ timeInForce: z14.enum(["GTC", "IOC", "FOK", "GTX"]).optional(),
3888
+ positionSide,
3889
+ newClientOrderId: z14.string().optional()
3890
+ }),
3891
+ handler: async (args) => textResult(await t.modifyOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3892
+ },
3893
+ {
3894
+ name: "futures_order_modify_history",
3895
+ description: "Get order modification history for a symbol. Signed.",
3896
+ inputSchema: z14.object({ symbol: symbol2, startTime: startTime4, endTime: endTime4, limit: limit3 }),
3897
+ handler: async ({ symbol: symbol3, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await t.getOrderModifyHistory(normalizeSymbol(symbol3), { startTime: startTime5, endTime: endTime5, limit: limit4 }))
3898
+ },
3899
+ {
3900
+ name: "futures_batch_orders",
3901
+ description: "Place up to 5 orders in one request (array of order objects, max 30 per minute per symbol). Signed.",
3902
+ inputSchema: z14.object({ orders: z14.array(orderParamsSchema).min(1).max(5) }),
3903
+ handler: async (args) => {
3904
+ const mapped = args.orders.map((o) => ({ ...o, symbol: normalizeSymbol(o.symbol) }));
3905
+ return textResult(await t.createBatchOrders(mapped));
3906
+ }
3907
+ },
3908
+ {
3909
+ name: "futures_cancel_batch_orders",
3910
+ description: "Cancel multiple orders at once (up to 10 by orderId list or client order id list). Signed.",
3911
+ inputSchema: z14.object({
3912
+ symbol: symbol2,
3913
+ orderIdList: z14.array(z14.number().int().positive()).max(10).optional(),
3914
+ origClientOrderIdList: z14.array(z14.string()).max(10).optional()
3915
+ }),
3916
+ handler: async ({ symbol: symbol3, orderIdList, origClientOrderIdList }) => textResult(await t.cancelBatchOrders(normalizeSymbol(symbol3), { orderIdList, origClientOrderIdList }))
3917
+ },
3918
+ {
3919
+ name: "futures_set_leverage",
3920
+ description: "Change initial leverage for a symbol (1-125, within bracket). Signed.",
3921
+ inputSchema: z14.object({ symbol: symbol2, leverage: z14.number().int().min(1).max(125) }),
3922
+ handler: async ({ symbol: symbol3, leverage }) => textResult(await t.setLeverage(normalizeSymbol(symbol3), leverage))
3923
+ },
3924
+ {
3925
+ name: "futures_set_margin_type",
3926
+ description: "Change margin type for a symbol: isolated or cross. Only allowed when no open position for the symbol. Signed.",
3927
+ inputSchema: z14.object({ symbol: symbol2, marginType: z14.enum(["isolated", "cross"]) }),
3928
+ handler: async ({ symbol: symbol3, marginType }) => textResult(await t.setMarginType(normalizeSymbol(symbol3), marginType))
3929
+ },
3930
+ {
3931
+ name: "futures_modify_position_margin",
3932
+ description: "Add or reduce isolated position margin. marginType: 1 = add margin, 2 = reduce margin. Signed.",
3933
+ inputSchema: z14.object({ symbol: symbol2, amount: z14.number().positive(), marginType: z14.enum(["1", "2"]).describe("1 = add margin, 2 = reduce margin") }),
3934
+ handler: async ({ symbol: symbol3, amount, marginType }) => textResult(await t.modifyPositionMargin(normalizeSymbol(symbol3), amount, Number(marginType)))
3935
+ },
3936
+ {
3937
+ name: "futures_countdown_cancel_all",
3938
+ description: "Auto-cancel all open orders for a symbol after countdownTime ms (0 = cancel the countdown). Emergency circuit breaker. Signed.",
3939
+ inputSchema: z14.object({ symbol: symbol2, countdownTime: z14.number().int().min(100).max(6e4) }),
3940
+ handler: async ({ symbol: symbol3, countdownTime }) => textResult(await t.setCountdownCancelAll(normalizeSymbol(symbol3), countdownTime))
3941
+ },
3942
+ {
3943
+ name: "futures_new_algo_order",
3944
+ description: "Place a conditional algo order (CONDITIONAL or VP). CONDITIONAL supports STOP_MARKET/TAKE_PROFIT_MARKET types with triggerPrice; VP is a volume-participation order with urgency and quantity. Signed.",
3945
+ inputSchema: z14.object({
3946
+ algoType: z14.enum(["CONDITIONAL", "VP"]),
3947
+ symbol: symbol2,
3948
+ side: side2,
3949
+ positionSide,
3950
+ type: z14.enum(["STOP_MARKET", "TAKE_PROFIT_MARKET", "LIMIT"]).describe("For CONDITIONAL: STOP_MARKET/TAKE_PROFIT_MARKET. For VP: LIMIT"),
3951
+ quantity: z14.union([z14.string(), z14.number()]),
3952
+ triggerPrice: z14.union([z14.string(), z14.number()]).optional().describe("Required for CONDITIONAL"),
3953
+ price: z14.union([z14.string(), z14.number()]).optional(),
3954
+ timeInForce: z14.enum(["GTC", "IOC", "FOK", "GTX"]).optional(),
3955
+ workingType: z14.enum(["MARK_PRICE", "CONTRACT_PRICE"]).optional(),
3956
+ priceProtect: z14.boolean().optional(),
3957
+ reduceOnly: z14.boolean().optional(),
3958
+ closePosition: z14.boolean().optional(),
3959
+ urgency: z14.enum(["LOW", "MEDIUM", "HIGH"]).optional().describe("Required for VP"),
3960
+ clientAlgoId: z14.string().optional()
3961
+ }),
3962
+ handler: async (args) => textResult(await t.createAlgoOrder({ ...args, symbol: normalizeSymbol(args.symbol) }))
3963
+ },
3964
+ {
3965
+ name: "futures_cancel_algo_order",
3966
+ description: "Cancel an open algo order by algoId or clientAlgoId. Signed.",
3967
+ inputSchema: z14.object({ symbol: symbol2, algoId: z14.number().int().positive().optional(), clientAlgoId: z14.string().optional() }),
3968
+ handler: async ({ symbol: symbol3, algoId, clientAlgoId }) => textResult(await t.cancelAlgoOrder(normalizeSymbol(symbol3), { algoId, clientAlgoId }))
3969
+ },
3970
+ {
3971
+ name: "futures_cancel_all_algo_orders",
3972
+ description: "Cancel all open algo orders for a symbol. Signed.",
3973
+ inputSchema: z14.object({ symbol: symbol2 }),
3974
+ handler: async ({ symbol: symbol3 }) => textResult(await t.cancelAllOpenAlgoOrders(normalizeSymbol(symbol3)))
3975
+ },
3976
+ {
3977
+ name: "futures_get_algo_order",
3978
+ description: "Query an algo order by algoId or clientAlgoId. Signed.",
3979
+ inputSchema: z14.object({ symbol: symbol2, algoId: z14.number().int().positive().optional(), clientAlgoId: z14.string().optional() }),
3980
+ handler: async ({ symbol: symbol3, algoId, clientAlgoId }) => textResult(await t.getAlgoOrder(normalizeSymbol(symbol3), { algoId, clientAlgoId }))
3981
+ },
3982
+ {
3983
+ name: "futures_open_algo_orders",
3984
+ description: "Get all current open algo orders, optionally filtered by symbol. Signed.",
3985
+ inputSchema: z14.object({ symbol: optionalSymbol3 }),
3986
+ handler: async ({ symbol: symbol3 }) => textResult(await t.getOpenAlgoOrders(symbol3 ? normalizeSymbol(symbol3) : void 0))
3987
+ },
3988
+ {
3989
+ name: "futures_all_algo_orders",
3990
+ description: "Get historical algo orders for a symbol with optional time range. Signed.",
3991
+ inputSchema: z14.object({ symbol: symbol2, startTime: startTime4, endTime: endTime4, limit: limit3 }),
3992
+ handler: async ({ symbol: symbol3, startTime: startTime5, endTime: endTime5, limit: limit4 }) => textResult(await t.getAllAlgoOrders(normalizeSymbol(symbol3), { startTime: startTime5, endTime: endTime5, limit: limit4 }))
3993
+ },
3994
+ {
3995
+ name: "futures_convert_exchange_info",
3996
+ description: "List asset pairs eligible for futures Convert, optionally filtered by fromAsset/toAsset.",
3997
+ inputSchema: z14.object({ fromAsset: z14.string().optional(), toAsset: z14.string().optional() }),
3998
+ handler: async ({ fromAsset, toAsset }) => textResult(await t.convertExchangeInfo({ fromAsset, toAsset }))
3999
+ },
4000
+ {
4001
+ name: "futures_convert_get_quote",
4002
+ description: "Request a Convert quote between two assets (quote expires quickly; accept with futures_convert_accept_quote). Signed.",
4003
+ inputSchema: z14.object({
4004
+ fromAsset: z14.string().min(1),
4005
+ toAsset: z14.string().min(1),
4006
+ fromAmount: z14.number().positive().optional(),
4007
+ toAmount: z14.number().positive().optional(),
4008
+ validTime: z14.enum(["10s", "30s", "1m", "2m"]).optional()
4009
+ }),
4010
+ handler: async (args) => textResult(await t.convertGetQuote(args))
4011
+ },
4012
+ {
4013
+ name: "futures_convert_accept_quote",
4014
+ description: "Accept a previously requested Convert quote by quoteId. Signed.",
4015
+ inputSchema: z14.object({ quoteId: z14.string().min(1) }),
4016
+ handler: async ({ quoteId }) => textResult(await t.convertAcceptQuote(quoteId))
4017
+ },
4018
+ {
4019
+ name: "futures_convert_order_status",
4020
+ description: "Query the status of a Convert order by orderId or quoteId. Signed.",
4021
+ inputSchema: z14.object({ orderId: z14.string().optional(), quoteId: z14.string().optional() }),
4022
+ handler: async ({ orderId, quoteId }) => textResult(await t.convertOrderStatus({ orderId, quoteId }))
4023
+ }
4024
+ ];
4025
+ }
4026
+
4027
+ // src/tools/ws.tools.ts
4028
+ import { z as z15 } from "zod";
4029
+ var buffers = /* @__PURE__ */ new Map();
4030
+ var MAX_BUFFER = 500;
4031
+ function bufferFor(client) {
4032
+ let buffer = buffers.get(client);
4033
+ if (!buffer) {
4034
+ buffer = { subscriptions: /* @__PURE__ */ new Set(), events: [] };
4035
+ buffers.set(client, buffer);
4036
+ client.futures.ws.on("message", (stream, payload) => {
4037
+ buffer.events.push({ stream, payload, ts: Date.now() });
4038
+ if (buffer.events.length > MAX_BUFFER) buffer.events.splice(0, buffer.events.length - MAX_BUFFER);
4039
+ });
4040
+ }
4041
+ return buffer;
4042
+ }
4043
+ function getBufferedWsEvents(client, limit4 = 50) {
4044
+ return bufferFor(client).events.slice(-limit4);
4045
+ }
4046
+ function clearBufferedWsEvents(client) {
4047
+ bufferFor(client).events = [];
4048
+ }
4049
+ function wsTools(client) {
4050
+ const buffer = bufferFor(client);
4051
+ return [
4052
+ {
4053
+ name: "futures_ws_subscribe",
4054
+ description: "Subscribe to real-time market data streams. Topic format: <symbol>@<stream>, e.g. btcusdt@aggTrade, btcusdt@kline_1m, btcusdt@depth20, btcusdt@markPrice@1s, btcusdt@bookTicker, btcusdt@ticker, btcusdt@miniTicker, btcusdt@forceOrder, or all-market topics !ticker@arr, !bookTicker, !markPrice@arr, !forceOrder@arr, !miniTicker@arr. Multiple topics can be passed at once.",
4055
+ inputSchema: z15.object({
4056
+ topics: z15.array(z15.string().min(1)).min(1).describe('Stream topics, e.g. ["btcusdt@aggTrade", "btcusdt@kline_1m"]')
4057
+ }),
4058
+ handler: async ({ topics }) => {
4059
+ const normalized = topics.map((t) => t.trim().toLowerCase());
4060
+ client.futures.ws.subscribe(normalized);
4061
+ normalized.forEach((t) => buffer.subscriptions.add(t));
4062
+ return textResult({ subscribed: normalized, active: [...buffer.subscriptions] });
4063
+ }
4064
+ },
4065
+ {
4066
+ name: "futures_ws_unsubscribe",
4067
+ description: "Unsubscribe from previously subscribed market data streams.",
4068
+ inputSchema: z15.object({ topics: z15.array(z15.string().min(1)).min(1) }),
4069
+ handler: async ({ topics }) => {
4070
+ const normalized = topics.map((t) => t.trim().toLowerCase());
4071
+ client.futures.ws.unsubscribe(normalized);
4072
+ normalized.forEach((t) => buffer.subscriptions.delete(t));
4073
+ return textResult({ unsubscribed: normalized, active: [...buffer.subscriptions] });
4074
+ }
4075
+ },
4076
+ {
4077
+ name: "futures_ws_subscriptions",
4078
+ description: "List the currently subscribed WebSocket market streams for this client.",
4079
+ inputSchema: z15.object({}),
4080
+ handler: async () => textResult({ active: [...buffer.subscriptions] })
4081
+ },
4082
+ {
4083
+ name: "futures_ws_events",
4084
+ description: "Read the buffered real-time payloads received since the last read for the subscribed streams (aggregate trades, klines, depth deltas, mark price, book tickers, tickers, force orders). Payloads are parsed; depth deltas must be applied to a snapshot from futures_order_book.",
4085
+ inputSchema: z15.object({ limit: z15.number().int().min(1).max(200).default(50) }),
4086
+ handler: async ({ limit: limit4 }) => textResult(getBufferedWsEvents(client, limit4))
4087
+ },
4088
+ {
4089
+ name: "futures_ws_clear_events",
4090
+ description: "Clear the buffered WebSocket events so the next read only returns new payloads.",
4091
+ inputSchema: z15.object({}),
4092
+ handler: async () => {
4093
+ clearBufferedWsEvents(client);
4094
+ return textResult({ cleared: true });
4095
+ }
4096
+ },
4097
+ {
4098
+ name: "futures_ws_start_user_stream",
4099
+ description: "Start the user data stream: creates a listenKey, connects the private WebSocket, and begins a 30-minute keep-alive. Emits private events (ACCOUNT_UPDATE, ORDER_TRADE_UPDATE, MARGIN_CALL, ACCOUNT_CONFIG_UPDATE, listenKeyExpired) that the application can consume from the client.",
4100
+ inputSchema: z15.object({}),
4101
+ handler: async () => textResult({ listenKey: await client.startUserStream() })
4102
+ },
4103
+ {
4104
+ name: "futures_ws_stop_user_stream",
4105
+ description: "Stop the user data stream: closes the private WebSocket and invalidates the listenKey.",
4106
+ inputSchema: z15.object({}),
4107
+ handler: async () => {
4108
+ client.closeUserStream();
4109
+ return textResult({ stopped: true });
4110
+ }
4111
+ },
4112
+ {
4113
+ name: "futures_ws_api_order_status",
4114
+ description: "Query an order via the signed WebSocket API (order.status) \u2014 lower-latency alternative to futures_get_order.",
4115
+ inputSchema: z15.object({
4116
+ symbol: z15.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
4117
+ orderId: z15.number().int().positive().optional(),
4118
+ origClientOrderId: z15.string().optional()
4119
+ }),
4120
+ handler: async ({ symbol: symbol3, orderId, origClientOrderId }) => textResult(await client.futures.wsApi.orderStatus({ symbol: normalizeSymbol(symbol3), orderId, origClientOrderId }))
4121
+ },
4122
+ {
4123
+ name: "futures_ws_api_account_status",
4124
+ description: "Get account info via the signed WebSocket API (account.status).",
4125
+ inputSchema: z15.object({}),
4126
+ handler: async () => textResult(await client.futures.wsApi.accountStatus())
4127
+ },
4128
+ {
4129
+ name: "futures_ws_api_account_position",
4130
+ description: "Get current positions via the signed WebSocket API (account.position).",
4131
+ inputSchema: z15.object({ symbol: z15.string().min(1).describe("USD-M pair, e.g. BTCUSDT").optional() }),
4132
+ handler: async ({ symbol: symbol3 }) => textResult(await client.futures.wsApi.accountPosition(symbol3 ? { symbol: normalizeSymbol(symbol3) } : {}))
4133
+ },
4134
+ {
4135
+ name: "futures_ws_api_user_data_stream_start",
4136
+ description: "Create a listenKey via the signed WebSocket API (userDataStream.start) \u2014 no REST call required.",
4137
+ inputSchema: z15.object({}),
4138
+ handler: async () => textResult(await client.futures.wsApi.userDataStreamStart())
4139
+ },
4140
+ {
4141
+ name: "futures_ws_api_user_data_stream_stop",
4142
+ description: "Invalidate a listenKey via the signed WebSocket API (userDataStream.stop).",
4143
+ inputSchema: z15.object({ listenKey: z15.string().min(1) }),
4144
+ handler: async ({ listenKey }) => textResult(await client.futures.wsApi.userDataStreamStop(listenKey))
4145
+ },
4146
+ {
4147
+ name: "futures_ws_api_ticker_price",
4148
+ description: "Get latest price via the unsigned WebSocket API (ticker.price); omit symbol for all symbols.",
4149
+ inputSchema: z15.object({ symbol: z15.string().min(1).describe("USD-M pair, e.g. BTCUSDT").optional() }),
4150
+ handler: async ({ symbol: symbol3 }) => textResult(await client.futures.wsApi.tickerPrice(symbol3 ? { symbol: normalizeSymbol(symbol3) } : {}))
4151
+ },
4152
+ {
4153
+ name: "futures_ws_api_order_book",
4154
+ description: "Get order-book depth via the unsigned WebSocket API (depth).",
4155
+ inputSchema: z15.object({
4156
+ symbol: z15.string().min(1).describe("USD-M pair, e.g. BTCUSDT"),
4157
+ limit: z15.enum(["5", "10", "20", "50", "100", "500", "1000"]).optional()
4158
+ }),
4159
+ handler: async ({ symbol: symbol3, limit: limit4 }) => textResult(await client.futures.wsApi.depth({ symbol: normalizeSymbol(symbol3), limit: limit4 }))
4160
+ }
4161
+ ];
4162
+ }
4163
+
4164
+ // src/tools/index.ts
4165
+ function createFuturesToolkit(client) {
4166
+ const market = marketDataTools(client);
4167
+ const account = accountTools(client);
4168
+ const trading = tradingTools(client);
4169
+ const spot = spotTools(client);
4170
+ const ws = wsTools(client);
4171
+ const derived = derivedTools(client);
4172
+ const paperContext = createPaperContext(client);
4173
+ const paper = paperTools(paperContext);
4174
+ return {
4175
+ client,
4176
+ tools: [...market, ...account, ...trading, ...spot, ...ws, ...derived, ...paper],
4177
+ market,
4178
+ account,
4179
+ trading,
4180
+ spot,
4181
+ ws,
4182
+ derived,
4183
+ paper,
4184
+ paperContext
4185
+ };
4186
+ }
4187
+ function toolkitToFormats(toolkit) {
4188
+ return {
4189
+ openai: toToolList(toolkit.tools, "openai"),
4190
+ anthropic: toToolList(toolkit.tools, "anthropic"),
4191
+ mcp: toToolList(toolkit.tools, "mcp")
4192
+ };
4193
+ }
4194
+
4195
+ // src/mcp/server.ts
4196
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
4197
+ function createBinanceMcpServer(client) {
4198
+ const server = new McpServer({ name: "binance-sdk", version: "3.0.0" });
4199
+ const toolkit = createFuturesToolkit(client);
4200
+ toolkit.tools.forEach((tool) => {
4201
+ server.registerTool(tool.name, { description: tool.description, inputSchema: tool.inputSchema }, async (args) => ({
4202
+ content: [{ type: "text", text: String(await tool.handler(args, { env: "live", isSigned: true })) }]
4203
+ }));
4204
+ });
4205
+ registerResources(server, client);
4206
+ return server;
4207
+ }
4208
+ function registerResources(server, client) {
4209
+ const json = (uri, data) => ({
4210
+ contents: [{ uri, mimeType: "application/json", text: JSON.stringify(data, null, 2) }]
4211
+ });
4212
+ server.registerResource(
4213
+ "futures-symbols",
4214
+ "binance://futures/symbols",
4215
+ {
4216
+ title: "Futures Trading Symbols",
4217
+ description: "Every tradeable USD-M futures symbol with its contract type and status",
4218
+ mimeType: "application/json"
4219
+ },
4220
+ async (uri) => {
4221
+ const info = await client.futures.market.exchangeInfo();
4222
+ return json(uri.href, info.symbols);
4223
+ }
4224
+ );
4225
+ server.registerResource(
4226
+ "futures-premium-index",
4227
+ "binance://futures/premium-index",
4228
+ {
4229
+ title: "Premium Index",
4230
+ description: "Mark price, index price and funding rate across all USD-M pairs",
4231
+ mimeType: "application/json"
4232
+ },
4233
+ async (uri) => json(uri.href, await client.futures.data.assetIndex())
4234
+ );
4235
+ server.registerResource(
4236
+ "spot-symbols",
4237
+ "binance://spot/symbols",
4238
+ {
4239
+ title: "Spot Trading Symbols",
4240
+ description: "Every tradeable Spot symbol with its status, base/quote assets and permissions",
4241
+ mimeType: "application/json"
4242
+ },
4243
+ async (uri) => {
4244
+ const info = await client.spot.market.exchangeInfo();
4245
+ return json(uri.href, info.symbols);
4246
+ }
4247
+ );
4248
+ }
4249
+
4250
+ // src/index.ts
4251
+ var VERSION = "2.1.0";
4252
+ export {
4253
+ AccountAssetSchema,
4254
+ AccountPositionSchema,
4255
+ AccountSchema,
4256
+ AggTradeSchema,
4257
+ AggTradesResponseSchema,
4258
+ AlgoOrderResponseSchema,
4259
+ AlgoOrderSchema,
4260
+ AlgoOrdersResponseSchema,
4261
+ ApiTradingStatusSchema,
4262
+ AvgPriceSchema,
4263
+ BalanceSchema,
4264
+ BalancesResponseSchema,
4265
+ BaseWS,
4266
+ BatchOrdersItemSchema,
4267
+ BatchOrdersResponseSchema,
4268
+ BinanceApiError,
4269
+ BinanceAuthError,
4270
+ BinanceClient,
4271
+ BinanceError,
4272
+ BookTickerSchema,
4273
+ CommissionRateSchema,
4274
+ CountdownCancelAllSchema,
4275
+ CreateOrderParamsSchema,
4276
+ DepthSnapshotSchema,
4277
+ ExchangeInfoSchema,
4278
+ ExchangeSymbolSchema,
4279
+ FeeBurnStatusSchema,
4280
+ FundingRateHistorySchema,
4281
+ FundingRateSchema,
4282
+ FuturesAccount,
4283
+ FuturesData,
4284
+ FuturesMarket,
4285
+ FuturesMarketWS,
4286
+ FuturesOps,
4287
+ FuturesTrading,
4288
+ FuturesUserWS,
4289
+ HttpClient,
4290
+ IncomeHistoryResponseSchema,
4291
+ IncomeSchema,
4292
+ KlineIntervalSchema,
4293
+ KlineSchema,
4294
+ KlinesResponseSchema,
4295
+ LeverageBracketResponseSchema,
4296
+ LeverageBracketSchema,
4297
+ LeverageChangeSchema,
4298
+ ListenKeySchema,
4299
+ LongShortRatioEntrySchema,
4300
+ LongShortRatioSchema,
4301
+ MarginTypeChangeSchema,
4302
+ MarketDataBase,
4303
+ ModifyOrderParamsSchema,
4304
+ MultiAssetsMarginSchema,
4305
+ NetworkError,
4306
+ NewOrderAckSchema,
4307
+ OpenInterestHistEntrySchema,
4308
+ OpenInterestHistSchema,
4309
+ OpenInterestSchema,
4310
+ OrderModifyHistoryEntrySchema,
4311
+ OrderModifyHistorySchema,
4312
+ OrderResponseSchema,
4313
+ OrderSchema,
4314
+ OrdersResponseSchema,
4315
+ PaperTradingEngine,
4316
+ PositionMarginChangeSchema,
4317
+ PositionMarginHistoryEntrySchema,
4318
+ PositionMarginHistorySchema,
4319
+ PositionModeSchema,
4320
+ PositionRiskResponseSchema,
4321
+ PositionRiskSchema,
4322
+ PremiumIndexSchema,
4323
+ RateLimitError,
4324
+ RateLimitOrderResponseSchema,
4325
+ RateLimitOrderSchema,
4326
+ SpotAccount,
4327
+ SpotAccountSchema,
4328
+ SpotBalanceSchema,
4329
+ SpotBalanceUpdateSchema,
4330
+ SpotExecutionReportSchema,
4331
+ SpotListStatusSchema,
4332
+ SpotMarket,
4333
+ SpotMarketWS,
4334
+ SpotOCOOrderAckResponseSchema,
4335
+ SpotOCOOrderAckSchema,
4336
+ SpotOCOOrdersResponseSchema,
4337
+ SpotOrderResponseSchema,
4338
+ SpotOrderSchema,
4339
+ SpotOrdersResponseSchema,
4340
+ SpotOutboundAccountPositionSchema,
4341
+ SpotTradeSchema,
4342
+ SpotTradesResponseSchema,
4343
+ SpotTrading,
4344
+ SpotUserDataStream,
4345
+ SpotUserWS,
4346
+ TakerLongShortRatioEntrySchema,
4347
+ TakerLongShortRatioSchema,
4348
+ Ticker24hrSchema,
4349
+ TickerPriceSchema,
4350
+ TradeSchema,
4351
+ TradesResponseSchema,
4352
+ UserDataAccountUpdateSchema,
4353
+ UserDataMarginCallSchema,
4354
+ UserDataOrderTradeUpdateSchema,
4355
+ UserDataStream,
4356
+ UserTradeSchema,
4357
+ UserTradesResponseSchema,
4358
+ VERSION,
4359
+ WsAggTradePayloadSchema,
4360
+ WsApi,
4361
+ WsApiResponseSchema,
4362
+ WsAssetIndexPayloadSchema,
4363
+ WsBookTickerPayloadSchema,
4364
+ WsCompositeIndexPayloadSchema,
4365
+ WsDepthUpdatePayloadSchema,
4366
+ WsForceOrderPayloadSchema,
4367
+ WsKlinePayloadSchema,
4368
+ WsMarkPricePayloadSchema,
4369
+ WsMiniTickerPayloadSchema,
4370
+ WsRollingWindowTickerPayloadSchema,
4371
+ WsSpotAvgPricePayloadSchema,
4372
+ WsTicker24hrPayloadSchema,
4373
+ WsTradePayloadSchema,
4374
+ accountTools,
4375
+ createBinanceMcpServer,
4376
+ createFuturesToolkit,
4377
+ createPaperContext,
4378
+ derivedTools,
4379
+ floorToStep,
4380
+ formatToDecimals,
4381
+ getBufferedWsEvents,
4382
+ marketDataTools,
4383
+ paperTools,
4384
+ parseSpotUserDataEvent,
4385
+ parseUserDataEvent,
4386
+ parseWsPayload,
4387
+ resolveEnvironment,
4388
+ roundToStep,
4389
+ spotTools,
4390
+ stepDecimals,
4391
+ symbolRulesFrom,
4392
+ textResult,
4393
+ toAnthropicTool,
4394
+ toJsonSchema,
4395
+ toMCPTool,
4396
+ toOpenAITool,
4397
+ toToolList,
4398
+ toolkitToFormats,
4399
+ tradingTools,
4400
+ wsTools
4401
+ };
4402
+ //# sourceMappingURL=index.js.map