pmxt-core 2.9.1 → 2.9.3

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 (67) hide show
  1. package/dist/BaseExchange.d.ts +144 -8
  2. package/dist/BaseExchange.js +186 -11
  3. package/dist/exchanges/baozi/fetchEvents.js +16 -11
  4. package/dist/exchanges/baozi/index.d.ts +5 -0
  5. package/dist/exchanges/baozi/index.js +6 -0
  6. package/dist/exchanges/kalshi/api.d.ts +7 -1
  7. package/dist/exchanges/kalshi/api.js +11 -2
  8. package/dist/exchanges/kalshi/config.d.ts +103 -0
  9. package/dist/exchanges/kalshi/config.js +144 -0
  10. package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -2
  11. package/dist/exchanges/kalshi/fetchEvents.js +138 -67
  12. package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -2
  13. package/dist/exchanges/kalshi/fetchMarkets.js +36 -25
  14. package/dist/exchanges/kalshi/fetchOHLCV.d.ts +3 -3
  15. package/dist/exchanges/kalshi/fetchOHLCV.js +20 -17
  16. package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
  17. package/dist/exchanges/kalshi/fetchOrderBook.js +60 -0
  18. package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
  19. package/dist/exchanges/kalshi/fetchTrades.js +32 -0
  20. package/dist/exchanges/kalshi/index.d.ts +20 -4
  21. package/dist/exchanges/kalshi/index.js +171 -90
  22. package/dist/exchanges/kalshi/kalshi.test.js +440 -157
  23. package/dist/exchanges/kalshi/utils.d.ts +1 -3
  24. package/dist/exchanges/kalshi/utils.js +15 -16
  25. package/dist/exchanges/kalshi/websocket.d.ts +4 -3
  26. package/dist/exchanges/kalshi/websocket.js +87 -61
  27. package/dist/exchanges/kalshi-demo/index.d.ts +10 -0
  28. package/dist/exchanges/kalshi-demo/index.js +23 -0
  29. package/dist/exchanges/limitless/api.d.ts +1 -1
  30. package/dist/exchanges/limitless/api.js +1 -1
  31. package/dist/exchanges/limitless/fetchEvents.d.ts +2 -1
  32. package/dist/exchanges/limitless/fetchEvents.js +95 -49
  33. package/dist/exchanges/limitless/fetchOHLCV.d.ts +2 -2
  34. package/dist/exchanges/limitless/index.d.ts +11 -3
  35. package/dist/exchanges/limitless/index.js +69 -1
  36. package/dist/exchanges/limitless/utils.js +1 -0
  37. package/dist/exchanges/myriad/api.d.ts +1 -1
  38. package/dist/exchanges/myriad/api.js +1 -1
  39. package/dist/exchanges/myriad/fetchOHLCV.d.ts +2 -2
  40. package/dist/exchanges/myriad/index.d.ts +9 -3
  41. package/dist/exchanges/myriad/index.js +34 -0
  42. package/dist/exchanges/myriad/utils.js +5 -1
  43. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  44. package/dist/exchanges/polymarket/api-clob.js +1 -1
  45. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  46. package/dist/exchanges/polymarket/api-data.js +1 -1
  47. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  48. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  49. package/dist/exchanges/polymarket/auth.js +3 -1
  50. package/dist/exchanges/polymarket/fetchEvents.js +116 -80
  51. package/dist/exchanges/polymarket/fetchOHLCV.d.ts +2 -2
  52. package/dist/exchanges/polymarket/index.d.ts +30 -6
  53. package/dist/exchanges/polymarket/index.js +101 -31
  54. package/dist/exchanges/polymarket/utils.js +1 -0
  55. package/dist/exchanges/probable/api.d.ts +1 -1
  56. package/dist/exchanges/probable/api.js +1 -1
  57. package/dist/exchanges/probable/index.d.ts +45 -3
  58. package/dist/exchanges/probable/index.js +61 -0
  59. package/dist/exchanges/probable/utils.js +5 -1
  60. package/dist/index.d.ts +4 -0
  61. package/dist/index.js +5 -1
  62. package/dist/server/app.js +56 -48
  63. package/dist/server/utils/port-manager.js +1 -1
  64. package/dist/types.d.ts +29 -0
  65. package/dist/utils/throttler.d.ts +17 -0
  66. package/dist/utils/throttler.js +50 -0
  67. package/package.json +7 -4
