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.
- package/dist/BaseExchange.d.ts +144 -8
- package/dist/BaseExchange.js +186 -11
- package/dist/exchanges/baozi/fetchEvents.js +16 -11
- package/dist/exchanges/baozi/index.d.ts +5 -0
- package/dist/exchanges/baozi/index.js +6 -0
- package/dist/exchanges/kalshi/api.d.ts +7 -1
- package/dist/exchanges/kalshi/api.js +11 -2
- package/dist/exchanges/kalshi/config.d.ts +103 -0
- package/dist/exchanges/kalshi/config.js +144 -0
- package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchEvents.js +138 -67
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -2
- package/dist/exchanges/kalshi/fetchMarkets.js +36 -25
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +3 -3
- package/dist/exchanges/kalshi/fetchOHLCV.js +20 -17
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
- package/dist/exchanges/kalshi/fetchOrderBook.js +60 -0
- package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
- package/dist/exchanges/kalshi/fetchTrades.js +32 -0
- package/dist/exchanges/kalshi/index.d.ts +20 -4
- package/dist/exchanges/kalshi/index.js +171 -90
- package/dist/exchanges/kalshi/kalshi.test.js +440 -157
- package/dist/exchanges/kalshi/utils.d.ts +1 -3
- package/dist/exchanges/kalshi/utils.js +15 -16
- package/dist/exchanges/kalshi/websocket.d.ts +4 -3
- package/dist/exchanges/kalshi/websocket.js +87 -61
- package/dist/exchanges/kalshi-demo/index.d.ts +10 -0
- package/dist/exchanges/kalshi-demo/index.js +23 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/fetchEvents.d.ts +2 -1
- package/dist/exchanges/limitless/fetchEvents.js +95 -49
- package/dist/exchanges/limitless/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/limitless/index.d.ts +11 -3
- package/dist/exchanges/limitless/index.js +69 -1
- package/dist/exchanges/limitless/utils.js +1 -0
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/myriad/index.d.ts +9 -3
- package/dist/exchanges/myriad/index.js +34 -0
- package/dist/exchanges/myriad/utils.js +5 -1
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket/auth.js +3 -1
- package/dist/exchanges/polymarket/fetchEvents.js +116 -80
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +2 -2
- package/dist/exchanges/polymarket/index.d.ts +30 -6
- package/dist/exchanges/polymarket/index.js +101 -31
- package/dist/exchanges/polymarket/utils.js +1 -0
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +45 -3
- package/dist/exchanges/probable/index.js +61 -0
- package/dist/exchanges/probable/utils.js +5 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +5 -1
- package/dist/server/app.js +56 -48
- package/dist/server/utils/port-manager.js +1 -1
- package/dist/types.d.ts +29 -0
- package/dist/utils/throttler.d.ts +17 -0
- package/dist/utils/throttler.js +50 -0
- package/package.json +7 -4
package/dist/BaseExchange.d.ts
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
|
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
|
*
|
package/dist/BaseExchange.js
CHANGED
|
@@ -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.
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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-
|
|
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-
|
|
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://
|
|
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": {
|