pmxt-core 2.37.0 → 2.37.2
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/exchanges/baozi/websocket.d.ts +6 -0
- package/dist/exchanges/baozi/websocket.js +7 -1
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/websocket.d.ts +2 -0
- package/dist/exchanges/kalshi/websocket.js +43 -20
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/opinion/websocket.d.ts +2 -0
- package/dist/exchanges/opinion/websocket.js +23 -6
- 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/websocket.d.ts +2 -0
- package/dist/exchanges/polymarket/websocket.js +5 -2
- package/dist/exchanges/polymarket_us/websocket.d.ts +6 -1
- package/dist/exchanges/polymarket_us/websocket.js +8 -3
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/websocket.d.ts +2 -0
- package/dist/exchanges/probable/websocket.js +3 -1
- package/dist/server/ws-handler.js +12 -3
- package/dist/utils/watch-timeout.d.ts +18 -0
- package/dist/utils/watch-timeout.js +35 -0
- package/package.json +3 -3
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { Connection } from '@solana/web3.js';
|
|
2
2
|
import { OrderBook } from '../../types';
|
|
3
|
+
export interface BaoziWebSocketConfig {
|
|
4
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
5
|
+
watchTimeoutMs?: number;
|
|
6
|
+
}
|
|
3
7
|
/**
|
|
4
8
|
* Uses Solana's onAccountChange to watch market PDA updates.
|
|
5
9
|
* When the account data changes (new bet placed), we re-parse
|
|
6
10
|
* and emit a new synthetic order book.
|
|
7
11
|
*/
|
|
8
12
|
export declare class BaoziWebSocket {
|
|
13
|
+
private config;
|
|
9
14
|
private orderBookResolvers;
|
|
10
15
|
private subscriptions;
|
|
16
|
+
constructor(config?: BaoziWebSocketConfig);
|
|
11
17
|
watchOrderBook(connection: Connection, outcomeId: string): Promise<OrderBook>;
|
|
12
18
|
private resolveOrderBook;
|
|
13
19
|
close(connection: Connection): Promise<void>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaoziWebSocket = void 0;
|
|
4
4
|
const web3_js_1 = require("@solana/web3.js");
|
|
5
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
5
6
|
const utils_1 = require("./utils");
|
|
6
7
|
/**
|
|
7
8
|
* Uses Solana's onAccountChange to watch market PDA updates.
|
|
@@ -9,8 +10,12 @@ const utils_1 = require("./utils");
|
|
|
9
10
|
* and emit a new synthetic order book.
|
|
10
11
|
*/
|
|
11
12
|
class BaoziWebSocket {
|
|
13
|
+
config;
|
|
12
14
|
orderBookResolvers = new Map();
|
|
13
15
|
subscriptions = new Map();
|
|
16
|
+
constructor(config = {}) {
|
|
17
|
+
this.config = config;
|
|
18
|
+
}
|
|
14
19
|
async watchOrderBook(connection, outcomeId) {
|
|
15
20
|
const marketPubkey = outcomeId.replace(/-YES$|-NO$|-\d+$/, '');
|
|
16
21
|
const marketKey = new web3_js_1.PublicKey(marketPubkey);
|
|
@@ -45,12 +50,13 @@ class BaoziWebSocket {
|
|
|
45
50
|
}, 'confirmed');
|
|
46
51
|
this.subscriptions.set(marketPubkey, subId);
|
|
47
52
|
}
|
|
48
|
-
|
|
53
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
49
54
|
if (!this.orderBookResolvers.has(marketPubkey)) {
|
|
50
55
|
this.orderBookResolvers.set(marketPubkey, []);
|
|
51
56
|
}
|
|
52
57
|
this.orderBookResolvers.get(marketPubkey).push({ resolve, reject });
|
|
53
58
|
});
|
|
59
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${outcomeId}')`);
|
|
54
60
|
}
|
|
55
61
|
resolveOrderBook(marketPubkey, orderBook) {
|
|
56
62
|
const resolvers = this.orderBookResolvers.get(marketPubkey);
|
|
@@ -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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.700Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.700Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -5,6 +5,8 @@ export interface KalshiWebSocketConfig {
|
|
|
5
5
|
wsUrl?: string;
|
|
6
6
|
/** Reconnection interval in milliseconds (default: 5000) */
|
|
7
7
|
reconnectIntervalMs?: number;
|
|
8
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
9
|
+
watchTimeoutMs?: number;
|
|
8
10
|
}
|
|
9
11
|
/**
|
|
10
12
|
* Kalshi WebSocket implementation for real-time order book and trade streaming.
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.KalshiWebSocket = void 0;
|
|
7
7
|
const ws_1 = __importDefault(require("ws"));
|
|
8
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
8
9
|
/**
|
|
9
10
|
* Kalshi WebSocket implementation for real-time order book and trade streaming.
|
|
10
11
|
* Follows CCXT Pro-style async iterator pattern.
|
|
@@ -317,51 +318,68 @@ class KalshiWebSocket {
|
|
|
317
318
|
}
|
|
318
319
|
}
|
|
319
320
|
async watchOrderBook(ticker) {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
await this.connect();
|
|
321
|
+
if (this.isTerminated) {
|
|
322
|
+
throw new Error(`WebSocket terminated, cannot watch ${ticker}`);
|
|
323
323
|
}
|
|
324
|
-
//
|
|
324
|
+
// Track the subscription regardless of connection state.
|
|
325
|
+
// When (re)connected, the open handler resubscribes automatically.
|
|
325
326
|
if (!this.subscribedOrderBookTickers.has(ticker)) {
|
|
326
327
|
this.subscribedOrderBookTickers.add(ticker);
|
|
328
|
+
}
|
|
329
|
+
// Attempt connection — if it fails, scheduleReconnect handles recovery.
|
|
330
|
+
// The resolver will be fulfilled once the connection is (re)established
|
|
331
|
+
// and data arrives.
|
|
332
|
+
if (!this.isConnected) {
|
|
333
|
+
this.connect().catch(() => {
|
|
334
|
+
// Connection failed — scheduleReconnect is already queued via the
|
|
335
|
+
// close handler, so we intentionally swallow here. The pending
|
|
336
|
+
// resolver will be resolved when data arrives on the next connection.
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
// Already connected — ensure subscription message is sent
|
|
327
341
|
this.subscribeToOrderbook([ticker]);
|
|
328
342
|
}
|
|
329
343
|
// Return a promise that resolves on the next orderbook update
|
|
330
|
-
|
|
344
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
331
345
|
if (!this.orderBookResolvers.has(ticker)) {
|
|
332
346
|
this.orderBookResolvers.set(ticker, []);
|
|
333
347
|
}
|
|
334
348
|
this.orderBookResolvers.get(ticker).push({ resolve, reject });
|
|
335
349
|
});
|
|
350
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${ticker}')`);
|
|
336
351
|
}
|
|
337
352
|
async watchOrderBooks(tickers) {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
await this.connect();
|
|
353
|
+
if (this.isTerminated) {
|
|
354
|
+
throw new Error("WebSocket terminated, cannot watch orderbooks");
|
|
341
355
|
}
|
|
342
|
-
//
|
|
356
|
+
// Track subscriptions regardless of connection state.
|
|
343
357
|
const newTickers = tickers.filter((t) => !this.subscribedOrderBookTickers.has(t));
|
|
344
|
-
// Add all new tickers to the subscription set
|
|
345
358
|
for (const t of newTickers) {
|
|
346
359
|
this.subscribedOrderBookTickers.add(t);
|
|
347
360
|
}
|
|
348
|
-
//
|
|
349
|
-
if (
|
|
361
|
+
// Attempt connection — if it fails, scheduleReconnect handles recovery.
|
|
362
|
+
if (!this.isConnected) {
|
|
363
|
+
this.connect().catch(() => {
|
|
364
|
+
// Swallow — scheduleReconnect will retry. Resolvers stay pending.
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
else if (newTickers.length > 0) {
|
|
350
368
|
this.subscribeToOrderbook(newTickers);
|
|
351
369
|
}
|
|
352
370
|
// Wait for all tickers to receive at least one snapshot/update
|
|
353
|
-
const
|
|
371
|
+
const dataPromise = Promise.all(tickers.map((ticker) => new Promise((resolve, reject) => {
|
|
354
372
|
if (!this.orderBookResolvers.has(ticker)) {
|
|
355
373
|
this.orderBookResolvers.set(ticker, []);
|
|
356
374
|
}
|
|
357
375
|
this.orderBookResolvers.get(ticker).push({
|
|
358
376
|
resolve: (book) => {
|
|
359
|
-
// Unwrap PromiseLike if needed, but in practice it is always OrderBook
|
|
360
377
|
Promise.resolve(book).then((b) => resolve([ticker, b]));
|
|
361
378
|
},
|
|
362
379
|
reject,
|
|
363
380
|
});
|
|
364
381
|
})));
|
|
382
|
+
const entries = await (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBooks(${JSON.stringify(tickers)})`);
|
|
365
383
|
const result = {};
|
|
366
384
|
for (const [ticker, book] of entries) {
|
|
367
385
|
result[ticker] = book;
|
|
@@ -369,22 +387,27 @@ class KalshiWebSocket {
|
|
|
369
387
|
return result;
|
|
370
388
|
}
|
|
371
389
|
async watchTrades(ticker) {
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
await this.connect();
|
|
390
|
+
if (this.isTerminated) {
|
|
391
|
+
throw new Error(`WebSocket terminated, cannot watch trades for ${ticker}`);
|
|
375
392
|
}
|
|
376
|
-
// Subscribe if not already subscribed
|
|
377
393
|
if (!this.subscribedTradeTickers.has(ticker)) {
|
|
378
394
|
this.subscribedTradeTickers.add(ticker);
|
|
395
|
+
}
|
|
396
|
+
if (!this.isConnected) {
|
|
397
|
+
this.connect().catch(() => {
|
|
398
|
+
// Swallow — scheduleReconnect will retry. Resolvers stay pending.
|
|
399
|
+
});
|
|
400
|
+
}
|
|
401
|
+
else {
|
|
379
402
|
this.subscribeToTrades([ticker]);
|
|
380
403
|
}
|
|
381
|
-
|
|
382
|
-
return new Promise((resolve, reject) => {
|
|
404
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
383
405
|
if (!this.tradeResolvers.has(ticker)) {
|
|
384
406
|
this.tradeResolvers.set(ticker, []);
|
|
385
407
|
}
|
|
386
408
|
this.tradeResolvers.get(ticker).push({ resolve, reject });
|
|
387
409
|
});
|
|
410
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${ticker}')`);
|
|
388
411
|
}
|
|
389
412
|
async close() {
|
|
390
413
|
this.isTerminated = true;
|
|
@@ -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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.746Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.746Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.759Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.759Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
3
|
-
* Generated at: 2026-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.764Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const opinionApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.opinionApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
|
|
6
|
-
* Generated at: 2026-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.764Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.opinionApiSpec = {
|
|
@@ -2,6 +2,8 @@ import { OrderBook, Trade } from "../../types";
|
|
|
2
2
|
export interface OpinionWebSocketConfig {
|
|
3
3
|
/** Reconnection interval in milliseconds (default: 5000) */
|
|
4
4
|
reconnectIntervalMs?: number;
|
|
5
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
6
|
+
watchTimeoutMs?: number;
|
|
5
7
|
}
|
|
6
8
|
/**
|
|
7
9
|
* Opinion Trade WebSocket implementation for real-time order book and trade streaming.
|
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.OpinionWebSocket = void 0;
|
|
7
7
|
const ws_1 = __importDefault(require("ws"));
|
|
8
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
8
9
|
/**
|
|
9
10
|
* Opinion Trade WebSocket implementation for real-time order book and trade streaming.
|
|
10
11
|
* Follows CCXT Pro-style async iterator pattern.
|
|
@@ -248,38 +249,54 @@ class OpinionWebSocket {
|
|
|
248
249
|
* Returns a promise that resolves on the next orderbook update.
|
|
249
250
|
*/
|
|
250
251
|
async watchOrderBook(marketId) {
|
|
251
|
-
if (
|
|
252
|
-
|
|
252
|
+
if (this.isTerminated) {
|
|
253
|
+
throw new Error(`WebSocket terminated, cannot watch market ${marketId}`);
|
|
253
254
|
}
|
|
254
255
|
if (!this.subscribedDepthMarketIds.has(marketId)) {
|
|
255
256
|
this.subscribedDepthMarketIds.add(marketId);
|
|
257
|
+
}
|
|
258
|
+
if (!this.isConnected) {
|
|
259
|
+
this.connect().catch(() => {
|
|
260
|
+
// Swallow — scheduleReconnect will retry. Resolvers stay pending.
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
256
264
|
this.sendSubscribe("market.depth.diff", marketId);
|
|
257
265
|
}
|
|
258
|
-
|
|
266
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
259
267
|
if (!this.orderBookResolvers.has(marketId)) {
|
|
260
268
|
this.orderBookResolvers.set(marketId, []);
|
|
261
269
|
}
|
|
262
270
|
this.orderBookResolvers.get(marketId).push({ resolve, reject });
|
|
263
271
|
});
|
|
272
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${marketId}')`);
|
|
264
273
|
}
|
|
265
274
|
/**
|
|
266
275
|
* Watch trade updates for a given binary market.
|
|
267
276
|
* Returns a promise that resolves on the next trade.
|
|
268
277
|
*/
|
|
269
278
|
async watchTrades(marketId) {
|
|
270
|
-
if (
|
|
271
|
-
|
|
279
|
+
if (this.isTerminated) {
|
|
280
|
+
throw new Error(`WebSocket terminated, cannot watch trades for market ${marketId}`);
|
|
272
281
|
}
|
|
273
282
|
if (!this.subscribedTradeMarketIds.has(marketId)) {
|
|
274
283
|
this.subscribedTradeMarketIds.add(marketId);
|
|
284
|
+
}
|
|
285
|
+
if (!this.isConnected) {
|
|
286
|
+
this.connect().catch(() => {
|
|
287
|
+
// Swallow — scheduleReconnect will retry. Resolvers stay pending.
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
else {
|
|
275
291
|
this.sendSubscribe("market.last.trade", marketId);
|
|
276
292
|
}
|
|
277
|
-
|
|
293
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
278
294
|
if (!this.tradeResolvers.has(marketId)) {
|
|
279
295
|
this.tradeResolvers.set(marketId, []);
|
|
280
296
|
}
|
|
281
297
|
this.tradeResolvers.get(marketId).push({ resolve, reject });
|
|
282
298
|
});
|
|
299
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${marketId}')`);
|
|
283
300
|
}
|
|
284
301
|
/**
|
|
285
302
|
* Close the WebSocket connection and reject all pending promises.
|
|
@@ -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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.707Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.707Z
|
|
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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.724Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.724Z
|
|
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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.720Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.720Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.polymarketGammaSpec = {
|
|
@@ -15,6 +15,8 @@ export interface PolymarketWebSocketConfig {
|
|
|
15
15
|
flushIntervalMs?: number;
|
|
16
16
|
/** Watcher subscription configurations */
|
|
17
17
|
watcherConfig?: WatcherConfig;
|
|
18
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
19
|
+
watchTimeoutMs?: number;
|
|
18
20
|
}
|
|
19
21
|
/**
|
|
20
22
|
* Wrapper around @nevuamarkets/poly-websockets that provides CCXT Pro-style
|
|
@@ -43,6 +43,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
43
43
|
exports.PolymarketWebSocket = void 0;
|
|
44
44
|
const goldsky_1 = require("../../subscriber/external/goldsky");
|
|
45
45
|
const watcher_1 = require("../../subscriber/watcher");
|
|
46
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
46
47
|
/**
|
|
47
48
|
* Wrapper around @nevuamarkets/poly-websockets that provides CCXT Pro-style
|
|
48
49
|
* watchOrderBook() and watchTrades() methods.
|
|
@@ -75,12 +76,13 @@ class PolymarketWebSocket {
|
|
|
75
76
|
await this.manager.addSubscriptions([id]);
|
|
76
77
|
}
|
|
77
78
|
// Return a promise that resolves on the next orderbook update
|
|
78
|
-
|
|
79
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
79
80
|
if (!this.orderBookResolvers.has(id)) {
|
|
80
81
|
this.orderBookResolvers.set(id, []);
|
|
81
82
|
}
|
|
82
83
|
this.orderBookResolvers.get(id).push({ resolve, reject });
|
|
83
84
|
});
|
|
85
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${id}')`);
|
|
84
86
|
}
|
|
85
87
|
async unwatchOrderBook(id) {
|
|
86
88
|
if (!this.manager) {
|
|
@@ -108,12 +110,13 @@ class PolymarketWebSocket {
|
|
|
108
110
|
await this.manager.addSubscriptions([id]);
|
|
109
111
|
}
|
|
110
112
|
// Return a promise that resolves on the next trade
|
|
111
|
-
|
|
113
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
112
114
|
if (!this.tradeResolvers.has(id)) {
|
|
113
115
|
this.tradeResolvers.set(id, []);
|
|
114
116
|
}
|
|
115
117
|
this.tradeResolvers.get(id).push({ resolve, reject });
|
|
116
118
|
});
|
|
119
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${id}')`);
|
|
117
120
|
}
|
|
118
121
|
async watchAddress(address, types) {
|
|
119
122
|
return this.watcher.watch(address, types);
|
|
@@ -16,16 +16,21 @@
|
|
|
16
16
|
import type { PolymarketUS as PolymarketUSClient } from 'polymarket-us';
|
|
17
17
|
import { OrderBook, Trade } from '../../types';
|
|
18
18
|
import { PolymarketUSNormalizer } from './normalizer';
|
|
19
|
+
export interface PolymarketUSWebSocketConfig {
|
|
20
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
21
|
+
watchTimeoutMs?: number;
|
|
22
|
+
}
|
|
19
23
|
export declare class PolymarketUSWebSocket {
|
|
20
24
|
private readonly client;
|
|
21
25
|
private readonly normalizer;
|
|
26
|
+
private readonly config;
|
|
22
27
|
private socket;
|
|
23
28
|
private initializationPromise?;
|
|
24
29
|
private readonly bookSubscriptions;
|
|
25
30
|
private readonly tradeSubscriptions;
|
|
26
31
|
private readonly orderBookResolvers;
|
|
27
32
|
private readonly tradeResolvers;
|
|
28
|
-
constructor(client: PolymarketUSClient, normalizer: PolymarketUSNormalizer);
|
|
33
|
+
constructor(client: PolymarketUSClient, normalizer: PolymarketUSNormalizer, config?: PolymarketUSWebSocketConfig);
|
|
29
34
|
watchOrderBook(id: string): Promise<OrderBook>;
|
|
30
35
|
watchTrades(id: string): Promise<Trade[]>;
|
|
31
36
|
close(): Promise<void>;
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
exports.PolymarketUSWebSocket = void 0;
|
|
19
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
19
20
|
const price_1 = require("./price");
|
|
20
21
|
function sideFromOrderSide(raw) {
|
|
21
22
|
if (raw === 'ORDER_SIDE_BUY')
|
|
@@ -44,15 +45,17 @@ function slugFromId(id) {
|
|
|
44
45
|
class PolymarketUSWebSocket {
|
|
45
46
|
client;
|
|
46
47
|
normalizer;
|
|
48
|
+
config;
|
|
47
49
|
socket = null;
|
|
48
50
|
initializationPromise;
|
|
49
51
|
bookSubscriptions = new Set();
|
|
50
52
|
tradeSubscriptions = new Set();
|
|
51
53
|
orderBookResolvers = new Map();
|
|
52
54
|
tradeResolvers = new Map();
|
|
53
|
-
constructor(client, normalizer) {
|
|
55
|
+
constructor(client, normalizer, config = {}) {
|
|
54
56
|
this.client = client;
|
|
55
57
|
this.normalizer = normalizer;
|
|
58
|
+
this.config = config;
|
|
56
59
|
}
|
|
57
60
|
async watchOrderBook(id) {
|
|
58
61
|
const slug = slugFromId(id);
|
|
@@ -61,11 +64,12 @@ class PolymarketUSWebSocket {
|
|
|
61
64
|
this.bookSubscriptions.add(slug);
|
|
62
65
|
this.socket.subscribeMarketData(`book:${slug}`, [slug]);
|
|
63
66
|
}
|
|
64
|
-
|
|
67
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
65
68
|
const queue = this.orderBookResolvers.get(slug) ?? [];
|
|
66
69
|
queue.push({ resolve, reject });
|
|
67
70
|
this.orderBookResolvers.set(slug, queue);
|
|
68
71
|
});
|
|
72
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${id}')`);
|
|
69
73
|
}
|
|
70
74
|
async watchTrades(id) {
|
|
71
75
|
const slug = slugFromId(id);
|
|
@@ -74,11 +78,12 @@ class PolymarketUSWebSocket {
|
|
|
74
78
|
this.tradeSubscriptions.add(slug);
|
|
75
79
|
this.socket.subscribeTrades(`trade:${slug}`, [slug]);
|
|
76
80
|
}
|
|
77
|
-
|
|
81
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
78
82
|
const queue = this.tradeResolvers.get(slug) ?? [];
|
|
79
83
|
queue.push({ resolve, reject });
|
|
80
84
|
this.tradeResolvers.set(slug, queue);
|
|
81
85
|
});
|
|
86
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${id}')`);
|
|
82
87
|
}
|
|
83
88
|
async close() {
|
|
84
89
|
if (this.socket) {
|
|
@@ -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-05-03T09:
|
|
3
|
+
* Generated at: 2026-05-03T09:26:01.752Z
|
|
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-05-03T09:
|
|
6
|
+
* Generated at: 2026-05-03T09:26:01.752Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.probableApiSpec = {
|
|
@@ -6,6 +6,8 @@ export interface ProbableWebSocketConfig {
|
|
|
6
6
|
baseUrl?: string;
|
|
7
7
|
/** Chain ID (default: 56 for BSC mainnet) */
|
|
8
8
|
chainId?: number;
|
|
9
|
+
/** Timeout in ms for watch methods to receive data (default: 30000). 0 = no timeout. */
|
|
10
|
+
watchTimeoutMs?: number;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Probable WebSocket implementation for real-time order book streaming.
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ProbableWebSocket = void 0;
|
|
4
|
+
const watch_timeout_1 = require("../../utils/watch-timeout");
|
|
4
5
|
/**
|
|
5
6
|
* Probable WebSocket implementation for real-time order book streaming.
|
|
6
7
|
* Uses the @prob/clob SDK's subscribePublicStream (no auth required).
|
|
@@ -42,12 +43,13 @@ class ProbableWebSocket {
|
|
|
42
43
|
this.subscriptions.set(tokenId, sub);
|
|
43
44
|
}
|
|
44
45
|
// Return a promise that resolves on the next orderbook update
|
|
45
|
-
|
|
46
|
+
const dataPromise = new Promise((resolve, reject) => {
|
|
46
47
|
if (!this.orderBookResolvers.has(tokenId)) {
|
|
47
48
|
this.orderBookResolvers.set(tokenId, []);
|
|
48
49
|
}
|
|
49
50
|
this.orderBookResolvers.get(tokenId).push({ resolve, reject });
|
|
50
51
|
});
|
|
52
|
+
return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${tokenId}')`);
|
|
51
53
|
}
|
|
52
54
|
handleOrderBookUpdate(tokenId, data) {
|
|
53
55
|
const bids = (data.bids || []).map((b) => ({
|
|
@@ -34,6 +34,10 @@ function subscriptionKey(msg) {
|
|
|
34
34
|
/**
|
|
35
35
|
* Start a streaming loop for a single-ticker watch method
|
|
36
36
|
* (watchOrderBook, watchTrades).
|
|
37
|
+
*
|
|
38
|
+
* The exchange layer owns connection lifecycle — watchOrderBook() blocks until
|
|
39
|
+
* data arrives, transparently handling reconnection. This loop is a simple
|
|
40
|
+
* consumer that only terminates on abort or fatal errors.
|
|
37
41
|
*/
|
|
38
42
|
async function streamSingle(exchange, method, args, id, ws, signal) {
|
|
39
43
|
const symbol = typeof args[0] === "string" ? args[0] : String(args[0]);
|
|
@@ -56,14 +60,18 @@ async function streamSingle(exchange, method, args, id, ws, signal) {
|
|
|
56
60
|
: "Unknown streaming error";
|
|
57
61
|
const code = err instanceof errors_1.BaseError ? err.code : undefined;
|
|
58
62
|
sendError(ws, id, message, code);
|
|
59
|
-
//
|
|
60
|
-
|
|
63
|
+
// Fatal error from exchange (terminated, auth failure, etc.) — stop streaming.
|
|
64
|
+
// The exchange layer handles transient reconnection internally and should
|
|
65
|
+
// never throw for recoverable connection drops.
|
|
66
|
+
break;
|
|
61
67
|
}
|
|
62
68
|
}
|
|
63
69
|
}
|
|
64
70
|
/**
|
|
65
71
|
* Start a streaming loop for the batch watchOrderBooks method.
|
|
66
72
|
* Each update is sent as an individual data message per symbol.
|
|
73
|
+
*
|
|
74
|
+
* Same lifecycle contract as streamSingle — the exchange owns reconnection.
|
|
67
75
|
*/
|
|
68
76
|
async function streamBatch(exchange, method, args, id, ws, signal) {
|
|
69
77
|
const ids = Array.isArray(args[0]) ? args[0] : [];
|
|
@@ -87,7 +95,8 @@ async function streamBatch(exchange, method, args, id, ws, signal) {
|
|
|
87
95
|
: "Unknown streaming error";
|
|
88
96
|
const code = err instanceof errors_1.BaseError ? err.code : undefined;
|
|
89
97
|
sendError(ws, id, message, code);
|
|
90
|
-
|
|
98
|
+
// Fatal error — stop streaming. Exchange handles transient reconnection.
|
|
99
|
+
break;
|
|
91
100
|
}
|
|
92
101
|
}
|
|
93
102
|
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Default timeout for watch methods (30 seconds).
|
|
3
|
+
*
|
|
4
|
+
* Generous enough for valid but illiquid markets, but prevents
|
|
5
|
+
* hanging forever on non-existing IDs.
|
|
6
|
+
*/
|
|
7
|
+
export declare const DEFAULT_WATCH_TIMEOUT_MS = 30000;
|
|
8
|
+
/**
|
|
9
|
+
* Wrap a watch promise with a timeout that rejects if no data arrives.
|
|
10
|
+
*
|
|
11
|
+
* Used by all exchange WebSocket implementations to prevent indefinite
|
|
12
|
+
* hangs when subscribing to non-existing market IDs.
|
|
13
|
+
*
|
|
14
|
+
* @param promise - The data promise (resolves when WS data arrives)
|
|
15
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (0 = no timeout)
|
|
16
|
+
* @param label - Human-readable label for the error message
|
|
17
|
+
*/
|
|
18
|
+
export declare function withWatchTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T>;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_WATCH_TIMEOUT_MS = void 0;
|
|
4
|
+
exports.withWatchTimeout = withWatchTimeout;
|
|
5
|
+
/**
|
|
6
|
+
* Default timeout for watch methods (30 seconds).
|
|
7
|
+
*
|
|
8
|
+
* Generous enough for valid but illiquid markets, but prevents
|
|
9
|
+
* hanging forever on non-existing IDs.
|
|
10
|
+
*/
|
|
11
|
+
exports.DEFAULT_WATCH_TIMEOUT_MS = 30_000;
|
|
12
|
+
/**
|
|
13
|
+
* Wrap a watch promise with a timeout that rejects if no data arrives.
|
|
14
|
+
*
|
|
15
|
+
* Used by all exchange WebSocket implementations to prevent indefinite
|
|
16
|
+
* hangs when subscribing to non-existing market IDs.
|
|
17
|
+
*
|
|
18
|
+
* @param promise - The data promise (resolves when WS data arrives)
|
|
19
|
+
* @param timeoutMs - Maximum time to wait in milliseconds (0 = no timeout)
|
|
20
|
+
* @param label - Human-readable label for the error message
|
|
21
|
+
*/
|
|
22
|
+
function withWatchTimeout(promise, timeoutMs, label) {
|
|
23
|
+
if (timeoutMs <= 0)
|
|
24
|
+
return promise;
|
|
25
|
+
let timer;
|
|
26
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
27
|
+
timer = setTimeout(() => {
|
|
28
|
+
reject(new Error(`${label}: timed out after ${timeoutMs}ms waiting for data. ` +
|
|
29
|
+
`The ID may not exist on this exchange.`));
|
|
30
|
+
}, timeoutMs);
|
|
31
|
+
});
|
|
32
|
+
return Promise.race([promise, timeoutPromise]).finally(() => {
|
|
33
|
+
clearTimeout(timer);
|
|
34
|
+
});
|
|
35
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.37.
|
|
3
|
+
"version": "2.37.2",
|
|
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.37.
|
|
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.37.
|
|
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.37.2,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.37.2,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",
|