pmxt-core 2.10.0 → 2.12.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 (33) hide show
  1. package/dist/BaseExchange.d.ts +63 -2
  2. package/dist/BaseExchange.js +97 -1
  3. package/dist/exchanges/baozi/index.d.ts +3 -0
  4. package/dist/exchanges/baozi/index.js +4 -0
  5. package/dist/exchanges/kalshi/api.d.ts +1 -1
  6. package/dist/exchanges/kalshi/api.js +1 -1
  7. package/dist/exchanges/kalshi/index.d.ts +9 -2
  8. package/dist/exchanges/kalshi/index.js +84 -27
  9. package/dist/exchanges/kalshi/kalshi.test.js +180 -0
  10. package/dist/exchanges/limitless/api.d.ts +1 -1
  11. package/dist/exchanges/limitless/api.js +1 -1
  12. package/dist/exchanges/limitless/index.d.ts +8 -2
  13. package/dist/exchanges/limitless/index.js +66 -0
  14. package/dist/exchanges/myriad/api.d.ts +1 -1
  15. package/dist/exchanges/myriad/api.js +1 -1
  16. package/dist/exchanges/myriad/index.d.ts +6 -2
  17. package/dist/exchanges/myriad/index.js +32 -0
  18. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  19. package/dist/exchanges/polymarket/api-clob.js +1 -1
  20. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  21. package/dist/exchanges/polymarket/api-data.js +1 -1
  22. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  23. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  24. package/dist/exchanges/polymarket/index.d.ts +6 -2
  25. package/dist/exchanges/polymarket/index.js +27 -0
  26. package/dist/exchanges/probable/api.d.ts +1 -1
  27. package/dist/exchanges/probable/api.js +1 -1
  28. package/dist/exchanges/probable/index.d.ts +6 -2
  29. package/dist/exchanges/probable/index.js +23 -0
  30. package/dist/types.d.ts +3 -0
  31. package/dist/utils/throttler.d.ts +17 -0
  32. package/dist/utils/throttler.js +50 -0
  33. package/package.json +3 -3
@@ -1,4 +1,4 @@
1
- import { UnifiedMarket, UnifiedEvent, PriceCandle, CandleInterval, OrderBook, Trade, Order, Position, Balance, CreateOrderParams } from './types';
1
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, CandleInterval, OrderBook, Trade, UserTrade, Order, Position, Balance, CreateOrderParams } from './types';
2
2
  import { ExecutionPriceResult } from './utils/math';
3
3
  import { AxiosInstance } from 'axios';
