pmxtjs 2.1.2 → 2.2.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.
Files changed (78) hide show
  1. package/dist/esm/index.d.ts +3 -0
  2. package/dist/esm/index.js +1 -0
  3. package/dist/esm/pmxt/client.js +2 -1
  4. package/dist/esm/pmxt/models.d.ts +16 -1
  5. package/dist/esm/pmxt/models.js +56 -1
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.js +3 -1
  8. package/dist/pmxt/client.js +2 -1
  9. package/dist/pmxt/models.d.ts +16 -1
  10. package/dist/pmxt/models.js +58 -0
  11. package/generated/package.json +1 -1
  12. package/generated/src/apis/DefaultApi.js +812 -0
  13. package/generated/src/apis/index.js +19 -0
  14. package/generated/src/index.js +21 -0
  15. package/generated/src/models/Balance.js +54 -0
  16. package/generated/src/models/BaseRequest.js +49 -0
  17. package/generated/src/models/BaseResponse.js +51 -0
  18. package/generated/src/models/CancelOrderRequest.js +53 -0
  19. package/generated/src/models/CreateOrder200Response.js +54 -0
  20. package/generated/src/models/CreateOrderParams.js +85 -0
  21. package/generated/src/models/CreateOrderRequest.js +54 -0
  22. package/generated/src/models/ErrorDetail.js +48 -0
  23. package/generated/src/models/ErrorResponse.js +51 -0
  24. package/generated/src/models/EventFetchParams.js +63 -0
  25. package/generated/src/models/ExchangeCredentials.js +59 -0
  26. package/generated/src/models/ExchangeCredentialsSignatureType.js +49 -0
  27. package/generated/src/models/ExecutionPriceResult.js +52 -0
  28. package/generated/src/models/FetchBalance200Response.js +54 -0
  29. package/generated/src/models/FetchEvents200Response.js +54 -0
  30. package/generated/src/models/FetchEventsRequest.js +52 -0
  31. package/generated/src/models/FetchMarkets200Response.js +54 -0
  32. package/generated/src/models/FetchMarketsRequest.js +52 -0
  33. package/generated/src/models/FetchOHLCV200Response.js +54 -0
  34. package/generated/src/models/FetchOHLCVRequest.js +54 -0
  35. package/generated/src/models/FetchOHLCVRequestArgsInner.js +56 -0
  36. package/generated/src/models/FetchOpenOrders200Response.js +54 -0
  37. package/generated/src/models/FetchOpenOrdersRequest.js +51 -0
  38. package/generated/src/models/FetchOrderBook200Response.js +54 -0
  39. package/generated/src/models/FetchOrderBookRequest.js +53 -0
  40. package/generated/src/models/FetchPositions200Response.js +54 -0
  41. package/generated/src/models/FetchPositionsRequest.js +51 -0
  42. package/generated/src/models/FetchTrades200Response.js +54 -0
  43. package/generated/src/models/FetchTradesRequest.js +54 -0
  44. package/generated/src/models/FilterEventsRequest.js +54 -0
  45. package/generated/src/models/FilterEventsRequestArgsInner.js +67 -0
  46. package/generated/src/models/FilterMarketsRequest.js +54 -0
  47. package/generated/src/models/FilterMarketsRequestArgsInner.js +67 -0
  48. package/generated/src/models/FilterMarketsRequestArgsInnerOneOf.js +47 -0
  49. package/generated/src/models/GetExecutionPrice200Response.js +53 -0
  50. package/generated/src/models/GetExecutionPriceDetailed200Response.js +54 -0
  51. package/generated/src/models/GetExecutionPriceRequest.js +54 -0
  52. package/generated/src/models/GetExecutionPriceRequestArgsInner.js +62 -0
  53. package/generated/src/models/HealthCheck200Response.js +50 -0
  54. package/generated/src/models/HistoryFilterParams.js +68 -0
  55. package/generated/src/models/MarketFilterParams.js +79 -0
  56. package/generated/src/models/MarketOutcome.js +56 -0
  57. package/generated/src/models/Order.js +95 -0
  58. package/generated/src/models/OrderBook.js +53 -0
  59. package/generated/src/models/OrderLevel.js +50 -0
  60. package/generated/src/models/Position.js +62 -0
  61. package/generated/src/models/PriceCandle.js +58 -0
  62. package/generated/src/models/Trade.js +65 -0
  63. package/generated/src/models/UnifiedEvent.js +65 -0
  64. package/generated/src/models/UnifiedMarket.js +81 -0
  65. package/generated/src/models/WatchOrderBookRequest.js +54 -0
  66. package/generated/src/models/WatchOrderBookRequestArgsInner.js +49 -0
  67. package/generated/src/models/WatchPricesRequest.js +53 -0
  68. package/generated/src/models/WatchTradesRequest.js +54 -0
  69. package/generated/src/models/WatchUserPositionsRequest.js +49 -0
  70. package/generated/src/models/index.js +73 -0
  71. package/generated/src/runtime.js +338 -0
  72. package/index.ts +1 -0
  73. package/package.json +2 -2
  74. package/pmxt/client.js +957 -0
  75. package/pmxt/client.ts +2 -1
  76. package/pmxt/models.js +60 -0
  77. package/pmxt/models.ts +60 -1
  78. package/pmxt/server-manager.js +204 -0