@@ -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, BuiltOrder } from './types';
2
2
  import { ExecutionPriceResult } from './utils/math';
3
3
  import { AxiosInstance } from 'axios';
4
4
  export interface ApiEndpoint {
@@ -37,6 +37,7 @@ export interface EventFetchParams {
37
37
  query?: string;
38
38
  limit?: number;
39
39
  offset?: number;
40
+ sort?: 'volume' | 'liquidity' | 'newest';
40
41
  status?: 'active' | 'inactive' | 'closed' | 'all';
41
42
  searchIn?: 'title' | 'description' | 'both';
42
43
  eventId?: string;
@@ -59,6 +60,21 @@ export interface TradesParams {
59
60
  end?: Date;
60
61
  limit?: number;
61
62
  }
63
+ export interface MyTradesParams {
64
+ outcomeId?: string;
65
+ marketId?: string;
66
+ since?: Date;
67
+ until?: Date;
68
+ limit?: number;
69
+ cursor?: string;
70
+ }
71
+ export interface OrderHistoryParams {
72
+ marketId?: string;
73
+ since?: Date;
74
+ until?: Date;
75
+ limit?: number;
76
+ cursor?: string;
77
+ }
62
78
  export type MarketFilterCriteria = {
63
79
  text?: string;
64
80
  searchIn?: ('title' | 'description' | 'category' | 'tags' | 'outcomes')[];
@@ -126,6 +142,11 @@ export interface ExchangeHas {
126
142
  fetchBalance: ExchangeCapability;
127
143
  watchOrderBook: ExchangeCapability;
128
144
  watchTrades: ExchangeCapability;
145
+ fetchMyTrades: ExchangeCapability;
146
+ fetchClosedOrders: ExchangeCapability;
147
+ fetchAllOrders: ExchangeCapability;
148
+ buildOrder: ExchangeCapability;
149
+ submitOrder: ExchangeCapability;
129
150
  }
130
151
  export interface ExchangeCredentials {
131
152
  apiKey?: string;
@@ -135,30 +156,69 @@ export interface ExchangeCredentials {
135
156
  signatureType?: number | string;
136
157
  funderAddress?: string;
137
158
  }
159
+ export interface ExchangeOptions {
160
+ /**
161
+ * How long (ms) a market snapshot created by `fetchMarketsPaginated` remains valid
162
+ * before being discarded and re-fetched from the API on the next call.
163
+ * Defaults to 0 (no TTL — the snapshot is re-fetched on every initial call).
164
+ */
165
+ snapshotTTL?: number;
166
+ }
167
+ /** Shape returned by fetchMarketsPaginated */
168
+ export interface PaginatedMarketsResult {
169
+ data: UnifiedMarket[];
170
+ total: number;
171
+ nextCursor?: string;
172
+ }
138
173
  export declare abstract class PredictionMarketExchange {
139
174
  [key: string]: any;
140
175
  protected credentials?: ExchangeCredentials;
141
176
  verbose: boolean;
142
177
  http: AxiosInstance;
178
+ enableRateLimit: boolean;
179
+ private _rateLimit;
180
+ private _throttler;
181
+ private _snapshotTTL;
182
+ private _snapshot?;
183
+ get rateLimit(): number;
184
+ set rateLimit(value: number);
143
185
  markets: Record<string, UnifiedMarket>;
144
186
  marketsBySlug: Record<string, UnifiedMarket>;
145
187
  loadedMarkets: boolean;
146
188
  protected apiDescriptor?: ApiDescriptor;
147
189
  private apiDescriptors;
148
190
  readonly has: ExchangeHas;
149
- constructor(credentials?: ExchangeCredentials);
191
+ constructor(credentials?: ExchangeCredentials, options?: ExchangeOptions);
150
192
  abstract get name(): string;
151
193
  /**
152
- * Load and cache markets from the exchange.
153
- * This method populates `this.markets` and `this.marketsBySlug`.
194
+ * Load and cache all markets from the exchange into `this.markets` and `this.marketsBySlug`.
195
+ * Subsequent calls return the cached result without hitting the API again.
154
196
  *
155
- * @param reload - Force a reload of markets from the API even if already loaded
197
+ * This is the correct way to paginate or iterate over markets without drift.
198
+ * Because `fetchMarkets()` always hits the API, repeated calls with different `offset`
199
+ * values may return inconsistent results if the exchange reorders or adds markets between
200
+ * requests. Use `loadMarkets()` once to get a stable snapshot, then paginate over
201
+ * `Object.values(exchange.markets)` locally.
202
+ *
203
+ * @param reload - Force a fresh fetch from the API even if markets are already loaded
156
204
  * @returns Dictionary of markets indexed by marketId
205
+ *
206
+ * @example-ts Stable pagination
207
+ * await exchange.loadMarkets();
208
+ * const all = Object.values(exchange.markets);
209
+ * const page1 = all.slice(0, 100);
210
+ * const page2 = all.slice(100, 200);
211
+ *
212
+ * @example-python Stable pagination
213
+ * exchange.load_markets()
214
+ * all = list(exchange.markets.values())
215
+ * page1 = all[:100]
216
+ * page2 = all[100:200]
157
217
  */
158
218
  loadMarkets(reload?: boolean): Promise<Record<string, UnifiedMarket>>;
159
219
  /**
160
220
  * Fetch markets with optional filtering, search, or slug lookup.
161
- * This is the primary method for retrieving markets.
221
+ * Always hits the exchange API results reflect the live state at the time of the call.
162
222
  *
163
223
  * @param params - Optional parameters for filtering and search
164
224
  * @param params.query - Search keyword to filter markets
@@ -169,6 +229,10 @@ export declare abstract class PredictionMarketExchange {
169
229
  * @param params.searchIn - Where to search ('title' | 'description' | 'both')
170
230
  * @returns Array of unified markets
171
231
  *
232
+ * @note Calling this repeatedly with different `offset` values does not guarantee stable
233
+ * ordering — exchanges may reorder or add markets between requests. For stable iteration
234
+ * across pages, use `loadMarkets()` and paginate over `Object.values(exchange.markets)`.
235
+ *
172
236
  * @note Some exchanges (like Limitless) may only support status 'active' for search results.
173
237
  *
174
238
  * @example-ts Fetch markets
@@ -186,12 +250,31 @@ export declare abstract class PredictionMarketExchange {
186
250
  * markets = exchange.fetch_markets(slug='will-trump-win')
187
251
  */
188
252
  fetchMarkets(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
253
+ /**
254
+ * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.
255
+ *
256
+ * On the first call (or when no cursor is supplied), fetches all markets once and
257
+ * caches them. Subsequent calls with a cursor returned from a previous call slice
258
+ * directly from the cached snapshot — no additional API calls are made.
259
+ *
260
+ * The snapshot is invalidated after `snapshotTTL` ms (configured via `ExchangeOptions`
261
+ * in the constructor). A request using a cursor from an expired snapshot throws
262
+ * `'Cursor has expired'`.
263
+ *
264
+ * @param params.limit - Page size (default: return all markets)
265
+ * @param params.cursor - Opaque cursor returned by a previous call
266
+ * @returns PaginatedMarketsResult with data, total, and optional nextCursor
267
+ */
268
+ fetchMarketsPaginated(params?: {
269
+ limit?: number;
270
+ cursor?: string;
271
+ }): Promise<PaginatedMarketsResult>;
189
272
  /**
190
273
  * Fetch events with optional keyword search.
191
274
  * Events group related markets together (e.g., "Who will be Fed Chair?" contains multiple candidate markets).
192
275
  *
193
276
  * @param params - Optional parameters for search and filtering
194
- * @param params.query - Search keyword to filter events (required)
277
+ * @param params.query - Search keyword to filter events. If omitted, returns top events by volume.
195
278
  * @param params.limit - Maximum number of results
196
279
  * @param params.offset - Pagination offset
197
280
  * @param params.searchIn - Where to search ('title' | 'description' | 'both')
@@ -280,7 +363,7 @@ export declare abstract class PredictionMarketExchange {
280
363
  * @notes Polymarket: outcomeId is the CLOB Token ID. Kalshi: outcomeId is the Market Ticker.
281
364
  * @notes Resolution options: '1m' | '5m' | '15m' | '1h' | '6h' | '1d'
282
365
  */
283
- fetchOHLCV(id: string, params: OHLCVParams | HistoryFilterParams): Promise<PriceCandle[]>;
366
+ fetchOHLCV(id: string, params: OHLCVParams): Promise<PriceCandle[]>;
284
367
  /**
285
368
  * Fetch the current order book (bids/asks) for a specific outcome.
286
369
  * Essential for calculating spread, depth, and execution prices.
@@ -369,6 +452,56 @@ export declare abstract class PredictionMarketExchange {
369
452
  * )
370
453
  */
371
454
  createOrder(params: CreateOrderParams): Promise<Order>;
455
+ /**
456
+ * Build an order payload without submitting it to the exchange.
457
+ * Returns the exchange-native signed order or request body for inspection,
458
+ * forwarding through a middleware layer, or deferred submission via submitOrder().
459
+ *
460
+ * @param params - Order parameters (same as createOrder)
461
+ * @returns A BuiltOrder containing the exchange-native payload
462
+ *
463
+ * @example-ts Build then inspect a Polymarket order
464
+ * const built = await exchange.buildOrder({
465
+ * marketId: market.marketId,
466
+ * outcomeId: market.yes.outcomeId,
467
+ * side: 'buy',
468
+ * type: 'limit',
469
+ * amount: 10,
470
+ * price: 0.55
471
+ * });
472
+ * console.log(built.signedOrder); // EIP-712 signed order struct
473
+ * const order = await exchange.submitOrder(built);
474
+ *
475
+ * @example-python Build then submit a Polymarket order
476
+ * built = exchange.build_order(
477
+ * market_id=market.market_id,
478
+ * outcome_id=market.yes.outcome_id,
479
+ * side='buy',
480
+ * type='limit',
481
+ * amount=10,
482
+ * price=0.55
483
+ * )
484
+ * print(built.signed_order)
485
+ * order = exchange.submit_order(built)
486
+ */
487
+ buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
488
+ /**
489
+ * Submit a pre-built order returned by buildOrder().
490
+ *
491
+ * @param built - A BuiltOrder from buildOrder()
492
+ * @returns The submitted order
493
+ *
494
+ * @example-ts Submit a pre-built order
495
+ * const built = await exchange.buildOrder(params);
496
+ * const order = await exchange.submitOrder(built);
497
+ * console.log(`Order ${order.id}: ${order.status}`);
498
+ *
499
+ * @example-python Submit a pre-built order
500
+ * built = exchange.build_order(params)
501
+ * order = exchange.submit_order(built)
502
+ * print(f"Order {order.id}: {order.status}")
503
+ */
504
+ submitOrder(built: BuiltOrder): Promise<Order>;
372
505
  /**
373
506
  * Cancel an existing open order.
374
507
  *
@@ -423,6 +556,9 @@ export declare abstract class PredictionMarketExchange {
423
556
  * orders = exchange.fetch_open_orders('FED-25JAN')
424
557
  */
425
558
  fetchOpenOrders(marketId?: string): Promise<Order[]>;
559
+ fetchMyTrades(params?: MyTradesParams): Promise<UserTrade[]>;
560
+ fetchClosedOrders(params?: OrderHistoryParams): Promise<Order[]>;
561
+ fetchAllOrders(params?: OrderHistoryParams): Promise<Order[]>;
426
562
  /**
427
563
  * Fetch current user positions across all markets.
428
564
  *
@@ -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,32 @@ class PredictionMarketExchange {
35
53
  fetchBalance: false,
36
54
  watchOrderBook: false,
37
55
  watchTrades: false,
56
+ fetchMyTrades: false,
57
+ fetchClosedOrders: false,
58
+ fetchAllOrders: false,
59
+ buildOrder: false,
60
+ submitOrder: false,
38
61
  };
39
- constructor(credentials) {
62
+ constructor(credentials, options) {
40
63
  this.credentials = credentials;
41
- this.http = axios_1.default.create();
64
+ this._snapshotTTL = options?.snapshotTTL ?? 0;
65
+ this.http = axios_1.default.create({
66
+ headers: {
67
+ 'User-Agent': `pmxt (https://github.com/pmxt-dev/pmxt)`
68
+ }
69
+ });
70
+ this._throttler = new throttler_1.Throttler({
71
+ refillRate: 1 / this._rateLimit,
72
+ capacity: 1,
73
+ delay: 1,
74
+ });
75
+ // Rate Limit Interceptor
76
+ this.http.interceptors.request.use(async (config) => {
77
+ if (this.enableRateLimit) {
78
+ await this._throttler.throttle();
79
+ }
80
+ return config;
81
+ });
42
82
  // Request Interceptor
43
83
  this.http.interceptors.request.use((config) => {
44
84
  if (this.verbose) {
@@ -71,11 +111,29 @@ class PredictionMarketExchange {
71
111
  });
72
112
  }
73
113
  /**
74
- * Load and cache markets from the exchange.
75
- * This method populates `this.markets` and `this.marketsBySlug`.
114
+ * Load and cache all markets from the exchange into `this.markets` and `this.marketsBySlug`.
115
+ * Subsequent calls return the cached result without hitting the API again.
76
116
  *
77
- * @param reload - Force a reload of markets from the API even if already loaded
117
+ * This is the correct way to paginate or iterate over markets without drift.
118
+ * Because `fetchMarkets()` always hits the API, repeated calls with different `offset`
119
+ * values may return inconsistent results if the exchange reorders or adds markets between
120
+ * requests. Use `loadMarkets()` once to get a stable snapshot, then paginate over
121
+ * `Object.values(exchange.markets)` locally.
122
+ *
123
+ * @param reload - Force a fresh fetch from the API even if markets are already loaded
78
124
  * @returns Dictionary of markets indexed by marketId
125
+ *
126
+ * @example-ts Stable pagination
127
+ * await exchange.loadMarkets();
128
+ * const all = Object.values(exchange.markets);
129
+ * const page1 = all.slice(0, 100);
130
+ * const page2 = all.slice(100, 200);
131
+ *
132
+ * @example-python Stable pagination
133
+ * exchange.load_markets()
134
+ * all = list(exchange.markets.values())
135
+ * page1 = all[:100]
136
+ * page2 = all[100:200]
79
137
  */
80
138
  async loadMarkets(reload = false) {
81
139
  if (this.loadedMarkets && !reload) {
@@ -98,7 +156,7 @@ class PredictionMarketExchange {
98
156
  }
99
157
  /**
100
158
  * Fetch markets with optional filtering, search, or slug lookup.
101
- * This is the primary method for retrieving markets.
159
+ * Always hits the exchange API results reflect the live state at the time of the call.
102
160
  *
103
161
  * @param params - Optional parameters for filtering and search
104
162
  * @param params.query - Search keyword to filter markets
@@ -109,6 +167,10 @@ class PredictionMarketExchange {
109
167
  * @param params.searchIn - Where to search ('title' | 'description' | 'both')
110
168
  * @returns Array of unified markets
111
169
  *
170
+ * @note Calling this repeatedly with different `offset` values does not guarantee stable
171
+ * ordering — exchanges may reorder or add markets between requests. For stable iteration
172
+ * across pages, use `loadMarkets()` and paginate over `Object.values(exchange.markets)`.
173
+ *
112
174
  * @note Some exchanges (like Limitless) may only support status 'active' for search results.
113
175
  *
114
176
  * @example-ts Fetch markets
@@ -128,12 +190,65 @@ class PredictionMarketExchange {
128
190
  async fetchMarkets(params) {
129
191
  return this.fetchMarketsImpl(params);
130
192
  }
193
+ /**
194
+ * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.
195
+ *
196
+ * On the first call (or when no cursor is supplied), fetches all markets once and
197
+ * caches them. Subsequent calls with a cursor returned from a previous call slice
198
+ * directly from the cached snapshot — no additional API calls are made.
199
+ *
200
+ * The snapshot is invalidated after `snapshotTTL` ms (configured via `ExchangeOptions`
201
+ * in the constructor). A request using a cursor from an expired snapshot throws
202
+ * `'Cursor has expired'`.
203
+ *
204
+ * @param params.limit - Page size (default: return all markets)
205
+ * @param params.cursor - Opaque cursor returned by a previous call
206
+ * @returns PaginatedMarketsResult with data, total, and optional nextCursor
207
+ */
208
+ async fetchMarketsPaginated(params) {
209
+ const limit = params?.limit;
210
+ const cursor = params?.cursor;
211
+ if (cursor) {
212
+ // Cursor encodes: snapshotId:offset
213
+ const sep = cursor.indexOf(':');
214
+ const snapshotId = cursor.substring(0, sep);
215
+ const offset = parseInt(cursor.substring(sep + 1), 10);
216
+ if (!this._snapshot ||
217
+ this._snapshot.id !== snapshotId ||
218
+ (this._snapshotTTL > 0 && Date.now() - this._snapshot.takenAt > this._snapshotTTL)) {
219
+ throw new Error('Cursor has expired');
220
+ }
221
+ const markets = this._snapshot.markets;
222
+ const slice = limit !== undefined ? markets.slice(offset, offset + limit) : markets.slice(offset);
223
+ const nextOffset = offset + slice.length;
224
+ const nextCursor = nextOffset < markets.length ? `${snapshotId}:${nextOffset}` : undefined;
225
+ return { data: slice, total: markets.length, nextCursor };
226
+ }
227
+ // No cursor — (re)fetch snapshot
228
+ if (!this._snapshot ||
229
+ this._snapshotTTL === 0 ||
230
+ Date.now() - this._snapshot.takenAt > this._snapshotTTL) {
231
+ const markets = await this.fetchMarketsImpl();
232
+ this._snapshot = {
233
+ markets,
234
+ takenAt: Date.now(),
235
+ id: Math.random().toString(36).slice(2),
236
+ };
237
+ }
238
+ const markets = this._snapshot.markets;
239
+ if (!limit) {
240
+ return { data: markets, total: markets.length, nextCursor: undefined };
241
+ }
242
+ const slice = markets.slice(0, limit);
243
+ const nextCursor = limit < markets.length ? `${this._snapshot.id}:${limit}` : undefined;
244
+ return { data: slice, total: markets.length, nextCursor };
245
+ }
131
246
  /**
132
247
  * Fetch events with optional keyword search.
133
248
  * Events group related markets together (e.g., "Who will be Fed Chair?" contains multiple candidate markets).
134
249
  *
135
250
  * @param params - Optional parameters for search and filtering
136
- * @param params.query - Search keyword to filter events (required)
251
+ * @param params.query - Search keyword to filter events. If omitted, returns top events by volume.
137
252
  * @param params.limit - Maximum number of results
138
253
  * @param params.offset - Pagination offset
139
254
  * @param params.searchIn - Where to search ('title' | 'description' | 'both')
@@ -152,10 +267,7 @@ class PredictionMarketExchange {
152
267
  * print(fed_event.title, len(fed_event.markets), 'markets')
153
268
  */
154
269
  async fetchEvents(params) {
155
- if (!params?.query && !params?.eventId && !params?.slug) {
156
- throw new Error("fetchEvents() requires a query, eventId, or slug parameter");
157
- }
158
- return this.fetchEventsImpl(params);
270
+ return this.fetchEventsImpl(params ?? {});
159
271
  }
160
272
  /**
161
273
  * Fetch a single market by lookup parameters.
@@ -362,6 +474,60 @@ class PredictionMarketExchange {
362
474
  async createOrder(params) {
363
475
  throw new Error("Method createOrder not implemented.");
364
476
  }
477
+ /**
478
+ * Build an order payload without submitting it to the exchange.
479
+ * Returns the exchange-native signed order or request body for inspection,
480
+ * forwarding through a middleware layer, or deferred submission via submitOrder().
481
+ *
482
+ * @param params - Order parameters (same as createOrder)
483
+ * @returns A BuiltOrder containing the exchange-native payload
484
+ *
485
+ * @example-ts Build then inspect a Polymarket order
486
+ * const built = await exchange.buildOrder({
487
+ * marketId: market.marketId,
488
+ * outcomeId: market.yes.outcomeId,
489
+ * side: 'buy',
490
+ * type: 'limit',
491
+ * amount: 10,
492
+ * price: 0.55
493
+ * });
494
+ * console.log(built.signedOrder); // EIP-712 signed order struct
495
+ * const order = await exchange.submitOrder(built);
496
+ *
497
+ * @example-python Build then submit a Polymarket order
498
+ * built = exchange.build_order(
499
+ * market_id=market.market_id,
500
+ * outcome_id=market.yes.outcome_id,
501
+ * side='buy',
502
+ * type='limit',
503
+ * amount=10,
504
+ * price=0.55
505
+ * )
506
+ * print(built.signed_order)
507
+ * order = exchange.submit_order(built)
508
+ */
509
+ async buildOrder(params) {
510
+ throw new Error("Method buildOrder not implemented.");
511
+ }
512
+ /**
513
+ * Submit a pre-built order returned by buildOrder().
514
+ *
515
+ * @param built - A BuiltOrder from buildOrder()
516
+ * @returns The submitted order
517
+ *
518
+ * @example-ts Submit a pre-built order
519
+ * const built = await exchange.buildOrder(params);
520
+ * const order = await exchange.submitOrder(built);
521
+ * console.log(`Order ${order.id}: ${order.status}`);
522
+ *
523
+ * @example-python Submit a pre-built order
524
+ * built = exchange.build_order(params)
525
+ * order = exchange.submit_order(built)
526
+ * print(f"Order {order.id}: {order.status}")
527
+ */
528
+ async submitOrder(built) {
529
+ throw new Error("Method submitOrder not implemented.");
530
+ }
365
531
  /**
366
532
  * Cancel an existing open order.
367
533
  *
@@ -422,6 +588,15 @@ class PredictionMarketExchange {
422
588
  async fetchOpenOrders(marketId) {
423
589
  throw new Error("Method fetchOpenOrders not implemented.");
424
590
  }
591
+ async fetchMyTrades(params) {
592
+ throw new Error("Method fetchMyTrades not implemented.");
593
+ }
594
+ async fetchClosedOrders(params) {
595
+ throw new Error("Method fetchClosedOrders not implemented.");
596
+ }
597
+ async fetchAllOrders(params) {
598
+ throw new Error("Method fetchAllOrders not implemented.");
599
+ }
425
600
  /**
426
601
  * Fetch current user positions across all markets.
427
602
  *
@@ -16,17 +16,22 @@ async function fetchEvents(connection, params) {
16
16
  status: params.status,
17
17
  searchIn: params.searchIn,
18
18
  });
19
- return markets.map(m => ({
20
- id: m.marketId,
21
- title: m.title,
22
- description: m.description,
23
- slug: m.marketId,
24
- markets: [m],
25
- url: m.url,
26
- image: m.image,
27
- category: m.category,
28
- tags: m.tags,
29
- }));
19
+ return markets.map(m => {
20
+ const unifiedEvent = {
21
+ id: m.marketId,
22
+ title: m.title,
23
+ description: m.description,
24
+ slug: m.marketId,
25
+ markets: [m],
26
+ volume24h: m.volume24h,
27
+ volume: m.volume,
28
+ url: m.url,
29
+ image: m.image,
30
+ category: m.category,
31
+ tags: m.tags,
32
+ };
33
+ return unifiedEvent;
34
+ });
30
35
  }
31
36
  catch (error) {
32
37
  throw errors_1.baoziErrorMapper.mapError(error);
@@ -19,6 +19,11 @@ 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;
25
+ buildOrder: false;
26
+ submitOrder: false;
22
27
  };
23
28
  private auth?;
24
29
  private connection;
@@ -28,6 +28,11 @@ 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,
34
+ buildOrder: false,
35
+ submitOrder: false,
31
36
  };
32
37
  auth;
33
38
  connection;
@@ -43,6 +48,7 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
43
48
  credentials = options;
44
49
  }
45
50
  super(credentials);
51
+ this.rateLimit = 500;
46
52
  rpcUrl = rpcUrl
47
53
  || process.env.BAOZI_RPC_URL
48
54
  || 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-18T16:13:58.523Z
3
+ * Generated at: 2026-03-04T17:45:20.579Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const kalshiApiSpec: {
@@ -11,6 +11,12 @@ export declare const kalshiApiSpec: {
11
11
  };
12
12
  servers: {
13
13
  url: string;
14
+ variables: {
15
+ env: {
16
+ default: string;
17
+ enum: string[];
18
+ };
19
+ };
14
20
  }[];
15
21
  paths: {
16
22
  "/historical/cutoff": {
@@ -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-18T16:13:58.523Z
6
+ * Generated at: 2026-03-04T17:45:20.579Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -14,7 +14,16 @@ exports.kalshiApiSpec = {
14
14
  },
15
15
  "servers": [
16
16
  {
17
- "url": "https://api.elections.kalshi.com/trade-api/v2"
17
+ "url": "https://{env}.elections.kalshi.com/trade-api/v2",
18
+ "variables": {
19
+ "env": {
20
+ "default": "api",
21
+ "enum": [
22
+ "api",
23
+ "demo-api"
24
+ ]
25
+ }
26
+ }
18
27
  }
19
28
  ],
20
29
  "paths": {