4
4
  export interface ApiEndpoint {
@@ -59,6 +59,21 @@ export interface TradesParams {
59
59
  end?: Date;
60
60
  limit?: number;
61
61
  }
62
+ export interface MyTradesParams {
63
+ outcomeId?: string;
64
+ marketId?: string;
65
+ since?: Date;
66
+ until?: Date;
67
+ limit?: number;
68
+ cursor?: string;
69
+ }
70
+ export interface OrderHistoryParams {
71
+ marketId?: string;
72
+ since?: Date;
73
+ until?: Date;
74
+ limit?: number;
75
+ cursor?: string;
76
+ }
62
77
  export type MarketFilterCriteria = {
63
78
  text?: string;
64
79
  searchIn?: ('title' | 'description' | 'category' | 'tags' | 'outcomes')[];
@@ -126,6 +141,9 @@ export interface ExchangeHas {
126
141
  fetchBalance: ExchangeCapability;
127
142
  watchOrderBook: ExchangeCapability;
128
143
  watchTrades: ExchangeCapability;
144
+ fetchMyTrades: ExchangeCapability;
145
+ fetchClosedOrders: ExchangeCapability;
146
+ fetchAllOrders: ExchangeCapability;
129
147
  }
130
148
  export interface ExchangeCredentials {
131
149
  apiKey?: string;
@@ -135,18 +153,39 @@ export interface ExchangeCredentials {
135
153
  signatureType?: number | string;
136
154
  funderAddress?: string;
137
155
  }
156
+ export interface ExchangeOptions {
157
+ /**
158
+ * How long (ms) a market snapshot created by `fetchMarketsPaginated` remains valid
159
+ * before being discarded and re-fetched from the API on the next call.
160
+ * Defaults to 0 (no TTL — the snapshot is re-fetched on every initial call).
161
+ */
162
+ snapshotTTL?: number;
163
+ }
164
+ /** Shape returned by fetchMarketsPaginated */
165
+ export interface PaginatedMarketsResult {
166
+ data: UnifiedMarket[];
167
+ total: number;
168
+ nextCursor?: string;
169
+ }
138
170
  export declare abstract class PredictionMarketExchange {
139
171
  [key: string]: any;
140
172
  protected credentials?: ExchangeCredentials;
141
173
  verbose: boolean;
142
174
  http: AxiosInstance;
175
+ enableRateLimit: boolean;
176
+ private _rateLimit;
177
+ private _throttler;
178
+ private _snapshotTTL;
179
+ private _snapshot?;
180
+ get rateLimit(): number;
181
+ set rateLimit(value: number);
143
182
  markets: Record<string, UnifiedMarket>;
144
183
  marketsBySlug: Record<string, UnifiedMarket>;
145
184
  loadedMarkets: boolean;
146
185
  protected apiDescriptor?: ApiDescriptor;
147
186
  private apiDescriptors;
148
187
  readonly has: ExchangeHas;
149
- constructor(credentials?: ExchangeCredentials);
188
+ constructor(credentials?: ExchangeCredentials, options?: ExchangeOptions);
150
189
  abstract get name(): string;
151
190
  /**
152
191
  * Load and cache all markets from the exchange into `this.markets` and `this.marketsBySlug`.
@@ -208,6 +247,25 @@ export declare abstract class PredictionMarketExchange {
208
247
  * markets = exchange.fetch_markets(slug='will-trump-win')
209
248
  */
210
249
  fetchMarkets(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
250
+ /**
251
+ * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.
252
+ *
253
+ * On the first call (or when no cursor is supplied), fetches all markets once and
254
+ * caches them. Subsequent calls with a cursor returned from a previous call slice
255
+ * directly from the cached snapshot — no additional API calls are made.
256
+ *
257
+ * The snapshot is invalidated after `snapshotTTL` ms (configured via `ExchangeOptions`
258
+ * in the constructor). A request using a cursor from an expired snapshot throws
259
+ * `'Cursor has expired'`.
260
+ *
261
+ * @param params.limit - Page size (default: return all markets)
262
+ * @param params.cursor - Opaque cursor returned by a previous call
263
+ * @returns PaginatedMarketsResult with data, total, and optional nextCursor
264
+ */
265
+ fetchMarketsPaginated(params?: {
266
+ limit?: number;
267
+ cursor?: string;
268
+ }): Promise<PaginatedMarketsResult>;
211
269
  /**
212
270
  * Fetch events with optional keyword search.
213
271
  * Events group related markets together (e.g., "Who will be Fed Chair?" contains multiple candidate markets).
@@ -445,6 +503,9 @@ export declare abstract class PredictionMarketExchange {
445
503
  * orders = exchange.fetch_open_orders('FED-25JAN')
446
504
  */
447
505
  fetchOpenOrders(marketId?: string): Promise<Order[]>;
506
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
507
+ fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]>;
508
+ fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
448
509
  /**
449
510
  * Fetch current user positions across all markets.
450
511
  *
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.PredictionMarketExchange = void 0;
7
7
  const math_1 = require("./utils/math");
8
8
  const errors_1 = require("./errors");
9
+ const throttler_1 = require("./utils/throttler");
9
10
  const axios_1 = __importDefault(require("axios"));
10
11
  // ----------------------------------------------------------------------------
11
12
  // Base Exchange Class
@@ -14,6 +15,23 @@ class PredictionMarketExchange {
14
15
  credentials;
15
16
  verbose = false;
16
17
  http;
18
+ enableRateLimit = true;
19
+ _rateLimit = 1000;
20
+ _throttler;
21
+ // Snapshot state for cursor-based pagination
22
+ _snapshotTTL;
23
+ _snapshot;
24
+ get rateLimit() {
25
+ return this._rateLimit;
26
+ }
27
+ set rateLimit(value) {
28
+ this._rateLimit = value;
29
+ this._throttler = new throttler_1.Throttler({
30
+ refillRate: 1 / value,
31
+ capacity: 1,
32
+ delay: 1,
33
+ });
34
+ }
17
35
  // Market Cache
18
36
  markets = {};
19
37
  marketsBySlug = {};
@@ -35,10 +53,26 @@ class PredictionMarketExchange {
35
53
  fetchBalance: false,
36
54
  watchOrderBook: false,
37
55
  watchTrades: false,
56
+ fetchMyTrades: false,
57
+ fetchClosedOrders: false,
58
+ fetchAllOrders: false,
38
59
  };
39
- constructor(credentials) {
60
+ constructor(credentials, options) {
40
61
  this.credentials = credentials;
62
+ this._snapshotTTL = options?.snapshotTTL ?? 0;
41
63
  this.http = axios_1.default.create();
64
+ this._throttler = new throttler_1.Throttler({
65
+ refillRate: 1 / this._rateLimit,
66
+ capacity: 1,
67
+ delay: 1,
68
+ });
69
+ // Rate Limit Interceptor
70
+ this.http.interceptors.request.use(async (config) => {
71
+ if (this.enableRateLimit) {
72
+ await this._throttler.throttle();
73
+ }
74
+ return config;
75
+ });
42
76
  // Request Interceptor
43
77
  this.http.interceptors.request.use((config) => {
44
78
  if (this.verbose) {
@@ -150,6 +184,59 @@ class PredictionMarketExchange {
150
184
  async fetchMarkets(params) {
151
185
  return this.fetchMarketsImpl(params);
152
186
  }
187
+ /**
188
+ * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.
189
+ *
190
+ * On the first call (or when no cursor is supplied), fetches all markets once and
191
+ * caches them. Subsequent calls with a cursor returned from a previous call slice
192
+ * directly from the cached snapshot — no additional API calls are made.
193
+ *
194
+ * The snapshot is invalidated after `snapshotTTL` ms (configured via `ExchangeOptions`
195
+ * in the constructor). A request using a cursor from an expired snapshot throws
196
+ * `'Cursor has expired'`.
197
+ *
198
+ * @param params.limit - Page size (default: return all markets)
199
+ * @param params.cursor - Opaque cursor returned by a previous call
200
+ * @returns PaginatedMarketsResult with data, total, and optional nextCursor
201
+ */
202
+ async fetchMarketsPaginated(params) {
203
+ const limit = params?.limit;
204
+ const cursor = params?.cursor;
205
+ if (cursor) {
206
+ // Cursor encodes: snapshotId:offset
207
+ const sep = cursor.indexOf(':');
208
+ const snapshotId = cursor.substring(0, sep);
209
+ const offset = parseInt(cursor.substring(sep + 1), 10);
210
+ if (!this._snapshot ||
211
+ this._snapshot.id !== snapshotId ||
212
+ (this._snapshotTTL > 0 && Date.now() - this._snapshot.takenAt > this._snapshotTTL)) {
213
+ throw new Error('Cursor has expired');
214
+ }
215
+ const markets = this._snapshot.markets;
216
+ const slice = limit !== undefined ? markets.slice(offset, offset + limit) : markets.slice(offset);
217
+ const nextOffset = offset + slice.length;
218
+ const nextCursor = nextOffset < markets.length ? `${snapshotId}:${nextOffset}` : undefined;
219
+ return { data: slice, total: markets.length, nextCursor };
220
+ }
221
+ // No cursor — (re)fetch snapshot
222
+ if (!this._snapshot ||
223
+ this._snapshotTTL === 0 ||
224
+ Date.now() - this._snapshot.takenAt > this._snapshotTTL) {
225
+ const markets = await this.fetchMarketsImpl();
226
+ this._snapshot = {
227
+ markets,
228
+ takenAt: Date.now(),
229
+ id: Math.random().toString(36).slice(2),
230
+ };
231
+ }
232
+ const markets = this._snapshot.markets;
233
+ if (!limit) {
234
+ return { data: markets, total: markets.length, nextCursor: undefined };
235
+ }
236
+ const slice = markets.slice(0, limit);
237
+ const nextCursor = limit < markets.length ? `${this._snapshot.id}:${limit}` : undefined;
238
+ return { data: slice, total: markets.length, nextCursor };
239
+ }
153
240
  /**
154
241
  * Fetch events with optional keyword search.
155
242
  * Events group related markets together (e.g., "Who will be Fed Chair?" contains multiple candidate markets).
@@ -444,6 +531,15 @@ class PredictionMarketExchange {
444
531
  async fetchOpenOrders(marketId) {
445
532
  throw new Error("Method fetchOpenOrders not implemented.");
446
533
  }
534
+ async fetchMyTrades(params) {
535
+ throw new Error("Method fetchMyTrades not implemented.");
536
+ }
537
+ async fetchClosedOrders(params) {
538
+ throw new Error("Method fetchClosedOrders not implemented.");
539
+ }
540
+ async fetchAllOrders(params) {
541
+ throw new Error("Method fetchAllOrders not implemented.");
542
+ }
447
543
  /**
448
544
  * Fetch current user positions across all markets.
449
545
  *
@@ -19,6 +19,9 @@ export declare class BaoziExchange extends PredictionMarketExchange {
19
19
  fetchBalance: true;
20
20
  watchOrderBook: true;
21
21
  watchTrades: false;
22
+ fetchMyTrades: false;
23
+ fetchClosedOrders: false;
24
+ fetchAllOrders: false;
22
25
  };
23
26
  private auth?;
24
27
  private connection;
@@ -28,6 +28,9 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
28
28
  fetchBalance: true,
29
29
  watchOrderBook: true,
30
30
  watchTrades: false,
31
+ fetchMyTrades: false,
32
+ fetchClosedOrders: false,
33
+ fetchAllOrders: false,
31
34
  };
32
35
  auth;
33
36
  connection;
@@ -43,6 +46,7 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
43
46
  credentials = options;
44
47
  }
45
48
  super(credentials);
49
+ this.rateLimit = 500;
46
50
  rpcUrl = rpcUrl
47
51
  || process.env.BAOZI_RPC_URL
48
52
  || process.env.HELIUS_RPC_URL
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
3
- * Generated at: 2026-02-19T11:56:56.608Z
3
+ * Generated at: 2026-02-22T14:48:41.786Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const kalshiApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.kalshiApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
6
- * Generated at: 2026-02-19T11:56:56.608Z
6
+ * Generated at: 2026-02-22T14:48:41.786Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Balance, Order, Position, CreateOrderParams } from '../../types';
1
+ import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Balance, Order, Position, CreateOrderParams } from '../../types';
3
3
  import { KalshiWebSocketConfig } from './websocket';
4
4
  export type { KalshiWebSocketConfig };
5
5
  export interface KalshiExchangeOptions {
@@ -21,6 +21,9 @@ export declare class KalshiExchange extends PredictionMarketExchange {
21
21
  fetchBalance: true;
22
22
  watchOrderBook: true;
23
23
  watchTrades: true;
24
+ fetchMyTrades: true;
25
+ fetchClosedOrders: true;
26
+ fetchAllOrders: true;
24
27
  };
25
28
  private auth?;
26
29
  private wsConfig?;
@@ -39,7 +42,11 @@ export declare class KalshiExchange extends PredictionMarketExchange {
39
42
  cancelOrder(orderId: string): Promise<Order>;
40
43
  fetchOrder(orderId: string): Promise<Order>;
41
44
  fetchOpenOrders(marketId?: string): Promise<Order[]>;
45
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
46
+ fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]>;
47
+ fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
42
48
  fetchPositions(): Promise<Position[]>;
49
+ private mapKalshiOrder;
43
50
  private mapKalshiOrderStatus;
44
51
  private ws?;
45
52
  watchOrderBook(id: string, limit?: number): Promise<OrderBook>;
@@ -27,6 +27,9 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
27
27
  fetchBalance: true,
28
28
  watchOrderBook: true,
29
29
  watchTrades: true,
30
+ fetchMyTrades: true,
31
+ fetchClosedOrders: true,
32
+ fetchAllOrders: true,
30
33
  };
31
34
  auth;
32
35
  wsConfig;
@@ -44,6 +47,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
44
47
  credentials = options;
45
48
  }
46
49
  super(credentials);
50
+ this.rateLimit = 100;
47
51
  this.wsConfig = wsConfig;
48
52
  if (credentials?.apiKey && credentials?.privateKey) {
49
53
  this.auth = new auth_1.KalshiAuth(credentials);
@@ -205,20 +209,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
205
209
  }
206
210
  async fetchOrder(orderId) {
207
211
  const data = await this.callApi('GetOrder', { order_id: orderId });
208
- const order = data.order;
209
- return {
210
- id: order.order_id,
211
- marketId: order.ticker,
212
- outcomeId: order.ticker,
213
- side: order.side === 'yes' ? 'buy' : 'sell',
214
- type: order.type === 'limit' ? 'limit' : 'market',
215
- price: order.yes_price ? order.yes_price / 100 : undefined,
216
- amount: order.count,
217
- status: this.mapKalshiOrderStatus(order.status),
218
- filled: order.count - (order.remaining_count || 0),
219
- remaining: order.remaining_count || 0,
220
- timestamp: new Date(order.created_time).getTime(),
221
- };
212
+ return this.mapKalshiOrder(data.order);
222
213
  }
223
214
  async fetchOpenOrders(marketId) {
224
215
  const queryParams = { status: 'resting' };
@@ -227,20 +218,70 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
227
218
  }
228
219
  const data = await this.callApi('GetOrders', queryParams);
229
220
  const orders = data.orders || [];
230
- return orders.map((order) => ({
231
- id: order.order_id,
232
- marketId: order.ticker,
233
- outcomeId: order.ticker,
234
- side: order.side === 'yes' ? 'buy' : 'sell',
235
- type: order.type === 'limit' ? 'limit' : 'market',
236
- price: order.yes_price ? order.yes_price / 100 : undefined,
237
- amount: order.count,
238
- status: 'open',
239
- filled: order.count - (order.remaining_count || 0),
240
- remaining: order.remaining_count || 0,
241
- timestamp: new Date(order.created_time).getTime(),
221
+ return orders.map((order) => this.mapKalshiOrder(order));
222
+ }
223
+ async fetchMyTrades(params) {
224
+ const queryParams = {};
225
+ if (params?.outcomeId || params?.marketId) {
226
+ queryParams.ticker = (params.outcomeId || params.marketId).replace(/-NO$/, '');
227
+ }
228
+ if (params?.since)
229
+ queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
230
+ if (params?.until)
231
+ queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
232
+ if (params?.limit)
233
+ queryParams.limit = params.limit;
234
+ if (params?.cursor)
235
+ queryParams.cursor = params.cursor;
236
+ const data = await this.callApi('GetFills', queryParams);
237
+ return (data.fills || []).map((f) => ({
238
+ id: f.fill_id,
239
+ timestamp: new Date(f.created_time).getTime(),
240
+ price: f.yes_price / 100,
241
+ amount: f.count,
242
+ side: f.side === 'yes' ? 'buy' : 'sell',
243
+ orderId: f.order_id,
242
244
  }));
243
245
  }
246
+ async fetchClosedOrders(params) {
247
+ const queryParams = {};
248
+ if (params?.marketId)
249
+ queryParams.ticker = params.marketId;
250
+ if (params?.until)
251
+ queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
252
+ if (params?.limit)
253
+ queryParams.limit = params.limit;
254
+ if (params?.cursor)
255
+ queryParams.cursor = params.cursor;
256
+ const data = await this.callApi('GetHistoricalOrders', queryParams);
257
+ return (data.orders || []).map((o) => this.mapKalshiOrder(o));
258
+ }
259
+ async fetchAllOrders(params) {
260
+ const queryParams = {};
261
+ if (params?.marketId)
262
+ queryParams.ticker = params.marketId;
263
+ if (params?.since)
264
+ queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
265
+ if (params?.until)
266
+ queryParams.max_ts = Math.floor(params.until.getTime() / 1000);
267
+ if (params?.limit)
268
+ queryParams.limit = params.limit;
269
+ const historicalParams = { ...queryParams };
270
+ delete historicalParams.min_ts; // GetHistoricalOrders only supports max_ts
271
+ const [liveData, historicalData] = await Promise.all([
272
+ this.callApi('GetOrders', queryParams),
273
+ this.callApi('GetHistoricalOrders', historicalParams),
274
+ ]);
275
+ const seen = new Set();
276
+ const all = [];
277
+ for (const o of [...(liveData.orders || []), ...(historicalData.orders || [])]) {
278
+ if (!seen.has(o.order_id)) {
279
+ seen.add(o.order_id);
280
+ all.push(this.mapKalshiOrder(o));
281
+ }
282
+ }
283
+ return all.sort((a, b) => b.timestamp - a.timestamp);
284
+ }
244
285
  async fetchPositions() {
245
286
  const data = await this.callApi('GetPositions');
246
287
  const positions = data.market_positions || [];
@@ -259,9 +300,25 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
259
300
  };
260
301
  });
261
302
  }
303
+ // Helper to map a raw Kalshi order object to a unified Order
304
+ mapKalshiOrder(order) {
305
+ return {
306
+ id: order.order_id,
307
+ marketId: order.ticker,
308
+ outcomeId: order.ticker,
309
+ side: order.side === 'yes' ? 'buy' : 'sell',
310
+ type: order.type === 'limit' ? 'limit' : 'market',
311
+ price: order.yes_price ? order.yes_price / 100 : undefined,
312
+ amount: order.count,
313
+ status: this.mapKalshiOrderStatus(order.status),
314
+ filled: order.count - (order.remaining_count || 0),
315
+ remaining: order.remaining_count || 0,
316
+ timestamp: new Date(order.created_time).getTime(),
317
+ };
318
+ }
262
319
  // Helper to map Kalshi order status to unified status
263
320
  mapKalshiOrderStatus(status) {
264
- switch (status.toLowerCase()) {
321
+ switch ((status ?? '').toLowerCase()) {
265
322
  case 'resting':
266
323
  return 'open';
267
324
  case 'canceled':
@@ -318,6 +318,186 @@ describe('KalshiExchange', () => {
318
318
  });
319
319
  });
320
320
  });
321
+ describe('Trading History Methods', () => {
322
+ beforeEach(() => {
323
+ exchange = new kalshi_1.KalshiExchange(mockCredentials);
324
+ });
325
+ describe('fetchMyTrades', () => {
326
+ it('should map GetFills response to UserTrade array', async () => {
327
+ const mockResponse = {
328
+ data: {
329
+ fills: [
330
+ {
331
+ fill_id: 'fill-abc',
332
+ order_id: 'order-123',
333
+ created_time: '2026-01-13T12:00:00Z',
334
+ yes_price: 55,
335
+ count: 10,
336
+ side: 'yes',
337
+ },
338
+ {
339
+ fill_id: 'fill-def',
340
+ order_id: 'order-456',
341
+ created_time: '2026-01-13T13:00:00Z',
342
+ yes_price: 45,
343
+ count: 5,
344
+ side: 'no',
345
+ },
346
+ ],
347
+ },
348
+ };
349
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
350
+ const trades = await exchange.fetchMyTrades();
351
+ expect(Array.isArray(trades)).toBe(true);
352
+ expect(trades).toHaveLength(2);
353
+ expect(trades[0].id).toBe('fill-abc');
354
+ expect(trades[0].orderId).toBe('order-123');
355
+ expect(trades[0].price).toBe(0.55); // 55 / 100
356
+ expect(trades[0].amount).toBe(10);
357
+ expect(trades[0].side).toBe('buy'); // 'yes' => 'buy'
358
+ expect(trades[1].id).toBe('fill-def');
359
+ expect(trades[1].side).toBe('sell'); // 'no' => 'sell'
360
+ expect(trades[1].price).toBe(0.45); // 45 / 100
361
+ });
362
+ it('should pass outcomeId as ticker (stripping -NO suffix) and date params', async () => {
363
+ const mockResponse = { data: { fills: [] } };
364
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
365
+ await exchange.fetchMyTrades({
366
+ outcomeId: 'TEST-MARKET-NO',
367
+ since: new Date('2026-01-01T00:00:00Z'),
368
+ until: new Date('2026-01-31T00:00:00Z'),
369
+ limit: 50,
370
+ });
371
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
372
+ params: expect.objectContaining({
373
+ ticker: 'TEST-MARKET', // -NO stripped
374
+ min_ts: Math.floor(new Date('2026-01-01T00:00:00Z').getTime() / 1000),
375
+ max_ts: Math.floor(new Date('2026-01-31T00:00:00Z').getTime() / 1000),
376
+ limit: 50,
377
+ }),
378
+ }));
379
+ });
380
+ it('should return empty array when fills is missing', async () => {
381
+ mockAxiosInstance.request.mockResolvedValue({ data: {} });
382
+ const trades = await exchange.fetchMyTrades();
383
+ expect(trades).toHaveLength(0);
384
+ });
385
+ });
386
+ describe('fetchClosedOrders', () => {
387
+ it('should map GetHistoricalOrders response to Order array', async () => {
388
+ const mockResponse = {
389
+ data: {
390
+ orders: [
391
+ {
392
+ order_id: 'hist-order-1',
393
+ ticker: 'TEST-MARKET',
394
+ side: 'yes',
395
+ type: 'limit',
396
+ yes_price: 60,
397
+ count: 8,
398
+ remaining_count: 0,
399
+ status: 'executed',
400
+ created_time: '2026-01-10T10:00:00Z',
401
+ },
402
+ ],
403
+ },
404
+ };
405
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
406
+ const orders = await exchange.fetchClosedOrders();
407
+ expect(orders).toHaveLength(1);
408
+ expect(orders[0].id).toBe('hist-order-1');
409
+ expect(orders[0].marketId).toBe('TEST-MARKET');
410
+ expect(orders[0].side).toBe('buy'); // 'yes' => 'buy'
411
+ expect(orders[0].price).toBe(0.60); // 60 / 100
412
+ expect(orders[0].amount).toBe(8);
413
+ expect(orders[0].filled).toBe(8); // count - remaining_count (0)
414
+ expect(orders[0].remaining).toBe(0);
415
+ expect(orders[0].status).toBe('filled'); // 'executed' => 'filled'
416
+ });
417
+ it('should pass marketId as ticker and limit', async () => {
418
+ const mockResponse = { data: { orders: [] } };
419
+ mockAxiosInstance.request.mockResolvedValue(mockResponse);
420
+ await exchange.fetchClosedOrders({ marketId: 'TEST-MARKET', limit: 25 });
421
+ expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
422
+ params: expect.objectContaining({
423
+ ticker: 'TEST-MARKET',
424
+ limit: 25,
425
+ }),
426
+ }));
427
+ });
428
+ });
429
+ describe('fetchAllOrders', () => {
430
+ it('should merge live and historical orders, dedup, and sort descending by timestamp', async () => {
431
+ const liveResponse = {
432
+ data: {
433
+ orders: [
434
+ {
435
+ order_id: 'order-live-1',
436
+ ticker: 'TEST',
437
+ side: 'yes',
438
+ type: 'limit',
439
+ yes_price: 50,
440
+ count: 5,
441
+ remaining_count: 5,
442
+ status: 'resting',
443
+ created_time: '2026-01-15T10:00:00Z',
444
+ },
445
+ {
446
+ // duplicate that also appears in historical
447
+ order_id: 'order-hist-1',
448
+ ticker: 'TEST',
449
+ side: 'no',
450
+ type: 'limit',
451
+ yes_price: 40,
452
+ count: 3,
453
+ remaining_count: 0,
454
+ status: 'executed',
455
+ created_time: '2026-01-10T08:00:00Z',
456
+ },
457
+ ],
458
+ },
459
+ };
460
+ const historicalResponse = {
461
+ data: {
462
+ orders: [
463
+ {
464
+ order_id: 'order-hist-1', // duplicate
465
+ ticker: 'TEST',
466
+ side: 'no',
467
+ type: 'limit',
468
+ yes_price: 40,
469
+ count: 3,
470
+ remaining_count: 0,
471
+ status: 'executed',
472
+ created_time: '2026-01-10T08:00:00Z',
473
+ },
474
+ {
475
+ order_id: 'order-hist-2',
476
+ ticker: 'TEST',
477
+ side: 'yes',
478
+ type: 'limit',
479
+ yes_price: 55,
480
+ count: 10,
481
+ remaining_count: 0,
482
+ status: 'executed',
483
+ created_time: '2026-01-05T06:00:00Z',
484
+ },
485
+ ],
486
+ },
487
+ };
488
+ mockAxiosInstance.request
489
+ .mockResolvedValueOnce(liveResponse)
490
+ .mockResolvedValueOnce(historicalResponse);
491
+ const orders = await exchange.fetchAllOrders();
492
+ // 3 unique orders (order-hist-1 deduped)
493
+ expect(orders).toHaveLength(3);
494
+ // sorted descending: order-live-1, order-hist-1, order-hist-2
495
+ expect(orders[0].id).toBe('order-live-1');
496
+ expect(orders[1].id).toBe('order-hist-1');
497
+ expect(orders[2].id).toBe('order-hist-2');
498
+ });
499
+ });
500
+ }); // Trading History Methods
321
501
  describe('Order Status Mapping', () => {
322
502
  beforeEach(() => {
323
503
  exchange = new kalshi_1.KalshiExchange(mockCredentials);
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
3
- * Generated at: 2026-02-19T11:56:56.657Z
3
+ * Generated at: 2026-02-22T14:48:41.831Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const limitlessApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.limitlessApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
6
- * Generated at: 2026-02-19T11:56:56.657Z
6
+ * Generated at: 2026-02-22T14:48:41.831Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFetchParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Order, Position, Balance, CreateOrderParams } from '../../types';
1
+ import { PredictionMarketExchange, MarketFetchParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Order, Position, Balance, CreateOrderParams } from '../../types';
3
3
  import { LimitlessWebSocketConfig } from './websocket';
4
4
  export type { LimitlessWebSocketConfig };
5
5
  export interface LimitlessExchangeOptions {
@@ -21,6 +21,9 @@ export declare class LimitlessExchange extends PredictionMarketExchange {
21
21
  fetchBalance: true;
22
22
  watchOrderBook: true;
23
23
  watchTrades: true;
24
+ fetchMyTrades: true;
25
+ fetchClosedOrders: true;
26
+ fetchAllOrders: true;
24
27
  };
25
28
  private auth?;
26
29
  private client?;
@@ -42,6 +45,9 @@ export declare class LimitlessExchange extends PredictionMarketExchange {
42
45
  cancelOrder(orderId: string): Promise<Order>;
43
46
  fetchOrder(orderId: string): Promise<Order>;
44
47
  fetchOpenOrders(marketId?: string): Promise<Order[]>;
48
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
49
+ fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]>;
50
+ fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
45
51
  fetchPositions(): Promise<Position[]>;
46
52
  fetchBalance(): Promise<Balance[]>;
47
53
  private ws?;
@@ -31,6 +31,9 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
31
31
  fetchBalance: true,
32
32
  watchOrderBook: true,
33
33
  watchTrades: true,
34
+ fetchMyTrades: true,
35
+ fetchClosedOrders: true,
36
+ fetchAllOrders: true,
34
37
  };
35
38
  auth;
36
39
  client;
@@ -53,6 +56,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
53
56
  credentials = options;
54
57
  }
55
58
  super(credentials);
59
+ this.rateLimit = 200;
56
60
  this.wsConfig = wsConfig;
57
61
  // Initialize auth if API key or private key are provided
58
62
  // API key is now the primary authentication method
@@ -225,6 +229,68 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
225
229
  throw errors_1.limitlessErrorMapper.mapError(error);
226
230
  }
227
231
  }
232
+ async fetchMyTrades(params) {
233
+ const auth = this.ensureAuth();
234
+ try {
235
+ const response = await this.http.get('https://api.limitless.exchange/portfolio/trades', {
236
+ headers: { Authorization: `Bearer ${auth.getApiKey()}` },
237
+ });
238
+ const trades = Array.isArray(response.data) ? response.data : (response.data?.data || []);
239
+ return trades.map((t) => ({
240
+ id: t.id || String(t.timestamp),
241
+ timestamp: t.createdAt ? new Date(t.createdAt).getTime() : (t.timestamp || 0),
242
+ price: parseFloat(t.price || '0'),
243
+ amount: parseFloat(t.quantity || t.amount || '0'),
244
+ side: (t.side || '').toLowerCase() === 'buy' ? 'buy' : 'sell',
245
+ orderId: t.orderId,
246
+ }));
247
+ }
248
+ catch (error) {
249
+ throw errors_1.limitlessErrorMapper.mapError(error);
250
+ }
251
+ }
252
+ async fetchClosedOrders(params) {
253
+ const client = this.ensureClient();
254
+ if (!params?.marketId) {
255
+ console.warn('Limitless: fetchClosedOrders requires marketId (slug). Returning [].');
256
+ return [];
257
+ }
258
+ const orders = await client.getOrders(params.marketId, ['MATCHED']);
259
+ return orders.map((o) => ({
260
+ id: o.id,
261
+ marketId: params.marketId,
262
+ outcomeId: o.tokenId || 'unknown',
263
+ side: o.side.toLowerCase(),
264
+ type: 'limit',
265
+ price: parseFloat(o.price),
266
+ amount: parseFloat(o.quantity),
267
+ status: 'filled',
268
+ filled: parseFloat(o.quantity),
269
+ remaining: 0,
270
+ timestamp: o.createdAt ? new Date(o.createdAt).getTime() : Date.now(),
271
+ }));
272
+ }
273
+ async fetchAllOrders(params) {
274
+ const client = this.ensureClient();
275
+ if (!params?.marketId) {
276
+ console.warn('Limitless: fetchAllOrders requires marketId (slug). Returning [].');
277
+ return [];
278
+ }
279
+ const orders = await client.getOrders(params.marketId, ['LIVE', 'MATCHED']);
280
+ return orders.map((o) => ({
281
+ id: o.id,
282
+ marketId: params.marketId,
283
+ outcomeId: o.tokenId || 'unknown',
284
+ side: o.side.toLowerCase(),
285
+ type: 'limit',
286
+ price: parseFloat(o.price),
287
+ amount: parseFloat(o.quantity),
288
+ status: o.status === 'LIVE' ? 'open' : 'filled',
289
+ filled: o.status === 'MATCHED' ? parseFloat(o.quantity) : 0,
290
+ remaining: o.status === 'LIVE' ? parseFloat(o.quantity) : 0,
291
+ timestamp: o.createdAt ? new Date(o.createdAt).getTime() : Date.now(),
292
+ }));
293
+ }
228
294
  async fetchPositions() {
229
295
  const auth = this.ensureAuth();
230
296
  const address = auth.getAddress();
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
3
- * Generated at: 2026-02-19T11:56:56.675Z
3
+ * Generated at: 2026-02-22T14:48:41.842Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const myriadApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.myriadApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
6
- * Generated at: 2026-02-19T11:56:56.675Z
6
+ * Generated at: 2026-02-22T14:48:41.842Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.myriadApiSpec = {
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Balance, Order, Position, CreateOrderParams } from '../../types';
1
+ import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Balance, Order, Position, CreateOrderParams } from '../../types';
3
3
  export declare class MyriadExchange extends PredictionMarketExchange {
4
4
  readonly has: {
5
5
  fetchMarkets: true;
@@ -15,6 +15,9 @@ export declare class MyriadExchange extends PredictionMarketExchange {
15
15
  fetchBalance: "emulated";
16
16
  watchOrderBook: "emulated";
17
17
  watchTrades: "emulated";
18
+ fetchMyTrades: true;
19
+ fetchClosedOrders: false;
20
+ fetchAllOrders: false;
18
21
  };
19
22
  private auth?;
20
23
  private ws?;
@@ -29,6 +32,7 @@ export declare class MyriadExchange extends PredictionMarketExchange {
29
32
  fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams): Promise<PriceCandle[]>;
30
33
  fetchOrderBook(id: string): Promise<OrderBook>;
31
34
  fetchTrades(id: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
35
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
32
36
  createOrder(params: CreateOrderParams): Promise<Order>;
33
37
  cancelOrder(_orderId: string): Promise<Order>;
34
38
  fetchOrder(_orderId: string): Promise<Order>;
@@ -28,11 +28,15 @@ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
28
28
  fetchBalance: 'emulated',
29
29
  watchOrderBook: 'emulated',
30
30
  watchTrades: 'emulated',
31
+ fetchMyTrades: true,
32
+ fetchClosedOrders: false,
33
+ fetchAllOrders: false,
31
34
  };
32
35
  auth;
33
36
  ws;
34
37
  constructor(credentials) {
35
38
  super(credentials);
39
+ this.rateLimit = 500;
36
40
  if (credentials?.apiKey) {
37
41
  this.auth = new auth_1.MyriadAuth(credentials);
38
42
  }
@@ -118,6 +122,34 @@ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
118
122
  side: t.action === 'buy' ? 'buy' : 'sell',
119
123
  }));
120
124
  }
125
+ async fetchMyTrades(params) {
126
+ const walletAddress = this.ensureAuth().walletAddress;
127
+ if (!walletAddress) {
128
+ throw new errors_2.AuthenticationError('fetchMyTrades requires a wallet address. Pass privateKey as the wallet address in credentials.', 'Myriad');
129
+ }
130
+ const queryParams = { address: walletAddress };
131
+ if (params?.marketId) {
132
+ const parts = params.marketId.split(':');
133
+ if (parts.length >= 2)
134
+ queryParams.market_id = parts[1];
135
+ }
136
+ if (params?.since)
137
+ queryParams.since = Math.floor(params.since.getTime() / 1000);
138
+ if (params?.until)
139
+ queryParams.until = Math.floor(params.until.getTime() / 1000);
140
+ if (params?.limit)
141
+ queryParams.limit = params.limit;
142
+ const data = await this.callApi('getUsersEvents', queryParams);
143
+ const events = data.data || data.events || [];
144
+ const tradeEvents = events.filter((e) => e.action === 'buy' || e.action === 'sell');
145
+ return tradeEvents.map((t, i) => ({
146
+ id: `${t.blockNumber || t.timestamp}-${i}`,
147
+ timestamp: (t.timestamp || 0) * 1000,
148
+ price: t.shares > 0 ? Number(t.value) / Number(t.shares) : 0,
149
+ amount: Number(t.shares || 0),
150
+ side: t.action === 'buy' ? 'buy' : 'sell',
151
+ }));
152
+ }
121
153
  // ------------------------------------------------------------------------
122
154
  // Trading
123
155
  // ------------------------------------------------------------------------
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
3
- * Generated at: 2026-02-19T11:56:56.616Z
3
+ * Generated at: 2026-02-22T14:48:41.794Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketClobSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketClobSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
6
- * Generated at: 2026-02-19T11:56:56.616Z
6
+ * Generated at: 2026-02-22T14:48:41.794Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketClobSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
3
- * Generated at: 2026-02-19T11:56:56.634Z
3
+ * Generated at: 2026-02-22T14:48:41.812Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketDataSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketDataSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
6
- * Generated at: 2026-02-19T11:56:56.634Z
6
+ * Generated at: 2026-02-22T14:48:41.812Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketDataSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
3
- * Generated at: 2026-02-19T11:56:56.631Z
3
+ * Generated at: 2026-02-22T14:48:41.808Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketGammaSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketGammaSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
6
- * Generated at: 2026-02-19T11:56:56.631Z
6
+ * Generated at: 2026-02-22T14:48:41.808Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, Order, Position, Balance, CreateOrderParams } from '../../types';
1
+ import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Order, Position, Balance, CreateOrderParams } from '../../types';
3
3
  import { PolymarketWebSocketConfig } from './websocket';
4
4
  export type { PolymarketWebSocketConfig };
5
5
  export interface PolymarketExchangeOptions {
@@ -21,6 +21,9 @@ export declare class PolymarketExchange extends PredictionMarketExchange {
21
21
  fetchBalance: true;
22
22
  watchOrderBook: true;
23
23
  watchTrades: true;
24
+ fetchMyTrades: true;
25
+ fetchClosedOrders: false;
26
+ fetchAllOrders: false;
24
27
  };
25
28
  private auth?;
26
29
  private wsConfig?;
@@ -71,6 +74,7 @@ export declare class PolymarketExchange extends PredictionMarketExchange {
71
74
  cancelOrder(orderId: string): Promise<Order>;
72
75
  fetchOrder(orderId: string): Promise<Order>;
73
76
  fetchOpenOrders(marketId?: string): Promise<Order[]>;
77
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
74
78
  fetchPositions(): Promise<Position[]>;
75
79
  fetchBalance(): Promise<Balance[]>;
76
80
  private ws?;
@@ -33,6 +33,9 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
33
33
  fetchBalance: true,
34
34
  watchOrderBook: true,
35
35
  watchTrades: true,
36
+ fetchMyTrades: true,
37
+ fetchClosedOrders: false,
38
+ fetchAllOrders: false,
36
39
  };
37
40
  auth;
38
41
  wsConfig;
@@ -52,6 +55,7 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
52
55
  credentials = options;
53
56
  }
54
57
  super(credentials);
58
+ this.rateLimit = 200;
55
59
  this.wsConfig = wsConfig;
56
60
  // Initialize auth if credentials are provided
57
61
  if (credentials?.privateKey) {
@@ -341,6 +345,29 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
341
345
  throw errors_1.polymarketErrorMapper.mapError(error);
342
346
  }
343
347
  }
348
+ async fetchMyTrades(params) {
349
+ const auth = this.ensureAuth();
350
+ const address = await auth.getEffectiveFunderAddress();
351
+ const queryParams = { user: address };
352
+ if (params?.marketId)
353
+ queryParams.market = params.marketId;
354
+ if (params?.limit)
355
+ queryParams.limit = params.limit;
356
+ if (params?.since)
357
+ queryParams.start = Math.floor(params.since.getTime() / 1000);
358
+ if (params?.until)
359
+ queryParams.end = Math.floor(params.until.getTime() / 1000);
360
+ const data = await this.callApi('getTrades', queryParams);
361
+ const trades = Array.isArray(data) ? data : (data.data || []);
362
+ return trades.map((t) => ({
363
+ id: t.id || t.transactionHash || String(t.timestamp),
364
+ timestamp: typeof t.timestamp === 'number' ? t.timestamp * 1000 : Date.now(),
365
+ price: parseFloat(t.price || '0'),
366
+ amount: parseFloat(t.size || t.amount || '0'),
367
+ side: t.side === 'BUY' ? 'buy' : t.side === 'SELL' ? 'sell' : 'unknown',
368
+ orderId: t.orderId,
369
+ }));
370
+ }
344
371
  async fetchPositions() {
345
372
  try {
346
373
  const auth = this.ensureAuth();
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
3
- * Generated at: 2026-02-19T11:56:56.667Z
3
+ * Generated at: 2026-02-22T14:48:41.835Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const probableApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.probableApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
6
- * Generated at: 2026-02-19T11:56:56.667Z
6
+ * Generated at: 2026-02-22T14:48:41.835Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFetchParams, EventFetchParams, ExchangeCredentials, OHLCVParams, HistoryFilterParams, TradesParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, OrderBook, PriceCandle, Trade, Order, Position, Balance, CreateOrderParams } from '../../types';
1
+ import { PredictionMarketExchange, MarketFetchParams, EventFetchParams, ExchangeCredentials, OHLCVParams, HistoryFilterParams, TradesParams, MyTradesParams } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, OrderBook, PriceCandle, Trade, UserTrade, Order, Position, Balance, CreateOrderParams } from '../../types';
3
3
  import { ProbableWebSocketConfig } from './websocket';
4
4
  export declare class ProbableExchange extends PredictionMarketExchange {
5
5
  readonly has: {
@@ -16,6 +16,9 @@ export declare class ProbableExchange extends PredictionMarketExchange {
16
16
  fetchBalance: true;
17
17
  watchOrderBook: true;
18
18
  watchTrades: false;
19
+ fetchMyTrades: true;
20
+ fetchClosedOrders: false;
21
+ fetchAllOrders: false;
19
22
  };
20
23
  private auth?;
21
24
  private ws?;
@@ -66,6 +69,7 @@ export declare class ProbableExchange extends PredictionMarketExchange {
66
69
  getEventBySlug(slug: string): Promise<UnifiedEvent | null>;
67
70
  fetchOrderBook(id: string): Promise<OrderBook>;
68
71
  fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams): Promise<PriceCandle[]>;
72
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
69
73
  createOrder(params: CreateOrderParams): Promise<Order>;
70
74
  /**
71
75
  * Cancel an order.
@@ -47,12 +47,16 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
47
47
  fetchBalance: true,
48
48
  watchOrderBook: true,
49
49
  watchTrades: false,
50
+ fetchMyTrades: true,
51
+ fetchClosedOrders: false,
52
+ fetchAllOrders: false,
50
53
  };
51
54
  auth;
52
55
  ws;
53
56
  wsConfig;
54
57
  constructor(credentials, wsConfig) {
55
58
  super(credentials);
59
+ this.rateLimit = 500;
56
60
  this.wsConfig = wsConfig;
57
61
  if (credentials?.privateKey && credentials?.apiKey && credentials?.apiSecret && credentials?.passphrase) {
58
62
  this.auth = new auth_1.ProbableAuth(credentials);
@@ -178,6 +182,25 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
178
182
  }
179
183
  return candles;
180
184
  }
185
+ async fetchMyTrades(params) {
186
+ const auth = this.ensureAuth();
187
+ const address = auth.getAddress();
188
+ const queryParams = { user: address };
189
+ if (params?.limit)
190
+ queryParams.limit = params.limit;
191
+ const data = await this.callApi('getPublicApiV1Trades', queryParams);
192
+ const trades = Array.isArray(data) ? data : (data.data || []);
193
+ return trades.map((t) => ({
194
+ id: String(t.tradeId || t.id || t.timestamp),
195
+ timestamp: typeof t.time === 'number'
196
+ ? (t.time > 1e12 ? t.time : t.time * 1000)
197
+ : Date.now(),
198
+ price: parseFloat(t.price || '0'),
199
+ amount: parseFloat(t.qty || t.size || t.amount || '0'),
200
+ side: (t.side || '').toLowerCase() === 'buy' ? 'buy' : 'sell',
201
+ orderId: t.orderId,
202
+ }));
203
+ }
181
204
  // --------------------------------------------------------------------------
182
205
  // Trading Methods
183
206
  // --------------------------------------------------------------------------
package/dist/types.d.ts CHANGED
@@ -66,6 +66,9 @@ export interface Trade {
66
66
  amount: number;
67
67
  side: 'buy' | 'sell' | 'unknown';
68
68
  }
69
+ export interface UserTrade extends Trade {
70
+ orderId?: string;
71
+ }
69
72
  export interface Order {
70
73
  id: string;
71
74
  marketId: string;
@@ -0,0 +1,17 @@
1
+ export declare class Throttler {
2
+ private tokens;
3
+ private queue;
4
+ private running;
5
+ private lastTimestamp;
6
+ private refillRate;
7
+ private capacity;
8
+ private delay;
9
+ constructor(config: {
10
+ refillRate: number;
11
+ capacity: number;
12
+ delay: number;
13
+ });
14
+ throttle(cost?: number): Promise<void>;
15
+ private loop;
16
+ private sleep;
17
+ }
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Throttler = void 0;
4
+ class Throttler {
5
+ tokens = 0;
6
+ queue = [];
7
+ running = false;
8
+ lastTimestamp = 0;
9
+ refillRate;
10
+ capacity;
11
+ delay;
12
+ constructor(config) {
13
+ this.refillRate = config.refillRate;
14
+ this.capacity = config.capacity;
15
+ this.delay = config.delay;
16
+ }
17
+ async throttle(cost = 1) {
18
+ return new Promise((resolve) => {
19
+ this.queue.push({ resolve, cost });
20
+ if (!this.running) {
21
+ this.running = true;
22
+ this.loop();
23
+ }
24
+ });
25
+ }
26
+ async loop() {
27
+ while (this.queue.length > 0) {
28
+ const now = Date.now();
29
+ if (this.lastTimestamp > 0) {
30
+ const elapsed = now - this.lastTimestamp;
31
+ this.tokens = Math.min(this.tokens + elapsed * this.refillRate, this.capacity);
32
+ }
33
+ this.lastTimestamp = now;
34
+ const head = this.queue[0];
35
+ if (this.tokens >= 0) {
36
+ this.tokens -= head.cost;
37
+ head.resolve();
38
+ this.queue.shift();
39
+ }
40
+ else {
41
+ await this.sleep(this.delay);
42
+ }
43
+ }
44
+ this.running = false;
45
+ }
46
+ sleep(ms) {
47
+ return new Promise((resolve) => setTimeout(resolve, ms));
48
+ }
49
+ }
50
+ exports.Throttler = Throttler;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.10.0",
3
+ "version": "2.12.0",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "test": "jest -c jest.config.js",
30
30
  "server": "tsx watch src/server/index.ts",
31
31
  "server:prod": "node dist/server/index.js",
32
- "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.10.0,library=urllib3",
33
- "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.10.0,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
32
+ "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.12.0,library=urllib3",
33
+ "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.12.0,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
34
34
  "fetch:openapi": "node scripts/fetch-openapi-specs.js",
35
35
  "extract:jsdoc": "node ../scripts/extract-jsdoc.js",
36
36
  "generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",