package/pmxt/client.js ADDED
@@ -0,0 +1,957 @@
1
+ "use strict";
2
+ /**
3
+ * Exchange client implementations.
4
+ *
5
+ * This module provides clean, TypeScript-friendly wrappers around the auto-generated
6
+ * OpenAPI client, matching the Python API exactly.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.Limitless = exports.Kalshi = exports.Polymarket = exports.Exchange = void 0;
10
+ const index_js_1 = require("../generated/src/index.js");
11
+ const models_js_1 = require("./models.js");
12
+ const server_manager_js_1 = require("./server-manager.js");
13
+ // Converter functions
14
+ function convertMarket(raw) {
15
+ const outcomes = (raw.outcomes || []).map((o) => ({
16
+ outcomeId: o.outcomeId,
17
+ label: o.label,
18
+ price: o.price,
19
+ priceChange24h: o.priceChange24h,
20
+ metadata: o.metadata,
21
+ }));
22
+ const convertOutcome = (o) => o ? ({
23
+ outcomeId: o.outcomeId,
24
+ label: o.label,
25
+ price: o.price,
26
+ priceChange24h: o.priceChange24h,
27
+ metadata: o.metadata,
28
+ }) : undefined;
29
+ return {
30
+ marketId: raw.marketId,
31
+ title: raw.title,
32
+ outcomes,
33
+ volume24h: raw.volume24h || 0,
34
+ liquidity: raw.liquidity || 0,
35
+ url: raw.url,
36
+ description: raw.description,
37
+ resolutionDate: raw.resolutionDate ? new Date(raw.resolutionDate) : undefined,
38
+ volume: raw.volume,
39
+ openInterest: raw.openInterest,
40
+ image: raw.image,
41
+ category: raw.category,
42
+ tags: raw.tags,
43
+ yes: convertOutcome(raw.yes),
44
+ no: convertOutcome(raw.no),
45
+ up: convertOutcome(raw.up),
46
+ down: convertOutcome(raw.down),
47
+ };
48
+ }
49
+ function convertCandle(raw) {
50
+ return {
51
+ timestamp: raw.timestamp,
52
+ open: raw.open,
53
+ high: raw.high,
54
+ low: raw.low,
55
+ close: raw.close,
56
+ volume: raw.volume,
57
+ };
58
+ }
59
+ function convertOrderBook(raw) {
60
+ const bids = (raw.bids || []).map((b) => ({
61
+ price: b.price,
62
+ size: b.size,
63
+ }));
64
+ const asks = (raw.asks || []).map((a) => ({
65
+ price: a.price,
66
+ size: a.size,
67
+ }));
68
+ return {
69
+ bids,
70
+ asks,
71
+ timestamp: raw.timestamp,
72
+ };
73
+ }
74
+ function convertTrade(raw) {
75
+ return {
76
+ id: raw.id,
77
+ timestamp: raw.timestamp,
78
+ price: raw.price,
79
+ amount: raw.amount,
80
+ side: raw.side || "unknown",
81
+ };
82
+ }
83
+ function convertOrder(raw) {
84
+ return {
85
+ id: raw.id,
86
+ marketId: raw.marketId,
87
+ outcomeId: raw.outcomeId,
88
+ side: raw.side,
89
+ type: raw.type,
90
+ amount: raw.amount,
91
+ status: raw.status,
92
+ filled: raw.filled,
93
+ remaining: raw.remaining,
94
+ timestamp: raw.timestamp,
95
+ price: raw.price,
96
+ fee: raw.fee,
97
+ };
98
+ }
99
+ function convertPosition(raw) {
100
+ return {
101
+ marketId: raw.marketId,
102
+ outcomeId: raw.outcomeId,
103
+ outcomeLabel: raw.outcomeLabel,
104
+ size: raw.size,
105
+ entryPrice: raw.entryPrice,
106
+ currentPrice: raw.currentPrice,
107
+ unrealizedPnL: raw.unrealizedPnL,
108
+ realizedPnL: raw.realizedPnL,
109
+ };
110
+ }
111
+ function convertBalance(raw) {
112
+ return {
113
+ currency: raw.currency,
114
+ total: raw.total,
115
+ available: raw.available,
116
+ locked: raw.locked,
117
+ };
118
+ }
119
+ function convertEvent(raw) {
120
+ const markets = models_js_1.MarketList.from((raw.markets || []).map(convertMarket));
121
+ return {
122
+ id: raw.id,
123
+ title: raw.title,
124
+ description: raw.description,
125
+ slug: raw.slug,
126
+ markets,
127
+ url: raw.url,
128
+ image: raw.image,
129
+ category: raw.category,
130
+ tags: raw.tags,
131
+ };
132
+ }
133
+ /**
134
+ * Base class for prediction market exchanges.
135
+ *
136
+ * This provides a unified interface for interacting with different
137
+ * prediction market platforms (Polymarket, Kalshi, etc.).
138
+ */
139
+ class Exchange {
140
+ constructor(exchangeName, options = {}) {
141
+ this.exchangeName = exchangeName.toLowerCase();
142
+ this.apiKey = options.apiKey;
143
+ this.privateKey = options.privateKey;
144
+ this.proxyAddress = options.proxyAddress;
145
+ this.signatureType = options.signatureType;
146
+ let baseUrl = options.baseUrl || "http://localhost:3847";
147
+ const autoStartServer = options.autoStartServer !== false;
148
+ // Initialize server manager
149
+ this.serverManager = new server_manager_js_1.ServerManager({ baseUrl });
150
+ // Configure the API client with the initial base URL (will be updated if port changes)
151
+ this.config = new index_js_1.Configuration({ basePath: baseUrl });
152
+ this.api = new index_js_1.DefaultApi(this.config);
153
+ // Initialize the server connection asynchronously
154
+ this.initPromise = this.initializeServer(autoStartServer);
155
+ }
156
+ async initializeServer(autoStartServer) {
157
+ if (autoStartServer) {
158
+ try {
159
+ await this.serverManager.ensureServerRunning();
160
+ // Get the actual port the server is running on
161
+ // (may differ from default if default port was busy)
162
+ const actualPort = this.serverManager.getRunningPort();
163
+ const newBaseUrl = `http://localhost:${actualPort}`;
164
+ const accessToken = this.serverManager.getAccessToken();
165
+ const headers = {};
166
+ if (accessToken) {
167
+ headers['x-pmxt-access-token'] = accessToken;
168
+ }
169
+ // Update API client with actual base URL
170
+ this.config = new index_js_1.Configuration({
171
+ basePath: newBaseUrl,
172
+ headers
173
+ });
174
+ this.api = new index_js_1.DefaultApi(this.config);
175
+ }
176
+ catch (error) {
177
+ throw new Error(`Failed to start PMXT server: ${error}\n\n` +
178
+ `Please ensure 'pmxt-core' is installed: npm install -g pmxt-core\n` +
179
+ `Or start the server manually: pmxt-server`);
180
+ }
181
+ }
182
+ }
183
+ handleResponse(response) {
184
+ if (!response.success) {
185
+ const error = response.error || {};
186
+ throw new Error(error.message || "Unknown error");
187
+ }
188
+ return response.data;
189
+ }
190
+ getCredentials() {
191
+ if (!this.apiKey && !this.privateKey) {
192
+ return undefined;
193
+ }
194
+ return {
195
+ apiKey: this.apiKey,
196
+ privateKey: this.privateKey,
197
+ funderAddress: this.proxyAddress,
198
+ signatureType: this.signatureType,
199
+ };
200
+ }
201
+ // Market Data Methods
202
+ /**
203
+ * Get active markets from the exchange.
204
+ *
205
+ * @param params - Optional filter parameters
206
+ * @returns List of unified markets
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const markets = await exchange.fetchMarkets({ limit: 20, sort: "volume" });
211
+ * ```
212
+ */
213
+ async fetchMarkets(params) {
214
+ await this.initPromise;
215
+ try {
216
+ const args = [];
217
+ if (params) {
218
+ args.push(params);
219
+ }
220
+ const requestBody = {
221
+ args,
222
+ credentials: this.getCredentials()
223
+ };
224
+ const response = await this.api.fetchMarkets({
225
+ exchange: this.exchangeName,
226
+ fetchMarketsRequest: requestBody,
227
+ });
228
+ const data = this.handleResponse(response);
229
+ return data.map(convertMarket);
230
+ }
231
+ catch (error) {
232
+ throw new Error(`Failed to fetch markets: ${error}`);
233
+ }
234
+ }
235
+ /**
236
+ * Get historical price candles.
237
+ *
238
+ * @param outcomeId - Outcome ID (from market.outcomes[].outcomeId)
239
+ * @param params - History filter parameters
240
+ * @returns List of price candles
241
+ *
242
+ * @example
243
+ * ```typescript
244
+ * const markets = await exchange.fetchMarkets({ query: "Trump" });
245
+ * const outcomeId = markets[0].outcomes[0].outcomeId;
246
+ * const candles = await exchange.fetchOHLCV(outcomeId, {
247
+ * resolution: "1h",
248
+ * limit: 100
249
+ * });
250
+ * ```
251
+ */
252
+ async fetchOHLCV(outcomeId, params) {
253
+ await this.initPromise;
254
+ try {
255
+ const paramsDict = { resolution: params.resolution };
256
+ if (params.start) {
257
+ paramsDict.start = params.start.toISOString();
258
+ }
259
+ if (params.end) {
260
+ paramsDict.end = params.end.toISOString();
261
+ }
262
+ if (params.limit) {
263
+ paramsDict.limit = params.limit;
264
+ }
265
+ const requestBody = {
266
+ args: [outcomeId, paramsDict],
267
+ credentials: this.getCredentials()
268
+ };
269
+ const response = await this.api.fetchOHLCV({
270
+ exchange: this.exchangeName,
271
+ fetchOHLCVRequest: requestBody,
272
+ });
273
+ const data = this.handleResponse(response);
274
+ return data.map(convertCandle);
275
+ }
276
+ catch (error) {
277
+ throw new Error(`Failed to fetch OHLCV: ${error}`);
278
+ }
279
+ }
280
+ /**
281
+ * Get current order book for an outcome.
282
+ *
283
+ * @param outcomeId - Outcome ID
284
+ * @returns Current order book
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * const orderBook = await exchange.fetchOrderBook(outcomeId);
289
+ * console.log(`Best bid: ${orderBook.bids[0].price}`);
290
+ * console.log(`Best ask: ${orderBook.asks[0].price}`);
291
+ * ```
292
+ */
293
+ async fetchOrderBook(outcomeId) {
294
+ await this.initPromise;
295
+ try {
296
+ const requestBody = {
297
+ args: [outcomeId],
298
+ credentials: this.getCredentials()
299
+ };
300
+ const response = await this.api.fetchOrderBook({
301
+ exchange: this.exchangeName,
302
+ fetchOrderBookRequest: requestBody,
303
+ });
304
+ const data = this.handleResponse(response);
305
+ return convertOrderBook(data);
306
+ }
307
+ catch (error) {
308
+ throw new Error(`Failed to fetch order book: ${error}`);
309
+ }
310
+ }
311
+ /**
312
+ * Get trade history for an outcome.
313
+ *
314
+ * Note: Polymarket requires API key.
315
+ *
316
+ * @param outcomeId - Outcome ID
317
+ * @param params - History filter parameters
318
+ * @returns List of trades
319
+ */
320
+ async fetchTrades(outcomeId, params) {
321
+ await this.initPromise;
322
+ try {
323
+ const paramsDict = { resolution: params.resolution };
324
+ if (params.limit) {
325
+ paramsDict.limit = params.limit;
326
+ }
327
+ const requestBody = {
328
+ args: [outcomeId, paramsDict],
329
+ credentials: this.getCredentials()
330
+ };
331
+ const response = await this.api.fetchTrades({
332
+ exchange: this.exchangeName,
333
+ fetchTradesRequest: requestBody,
334
+ });
335
+ const data = this.handleResponse(response);
336
+ return data.map(convertTrade);
337
+ }
338
+ catch (error) {
339
+ throw new Error(`Failed to fetch trades: ${error}`);
340
+ }
341
+ }
342
+ // WebSocket Streaming Methods
343
+ /**
344
+ * Watch real-time order book updates via WebSocket.
345
+ *
346
+ * Returns a promise that resolves with the next order book update.
347
+ * Call repeatedly in a loop to stream updates (CCXT Pro pattern).
348
+ *
349
+ * @param outcomeId - Outcome ID to watch
350
+ * @param limit - Optional depth limit for order book
351
+ * @returns Next order book update
352
+ *
353
+ * @example
354
+ * ```typescript
355
+ * // Stream order book updates
356
+ * while (true) {
357
+ * const orderBook = await exchange.watchOrderBook(outcomeId);
358
+ * console.log(`Best bid: ${orderBook.bids[0].price}`);
359
+ * console.log(`Best ask: ${orderBook.asks[0].price}`);
360
+ * }
361
+ * ```
362
+ */
363
+ async watchOrderBook(outcomeId, limit) {
364
+ await this.initPromise;
365
+ try {
366
+ const args = [outcomeId];
367
+ if (limit !== undefined) {
368
+ args.push(limit);
369
+ }
370
+ const requestBody = {
371
+ args,
372
+ credentials: this.getCredentials()
373
+ };
374
+ const response = await this.api.watchOrderBook({
375
+ exchange: this.exchangeName,
376
+ watchOrderBookRequest: requestBody,
377
+ });
378
+ const data = this.handleResponse(response);
379
+ return convertOrderBook(data);
380
+ }
381
+ catch (error) {
382
+ throw new Error(`Failed to watch order book: ${error}`);
383
+ }
384
+ }
385
+ /**
386
+ * Watch real-time trade updates via WebSocket.
387
+ *
388
+ * Returns a promise that resolves with the next trade(s).
389
+ * Call repeatedly in a loop to stream updates (CCXT Pro pattern).
390
+ *
391
+ * @param outcomeId - Outcome ID to watch
392
+ * @param since - Optional timestamp to filter trades from
393
+ * @param limit - Optional limit for number of trades
394
+ * @returns Next trade update(s)
395
+ *
396
+ * @example
397
+ * ```typescript
398
+ * // Stream trade updates
399
+ * while (true) {
400
+ * const trades = await exchange.watchTrades(outcomeId);
401
+ * for (const trade of trades) {
402
+ * console.log(`Trade: ${trade.price} @ ${trade.amount}`);
403
+ * }
404
+ * }
405
+ * ```
406
+ */
407
+ async watchTrades(outcomeId, since, limit) {
408
+ await this.initPromise;
409
+ try {
410
+ const args = [outcomeId];
411
+ if (since !== undefined) {
412
+ args.push(since);
413
+ }
414
+ if (limit !== undefined) {
415
+ args.push(limit);
416
+ }
417
+ const requestBody = {
418
+ args,
419
+ credentials: this.getCredentials()
420
+ };
421
+ const response = await this.api.watchTrades({
422
+ exchange: this.exchangeName,
423
+ watchTradesRequest: requestBody,
424
+ });
425
+ const data = this.handleResponse(response);
426
+ return data.map(convertTrade);
427
+ }
428
+ catch (error) {
429
+ throw new Error(`Failed to watch trades: ${error}`);
430
+ }
431
+ }
432
+ // Trading Methods (require authentication)
433
+ /**
434
+ * Create a new order.
435
+ *
436
+ * @param params - Order parameters
437
+ * @returns Created order
438
+ *
439
+ * @example
440
+ * ```typescript
441
+ * const order = await exchange.createOrder({
442
+ * marketId: "663583",
443
+ * outcomeId: "10991849...",
444
+ * side: "buy",
445
+ * type: "limit",
446
+ * amount: 10,
447
+ * price: 0.55
448
+ * });
449
+ * ```
450
+ */
451
+ async createOrder(params) {
452
+ await this.initPromise;
453
+ try {
454
+ const paramsDict = {
455
+ marketId: params.marketId,
456
+ outcomeId: params.outcomeId,
457
+ side: params.side,
458
+ type: params.type,
459
+ amount: params.amount,
460
+ };
461
+ if (params.price !== undefined) {
462
+ paramsDict.price = params.price;
463
+ }
464
+ if (params.fee !== undefined) {
465
+ paramsDict.fee = params.fee;
466
+ }
467
+ const requestBody = {
468
+ args: [paramsDict],
469
+ credentials: this.getCredentials()
470
+ };
471
+ const response = await this.api.createOrder({
472
+ exchange: this.exchangeName,
473
+ createOrderRequest: requestBody,
474
+ });
475
+ const data = this.handleResponse(response);
476
+ return convertOrder(data);
477
+ }
478
+ catch (error) {
479
+ throw new Error(`Failed to create order: ${error}`);
480
+ }
481
+ }
482
+ /**
483
+ * Cancel an open order.
484
+ *
485
+ * @param orderId - Order ID to cancel
486
+ * @returns Cancelled order
487
+ */
488
+ async cancelOrder(orderId) {
489
+ await this.initPromise;
490
+ try {
491
+ const requestBody = {
492
+ args: [orderId],
493
+ credentials: this.getCredentials()
494
+ };
495
+ const response = await this.api.cancelOrder({
496
+ exchange: this.exchangeName,
497
+ cancelOrderRequest: requestBody,
498
+ });
499
+ const data = this.handleResponse(response);
500
+ return convertOrder(data);
501
+ }
502
+ catch (error) {
503
+ throw new Error(`Failed to cancel order: ${error}`);
504
+ }
505
+ }
506
+ /**
507
+ * Get details of a specific order.
508
+ *
509
+ * @param orderId - Order ID
510
+ * @returns Order details
511
+ */
512
+ async fetchOrder(orderId) {
513
+ await this.initPromise;
514
+ try {
515
+ const requestBody = {
516
+ args: [orderId],
517
+ credentials: this.getCredentials()
518
+ };
519
+ const response = await this.api.fetchOrder({
520
+ exchange: this.exchangeName,
521
+ cancelOrderRequest: requestBody,
522
+ });
523
+ const data = this.handleResponse(response);
524
+ return convertOrder(data);
525
+ }
526
+ catch (error) {
527
+ throw new Error(`Failed to fetch order: ${error}`);
528
+ }
529
+ }
530
+ /**
531
+ * Get all open orders, optionally filtered by market.
532
+ *
533
+ * @param marketId - Optional market ID to filter by
534
+ * @returns List of open orders
535
+ */
536
+ async fetchOpenOrders(marketId) {
537
+ await this.initPromise;
538
+ try {
539
+ const args = [];
540
+ if (marketId) {
541
+ args.push(marketId);
542
+ }
543
+ const requestBody = {
544
+ args,
545
+ credentials: this.getCredentials()
546
+ };
547
+ const response = await this.api.fetchOpenOrders({
548
+ exchange: this.exchangeName,
549
+ fetchOpenOrdersRequest: requestBody,
550
+ });
551
+ const data = this.handleResponse(response);
552
+ return data.map(convertOrder);
553
+ }
554
+ catch (error) {
555
+ throw new Error(`Failed to fetch open orders: ${error}`);
556
+ }
557
+ }
558
+ // Account Methods
559
+ /**
560
+ * Get current positions across all markets.
561
+ *
562
+ * @returns List of positions
563
+ */
564
+ async fetchPositions() {
565
+ await this.initPromise;
566
+ try {
567
+ const requestBody = {
568
+ args: [],
569
+ credentials: this.getCredentials()
570
+ };
571
+ const response = await this.api.fetchPositions({
572
+ exchange: this.exchangeName,
573
+ fetchPositionsRequest: requestBody,
574
+ });
575
+ const data = this.handleResponse(response);
576
+ return data.map(convertPosition);
577
+ }
578
+ catch (error) {
579
+ throw new Error(`Failed to fetch positions: ${error}`);
580
+ }
581
+ }
582
+ /**
583
+ * Get account balance.
584
+ *
585
+ * @returns List of balances (by currency)
586
+ */
587
+ async fetchBalance() {
588
+ await this.initPromise;
589
+ try {
590
+ const requestBody = {
591
+ args: [],
592
+ credentials: this.getCredentials()
593
+ };
594
+ const response = await this.api.fetchBalance({
595
+ exchange: this.exchangeName,
596
+ fetchPositionsRequest: requestBody,
597
+ });
598
+ const data = this.handleResponse(response);
599
+ return data.map(convertBalance);
600
+ }
601
+ catch (error) {
602
+ throw new Error(`Failed to fetch balance: ${error}`);
603
+ }
604
+ }
605
+ /**
606
+ * Calculate the average execution price for a given amount by walking the order book.
607
+ * Uses the sidecar server for calculation to ensure consistency.
608
+ *
609
+ * @param orderBook - The current order book
610
+ * @param side - 'buy' or 'sell'
611
+ * @param amount - The amount to execute
612
+ * @returns The volume-weighted average price, or 0 if insufficient liquidity
613
+ */
614
+ async getExecutionPrice(orderBook, side, amount) {
615
+ const result = await this.getExecutionPriceDetailed(orderBook, side, amount);
616
+ return result.fullyFilled ? result.price : 0;
617
+ }
618
+ /**
619
+ * Calculate detailed execution price information.
620
+ * Uses the sidecar server for calculation to ensure consistency.
621
+ *
622
+ * @param orderBook - The current order book
623
+ * @param side - 'buy' or 'sell'
624
+ * @param amount - The amount to execute
625
+ * @returns Detailed execution result
626
+ */
627
+ async getExecutionPriceDetailed(orderBook, side, amount) {
628
+ await this.initPromise;
629
+ try {
630
+ const body = {
631
+ args: [orderBook, side, amount]
632
+ };
633
+ const credentials = this.getCredentials();
634
+ if (credentials) {
635
+ body.credentials = credentials;
636
+ }
637
+ const url = `${this.config.basePath}/api/${this.exchangeName}/getExecutionPriceDetailed`;
638
+ const response = await fetch(url, {
639
+ method: 'POST',
640
+ headers: {
641
+ 'Content-Type': 'application/json',
642
+ ...this.config.headers
643
+ },
644
+ body: JSON.stringify(body)
645
+ });
646
+ if (!response.ok) {
647
+ const error = await response.json().catch(() => ({}));
648
+ throw new Error(error.error?.message || response.statusText);
649
+ }
650
+ const json = await response.json();
651
+ return this.handleResponse(json);
652
+ }
653
+ catch (error) {
654
+ throw new Error(`Failed to get execution price: ${error}`);
655
+ }
656
+ }
657
+ // ----------------------------------------------------------------------------
658
+ // Filtering Methods
659
+ // ----------------------------------------------------------------------------
660
+ /**
661
+ * Filter markets based on criteria or custom function.
662
+ *
663
+ * @param markets - Array of markets to filter
664
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
665
+ * @returns Filtered array of markets
666
+ *
667
+ * @example Simple text search
668
+ * api.filterMarkets(markets, 'Trump')
669
+ *
670
+ * @example Advanced filtering
671
+ * api.filterMarkets(markets, {
672
+ * text: 'Trump',
673
+ * searchIn: ['title', 'tags'],
674
+ * volume24h: { min: 10000 },
675
+ * category: 'Politics',
676
+ * price: { outcome: 'yes', max: 0.5 }
677
+ * })
678
+ *
679
+ * @example Custom predicate
680
+ * api.filterMarkets(markets, m => m.liquidity > 5000 && m.yes?.price < 0.3)
681
+ */
682
+ filterMarkets(markets, criteria) {
683
+ // Handle predicate function
684
+ if (typeof criteria === 'function') {
685
+ return markets.filter(criteria);
686
+ }
687
+ // Handle simple string search
688
+ if (typeof criteria === 'string') {
689
+ const lowerQuery = criteria.toLowerCase();
690
+ return markets.filter(m => m.title.toLowerCase().includes(lowerQuery));
691
+ }
692
+ // Handle criteria object
693
+ return markets.filter(market => {
694
+ // Text search
695
+ if (criteria.text) {
696
+ const lowerQuery = criteria.text.toLowerCase();
697
+ const searchIn = criteria.searchIn || ['title'];
698
+ let textMatch = false;
699
+ for (const field of searchIn) {
700
+ if (field === 'title' && market.title?.toLowerCase().includes(lowerQuery)) {
701
+ textMatch = true;
702
+ break;
703
+ }
704
+ if (field === 'description' && market.description?.toLowerCase().includes(lowerQuery)) {
705
+ textMatch = true;
706
+ break;
707
+ }
708
+ if (field === 'category' && market.category?.toLowerCase().includes(lowerQuery)) {
709
+ textMatch = true;
710
+ break;
711
+ }
712
+ if (field === 'tags' && market.tags?.some(tag => tag.toLowerCase().includes(lowerQuery))) {
713
+ textMatch = true;
714
+ break;
715
+ }
716
+ if (field === 'outcomes' && market.outcomes?.some(o => o.label.toLowerCase().includes(lowerQuery))) {
717
+ textMatch = true;
718
+ break;
719
+ }
720
+ }
721
+ if (!textMatch)
722
+ return false;
723
+ }
724
+ // Category filter
725
+ if (criteria.category && market.category !== criteria.category) {
726
+ return false;
727
+ }
728
+ // Tags filter (match ANY of the provided tags)
729
+ if (criteria.tags && criteria.tags.length > 0) {
730
+ const hasMatchingTag = criteria.tags.some(tag => market.tags?.some(marketTag => marketTag.toLowerCase() === tag.toLowerCase()));
731
+ if (!hasMatchingTag)
732
+ return false;
733
+ }
734
+ // Volume24h filter
735
+ if (criteria.volume24h) {
736
+ if (criteria.volume24h.min !== undefined && market.volume24h < criteria.volume24h.min) {
737
+ return false;
738
+ }
739
+ if (criteria.volume24h.max !== undefined && market.volume24h > criteria.volume24h.max) {
740
+ return false;
741
+ }
742
+ }
743
+ // Volume filter
744
+ if (criteria.volume) {
745
+ if (criteria.volume.min !== undefined && (market.volume || 0) < criteria.volume.min) {
746
+ return false;
747
+ }
748
+ if (criteria.volume.max !== undefined && (market.volume || 0) > criteria.volume.max) {
749
+ return false;
750
+ }
751
+ }
752
+ // Liquidity filter
753
+ if (criteria.liquidity) {
754
+ if (criteria.liquidity.min !== undefined && market.liquidity < criteria.liquidity.min) {
755
+ return false;
756
+ }
757
+ if (criteria.liquidity.max !== undefined && market.liquidity > criteria.liquidity.max) {
758
+ return false;
759
+ }
760
+ }
761
+ // OpenInterest filter
762
+ if (criteria.openInterest) {
763
+ if (criteria.openInterest.min !== undefined && (market.openInterest || 0) < criteria.openInterest.min) {
764
+ return false;
765
+ }
766
+ if (criteria.openInterest.max !== undefined && (market.openInterest || 0) > criteria.openInterest.max) {
767
+ return false;
768
+ }
769
+ }
770
+ // ResolutionDate filter
771
+ if (criteria.resolutionDate && market.resolutionDate) {
772
+ const resDate = market.resolutionDate;
773
+ if (criteria.resolutionDate.before && resDate >= criteria.resolutionDate.before) {
774
+ return false;
775
+ }
776
+ if (criteria.resolutionDate.after && resDate <= criteria.resolutionDate.after) {
777
+ return false;
778
+ }
779
+ }
780
+ // Price filter (for binary markets)
781
+ if (criteria.price) {
782
+ const outcome = market[criteria.price.outcome];
783
+ if (!outcome)
784
+ return false;
785
+ if (criteria.price.min !== undefined && outcome.price < criteria.price.min) {
786
+ return false;
787
+ }
788
+ if (criteria.price.max !== undefined && outcome.price > criteria.price.max) {
789
+ return false;
790
+ }
791
+ }
792
+ // Price change filter
793
+ if (criteria.priceChange24h) {
794
+ const outcome = market[criteria.priceChange24h.outcome];
795
+ if (!outcome || outcome.priceChange24h === undefined)
796
+ return false;
797
+ if (criteria.priceChange24h.min !== undefined && outcome.priceChange24h < criteria.priceChange24h.min) {
798
+ return false;
799
+ }
800
+ if (criteria.priceChange24h.max !== undefined && outcome.priceChange24h > criteria.priceChange24h.max) {
801
+ return false;
802
+ }
803
+ }
804
+ return true;
805
+ });
806
+ }
807
+ /**
808
+ * Filter events based on criteria or custom function.
809
+ *
810
+ * @param events - Array of events to filter
811
+ * @param criteria - Filter criteria object, string (simple text search), or predicate function
812
+ * @returns Filtered array of events
813
+ *
814
+ * @example Simple text search
815
+ * api.filterEvents(events, 'Trump')
816
+ *
817
+ * @example Advanced filtering
818
+ * api.filterEvents(events, {
819
+ * text: 'Election',
820
+ * searchIn: ['title', 'tags'],
821
+ * category: 'Politics',
822
+ * marketCount: { min: 5 }
823
+ * })
824
+ *
825
+ * @example Custom predicate
826
+ * api.filterEvents(events, e => e.markets.length > 10)
827
+ */
828
+ filterEvents(events, criteria) {
829
+ // Handle predicate function
830
+ if (typeof criteria === 'function') {
831
+ return events.filter(criteria);
832
+ }
833
+ // Handle simple string search
834
+ if (typeof criteria === 'string') {
835
+ const lowerQuery = criteria.toLowerCase();
836
+ return events.filter(e => e.title.toLowerCase().includes(lowerQuery));
837
+ }
838
+ // Handle criteria object
839
+ return events.filter(event => {
840
+ // Text search
841
+ if (criteria.text) {
842
+ const lowerQuery = criteria.text.toLowerCase();
843
+ const searchIn = criteria.searchIn || ['title'];
844
+ let textMatch = false;
845
+ for (const field of searchIn) {
846
+ if (field === 'title' && event.title?.toLowerCase().includes(lowerQuery)) {
847
+ textMatch = true;
848
+ break;
849
+ }
850
+ if (field === 'description' && event.description?.toLowerCase().includes(lowerQuery)) {
851
+ textMatch = true;
852
+ break;
853
+ }
854
+ if (field === 'category' && event.category?.toLowerCase().includes(lowerQuery)) {
855
+ textMatch = true;
856
+ break;
857
+ }
858
+ if (field === 'tags' && event.tags?.some(tag => tag.toLowerCase().includes(lowerQuery))) {
859
+ textMatch = true;
860
+ break;
861
+ }
862
+ }
863
+ if (!textMatch)
864
+ return false;
865
+ }
866
+ // Category filter
867
+ if (criteria.category && event.category !== criteria.category) {
868
+ return false;
869
+ }
870
+ // Tags filter (match ANY of the provided tags)
871
+ if (criteria.tags && criteria.tags.length > 0) {
872
+ const hasMatchingTag = criteria.tags.some(tag => event.tags?.some(eventTag => eventTag.toLowerCase() === tag.toLowerCase()));
873
+ if (!hasMatchingTag)
874
+ return false;
875
+ }
876
+ // Market count filter
877
+ if (criteria.marketCount) {
878
+ const count = event.markets.length;
879
+ if (criteria.marketCount.min !== undefined && count < criteria.marketCount.min) {
880
+ return false;
881
+ }
882
+ if (criteria.marketCount.max !== undefined && count > criteria.marketCount.max) {
883
+ return false;
884
+ }
885
+ }
886
+ // Total volume filter
887
+ if (criteria.totalVolume) {
888
+ const totalVolume = event.markets.reduce((sum, m) => sum + m.volume24h, 0);
889
+ if (criteria.totalVolume.min !== undefined && totalVolume < criteria.totalVolume.min) {
890
+ return false;
891
+ }
892
+ if (criteria.totalVolume.max !== undefined && totalVolume > criteria.totalVolume.max) {
893
+ return false;
894
+ }
895
+ }
896
+ return true;
897
+ });
898
+ }
899
+ }
900
+ exports.Exchange = Exchange;
901
+ class Polymarket extends Exchange {
902
+ constructor(options = {}) {
903
+ // Default to gnosis-safe signature type
904
+ const polyOptions = {
905
+ signatureType: 'gnosis-safe',
906
+ ...options
907
+ };
908
+ super("polymarket", polyOptions);
909
+ }
910
+ }
911
+ exports.Polymarket = Polymarket;
912
+ /**
913
+ * Kalshi exchange client.
914
+ *
915
+ * @example
916
+ * ```typescript
917
+ * // Public data (no auth)
918
+ * const kalshi = new Kalshi();
919
+ * const markets = await kalshi.fetchMarkets({ query: "Fed rates" });
920
+ *
921
+ * // Trading (requires auth)
922
+ * const kalshi = new Kalshi({
923
+ * apiKey: process.env.KALSHI_API_KEY,
924
+ * privateKey: process.env.KALSHI_PRIVATE_KEY
925
+ * });
926
+ * const balance = await kalshi.fetchBalance();
927
+ * ```
928
+ */
929
+ class Kalshi extends Exchange {
930
+ constructor(options = {}) {
931
+ super("kalshi", options);
932
+ }
933
+ }
934
+ exports.Kalshi = Kalshi;
935
+ /**
936
+ * Limitless exchange client.
937
+ *
938
+ * @example
939
+ * ```typescript
940
+ * // Public data (no auth)
941
+ * const limitless = new Limitless();
942
+ * const markets = await limitless.fetchMarkets({ query: "Trump" });
943
+ *
944
+ * // Trading (requires auth)
945
+ * const limitless = new Limitless({
946
+ * apiKey: process.env.LIMITLESS_API_KEY,
947
+ * privateKey: process.env.LIMITLESS_PRIVATE_KEY
948
+ * });
949
+ * const balance = await limitless.fetchBalance();
950
+ * ```
951
+ */
952
+ class Limitless extends Exchange {
953
+ constructor(options = {}) {
954
+ super("limitless", options);
955
+ }
956
+ }
957
+ exports.Limitless = Limitless;