pmxt-core 2.48.6 → 2.49.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/exchanges/gemini-titan/normalizer.js +9 -3
  2. package/dist/exchanges/gemini-titan/websocket.js +17 -5
  3. package/dist/exchanges/hyperliquid/normalizer.js +2 -0
  4. package/dist/exchanges/interfaces.d.ts +2 -2
  5. package/dist/exchanges/kalshi/api.d.ts +1 -1
  6. package/dist/exchanges/kalshi/api.js +2 -2
  7. package/dist/exchanges/kalshi/config.d.ts +6 -6
  8. package/dist/exchanges/kalshi/config.js +8 -8
  9. package/dist/exchanges/kalshi/fetcher.d.ts +2 -0
  10. package/dist/exchanges/kalshi/fetcher.js +6 -7
  11. package/dist/exchanges/kalshi/normalizer.js +6 -1
  12. package/dist/exchanges/kalshi/websocket.js +21 -9
  13. package/dist/exchanges/limitless/api.d.ts +1 -1
  14. package/dist/exchanges/limitless/api.js +1 -1
  15. package/dist/exchanges/limitless/client.js +2 -2
  16. package/dist/exchanges/limitless/config.d.ts +2 -0
  17. package/dist/exchanges/limitless/config.js +3 -1
  18. package/dist/exchanges/limitless/fetcher.d.ts +10 -0
  19. package/dist/exchanges/limitless/fetcher.js +2 -1
  20. package/dist/exchanges/limitless/index.js +1 -1
  21. package/dist/exchanges/limitless/utils.js +3 -3
  22. package/dist/exchanges/limitless/websocket.js +8 -11
  23. package/dist/exchanges/metaculus/utils.js +0 -1
  24. package/dist/exchanges/myriad/api.d.ts +1 -1
  25. package/dist/exchanges/myriad/api.js +17 -2
  26. package/dist/exchanges/myriad/normalizer.js +10 -5
  27. package/dist/exchanges/myriad/utils.js +2 -0
  28. package/dist/exchanges/opinion/api.d.ts +16 -1
  29. package/dist/exchanges/opinion/api.js +20 -1
  30. package/dist/exchanges/opinion/websocket.js +21 -14
  31. package/dist/exchanges/polymarket/api-clob.d.ts +13 -1
  32. package/dist/exchanges/polymarket/api-clob.js +26 -1
  33. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  34. package/dist/exchanges/polymarket/api-data.js +1 -1
  35. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  36. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  37. package/dist/exchanges/polymarket/auth.js +3 -3
  38. package/dist/exchanges/polymarket/config.d.ts +1 -0
  39. package/dist/exchanges/polymarket/config.js +4 -0
  40. package/dist/exchanges/polymarket/index.js +2 -1
  41. package/dist/exchanges/polymarket_us/websocket.js +3 -2
  42. package/dist/exchanges/probable/api.d.ts +1 -1
  43. package/dist/exchanges/probable/api.js +1 -1
  44. package/dist/exchanges/probable/auth.js +5 -4
  45. package/dist/exchanges/probable/config.d.ts +2 -0
  46. package/dist/exchanges/probable/config.js +5 -0
  47. package/dist/exchanges/probable/websocket.js +2 -1
  48. package/dist/exchanges/suibets/api.d.ts +1 -1
  49. package/dist/exchanges/suibets/api.js +1 -1
  50. package/dist/exchanges/suibets/config.d.ts +1 -1
  51. package/dist/exchanges/suibets/config.js +2 -2
  52. package/dist/exchanges/suibets/errors.js +9 -0
  53. package/dist/exchanges/suibets/fetcher.d.ts +22 -2
  54. package/dist/exchanges/suibets/fetcher.js +23 -5
  55. package/dist/exchanges/suibets/index.d.ts +1 -1
  56. package/dist/exchanges/suibets/index.js +1 -1
  57. package/dist/exchanges/suibets/normalizer.js +2 -22
  58. package/dist/feeds/binance/binance-feed.js +6 -3
  59. package/dist/feeds/chainlink/chainlink-feed.js +5 -2
  60. package/dist/server/openapi.yaml +2 -0
  61. package/dist/types.d.ts +1 -0
  62. package/dist/utils/throttler.js +3 -5
  63. package/package.json +4 -4
@@ -181,11 +181,17 @@ class GeminiNormalizer {
181
181
  // Extract prices
182
182
  const bestBid = contract.prices?.bestBid ? parseFloat(contract.prices.bestBid) : 0.5;
183
183
  const bestAsk = contract.prices?.bestAsk ? parseFloat(contract.prices.bestAsk) : 0.5;
184
+ const buyYes = contract.prices?.buy?.yes ? parseFloat(contract.prices.buy.yes) : undefined;
185
+ const sellYes = contract.prices?.sell?.yes ? parseFloat(contract.prices.sell.yes) : undefined;
186
+ const buyNo = contract.prices?.buy?.no ? parseFloat(contract.prices.buy.no) : undefined;
187
+ const sellNo = contract.prices?.sell?.no ? parseFloat(contract.prices.sell.no) : undefined;
184
188
  const lastPrice = contract.prices?.lastTradePrice
185
189
  ? parseFloat(contract.prices.lastTradePrice)
186
190
  : (bestBid + bestAsk) / 2;
187
- const yesPrice = roundPrice(Math.max(0, Math.min(1, lastPrice)));
188
- const noPrice = roundPrice(Math.max(0, Math.min(1, 1 - yesPrice)));
191
+ const yesPriceSource = buyYes ?? sellYes ?? lastPrice;
192
+ const noPriceSource = buyNo ?? sellNo ?? (1 - yesPriceSource);
193
+ const yesPrice = roundPrice(Math.max(0, Math.min(1, yesPriceSource)));
194
+ const noPrice = roundPrice(Math.max(0, Math.min(1, noPriceSource)));
189
195
  const outcomes = [
190
196
  {
191
197
  outcomeId: (0, utils_1.toOutcomeId)(instrumentSymbol, 'yes'),
@@ -220,7 +226,7 @@ class GeminiNormalizer {
220
226
  outcomes,
221
227
  resolutionDate,
222
228
  volume24h: 0,
223
- liquidity: event.liquidity ? parseFloat(event.liquidity) : 0,
229
+ liquidity: event.liquidity ? parseFloat(event.liquidity) : (event.volume24h ? parseFloat(event.volume24h) : (event.volume ? parseFloat(event.volume) : 0)),
224
230
  url: buildExchangeUrl(event.ticker),
225
231
  category: event.category,
226
232
  tags,
@@ -76,8 +76,10 @@ class GeminiWebSocket {
76
76
  const message = JSON.parse(data.toString());
77
77
  this.handleMessage(message);
78
78
  }
79
- catch {
80
- // Ignore unparseable messages
79
+ catch (error) {
80
+ logger_1.logger.warn('[gemini-titan] failed to parse or handle message', {
81
+ error: error instanceof Error ? error.message : String(error),
82
+ });
81
83
  }
82
84
  });
83
85
  this.ws.on('error', (error) => {
@@ -179,7 +181,7 @@ class GeminiWebSocket {
179
181
  this.resolveOrderBook(symbol, existing);
180
182
  }
181
183
  applyDelta(levels, price, size, sortOrder) {
182
- const idx = levels.findIndex(l => Math.abs(l.price - price) < 0.0001);
184
+ const idx = levels.findIndex(l => l.price === price);
183
185
  if (size === 0) {
184
186
  if (idx !== -1)
185
187
  levels.splice(idx, 1);
@@ -229,7 +231,12 @@ class GeminiWebSocket {
229
231
  this.subscribedDepthSymbols.add(symbol);
230
232
  if (!this.isConnected) {
231
233
  this.connect().catch((err) => {
232
- logger_1.logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}'): ${err instanceof Error ? err.message : String(err)}`);
234
+ logger_1.logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}')`, {
235
+ error: err instanceof Error ? err.message : String(err),
236
+ });
237
+ if (!this.isTerminated) {
238
+ this.scheduleReconnect();
239
+ }
233
240
  });
234
241
  }
235
242
  else {
@@ -255,7 +262,12 @@ class GeminiWebSocket {
255
262
  this.subscribedTradeSymbols.add(symbol);
256
263
  if (!this.isConnected) {
257
264
  this.connect().catch((err) => {
258
- logger_1.logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}'): ${err instanceof Error ? err.message : String(err)}`);
265
+ logger_1.logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}')`, {
266
+ error: err instanceof Error ? err.message : String(err),
267
+ });
268
+ if (!this.isTerminated) {
269
+ this.scheduleReconnect();
270
+ }
259
271
  });
260
272
  }
261
273
  else {
@@ -251,10 +251,12 @@ class HyperliquidNormalizer {
251
251
  const bids = rawBids.map(level => ({
252
252
  price: parseFloat(level.px),
253
253
  size: parseFloat(level.sz),
254
+ orderCount: level.n,
254
255
  }));
255
256
  const asks = rawAsks.map(level => ({
256
257
  price: parseFloat(level.px),
257
258
  size: parseFloat(level.sz),
259
+ orderCount: level.n,
258
260
  }));
259
261
  return {
260
262
  bids: [...bids].sort((a, b) => b.price - a.price),
@@ -6,14 +6,14 @@ export interface FetcherContext {
6
6
  callApi(operationId: string, params?: Record<string, any>): Promise<any>;
7
7
  getHeaders(): Record<string, string>;
8
8
  }
9
- export interface IExchangeFetcher<TRawMarket = unknown, TRawEvent = unknown> {
9
+ export interface IExchangeFetcher<TRawMarket = unknown, TRawEvent = unknown, TRawPositions = unknown[]> {
10
10
  fetchRawMarkets(params?: MarketFilterParams): Promise<TRawMarket[]>;
11
11
  fetchRawEvents(params: EventFetchParams): Promise<TRawEvent[]>;
12
12
  fetchRawOHLCV?(id: string, params: OHLCVParams): Promise<unknown>;
13
13
  fetchRawOrderBook?(id: string): Promise<unknown>;
14
14
  fetchRawTrades?(id: string, params: TradesParams): Promise<unknown[]>;
15
15
  fetchRawMyTrades?(params: MyTradesParams, walletAddress: string): Promise<unknown[]>;
16
- fetchRawPositions?(walletAddress: string): Promise<unknown[]>;
16
+ fetchRawPositions?(walletAddress: string): Promise<TRawPositions>;
17
17
  fetchRawBalance?(walletAddress: string): Promise<unknown>;
18
18
  }
19
19
  export interface IExchangeNormalizer<TRawMarket = unknown, TRawEvent = unknown> {
@@ -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-06-04T11:40:47.252Z
3
+ * Generated at: 2026-06-08T19:06:11.820Z
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-06-04T11:40:47.252Z
6
+ * Generated at: 2026-06-08T19:06:11.820Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -14,7 +14,7 @@ exports.kalshiApiSpec = {
14
14
  },
15
15
  "servers": [
16
16
  {
17
- "url": "https://{env}.elections.kalshi.com/trade-api/v2",
17
+ "url": "https://{env}.external-api.kalshi.com/trade-api/v2",
18
18
  "variables": {
19
19
  "env": {
20
20
  "default": "api",
@@ -11,13 +11,13 @@
11
11
  * Do NOT put runtime config into api.ts — it will be overwritten.
12
12
  *
13
13
  * Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
14
- * env = "api" → production: https://api.elections.kalshi.com
15
- * env = "demo-api" → demo/paper: https://demo-api.elections.kalshi.com
14
+ * env = "api" → production: https://api.external-api.kalshi.com
15
+ * env = "demo-api" → demo/paper: https://demo-api.external-api.kalshi.com
16
16
  */
17
17
  export declare const KALSHI_PROD_API_URL: string;
18
18
  export declare const KALSHI_DEMO_API_URL: string;
19
- export declare const KALSHI_PROD_WS_URL = "wss://api.elections.kalshi.com/trade-api/ws/v2";
20
- export declare const KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.co/trade-api/ws/v2";
19
+ export declare const KALSHI_PROD_WS_URL = "wss://api.external-api.kalshi.com/trade-api/ws/v2";
20
+ export declare const KALSHI_DEMO_WS_URL = "wss://demo-api.external-api.kalshi.com/trade-api/ws/v2";
21
21
  export declare const KALSHI_PATHS: {
22
22
  TRADE_API: string;
23
23
  EVENTS: string;
@@ -39,12 +39,12 @@ export interface KalshiApiConfig {
39
39
  /**
40
40
  * Return a typed config object for the requested environment.
41
41
  *
42
- * @param demoMode - Pass `true` to target demo-api.elections.kalshi.com.
42
+ * @param demoMode - Pass `true` to target demo-api.external-api.kalshi.com.
43
43
  *
44
44
  * @example
45
45
  * ```typescript
46
46
  * const config = getKalshiConfig(true);
47
- * // config.apiUrl === "https://demo-api.elections.kalshi.com"
47
+ * // config.apiUrl === "https://demo-api.external-api.kalshi.com"
48
48
  * ```
49
49
  */
50
50
  export declare function getKalshiConfig(demoMode?: boolean, baseUrlOverride?: string): KalshiApiConfig;
@@ -12,8 +12,8 @@
12
12
  * Do NOT put runtime config into api.ts — it will be overwritten.
13
13
  *
14
14
  * Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
15
- * env = "api" → production: https://api.elections.kalshi.com
16
- * env = "demo-api" → demo/paper: https://demo-api.elections.kalshi.com
15
+ * env = "api" → production: https://api.external-api.kalshi.com
16
+ * env = "demo-api" → demo/paper: https://demo-api.external-api.kalshi.com
17
17
  */
18
18
  Object.defineProperty(exports, "__esModule", { value: true });
19
19
  exports.KALSHI_PATHS = exports.KALSHI_DEMO_WS_URL = exports.KALSHI_PROD_WS_URL = exports.KALSHI_DEMO_API_URL = exports.KALSHI_PROD_API_URL = void 0;
@@ -24,10 +24,10 @@ exports.getSeriesUrl = getSeriesUrl;
24
24
  exports.getPortfolioUrl = getPortfolioUrl;
25
25
  exports.getMarketsUrl = getMarketsUrl;
26
26
  // ── Base URL constants ────────────────────────────────────────────────────────
27
- exports.KALSHI_PROD_API_URL = process.env.KALSHI_BASE_URL || "https://api.elections.kalshi.com";
28
- exports.KALSHI_DEMO_API_URL = process.env.KALSHI_DEMO_BASE_URL || "https://demo-api.kalshi.co";
29
- exports.KALSHI_PROD_WS_URL = "wss://api.elections.kalshi.com/trade-api/ws/v2";
30
- exports.KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.co/trade-api/ws/v2";
27
+ exports.KALSHI_PROD_API_URL = process.env.KALSHI_BASE_URL || "https://api.external-api.kalshi.com";
28
+ exports.KALSHI_DEMO_API_URL = process.env.KALSHI_DEMO_BASE_URL || "https://demo-api.external-api.kalshi.com";
29
+ exports.KALSHI_PROD_WS_URL = "wss://api.external-api.kalshi.com/trade-api/ws/v2";
30
+ exports.KALSHI_DEMO_WS_URL = "wss://demo-api.external-api.kalshi.com/trade-api/ws/v2";
31
31
  // ── Path constants ────────────────────────────────────────────────────────────
32
32
  exports.KALSHI_PATHS = {
33
33
  TRADE_API: "/trade-api/v2",
@@ -42,12 +42,12 @@ exports.KALSHI_PATHS = {
42
42
  /**
43
43
  * Return a typed config object for the requested environment.
44
44
  *
45
- * @param demoMode - Pass `true` to target demo-api.elections.kalshi.com.
45
+ * @param demoMode - Pass `true` to target demo-api.external-api.kalshi.com.
46
46
  *
47
47
  * @example
48
48
  * ```typescript
49
49
  * const config = getKalshiConfig(true);
50
- * // config.apiUrl === "https://demo-api.elections.kalshi.com"
50
+ * // config.apiUrl === "https://demo-api.external-api.kalshi.com"
51
51
  * ```
52
52
  */
53
53
  function getKalshiConfig(demoMode = false, baseUrlOverride) {
@@ -13,6 +13,8 @@ export interface KalshiRawMarket {
13
13
  last_price_dollars?: string;
14
14
  yes_ask_dollars?: string;
15
15
  yes_bid_dollars?: string;
16
+ yes_ask_size_fp?: string;
17
+ yes_bid_size_fp?: string;
16
18
  rules_primary?: string;
17
19
  rules_secondary?: string;
18
20
  expiration_time: string;
@@ -65,11 +65,9 @@ class KalshiFetcher {
65
65
  }
66
66
  const status = params?.status || 'active';
67
67
  if (status === 'all') {
68
- const [openEvents, closedEvents, settledEvents] = await Promise.all([
69
- this.fetchAllWithStatus('open'),
70
- this.fetchAllWithStatus('closed'),
71
- this.fetchAllWithStatus('settled'),
72
- ]);
68
+ const openEvents = await this.fetchAllWithStatus('open');
69
+ const closedEvents = await this.fetchAllWithStatus('closed');
70
+ const settledEvents = await this.fetchAllWithStatus('settled');
73
71
  return this.enrichEventsWithSeriesList([...openEvents, ...closedEvents, ...settledEvents]);
74
72
  }
75
73
  else if (status === 'closed' || status === 'inactive') {
@@ -293,7 +291,8 @@ class KalshiFetcher {
293
291
  return this.cachedEvents;
294
292
  }
295
293
  const isSorted = params?.sort && (params.sort === 'volume' || params.sort === 'liquidity');
296
- const fetchLimit = isSorted ? 1000 : limit;
294
+ const safetyCap = BATCH_SIZE * 10;
295
+ const fetchLimit = isSorted ? Math.min(1000, safetyCap) : Math.min(limit, safetyCap);
297
296
  const [allEvents, fetchedSeriesMap] = await Promise.all([
298
297
  this.fetchActiveEvents(fetchLimit, apiStatus),
299
298
  this.fetchRawSeriesMap(),
@@ -364,7 +363,7 @@ class KalshiFetcher {
364
363
  }
365
364
  cursor = data.cursor;
366
365
  page++;
367
- if (!targetMarketCount && page >= 10)
366
+ if (page >= 10)
368
367
  break;
369
368
  }
370
369
  catch (e) {
@@ -112,7 +112,12 @@ class KalshiNormalizer {
112
112
  // market, and aren't promoted to a column — attach them here so
113
113
  // markets are queryable by series. event_ticker is omitted (already
114
114
  // promoted to eventId).
115
- sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, { series_ticker: event.series_ticker, series_title: event.series_title }),
115
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, {
116
+ yes_ask_size_fp: market.yes_ask_size_fp,
117
+ yes_bid_size_fp: market.yes_bid_size_fp,
118
+ series_ticker: event.series_ticker,
119
+ series_title: event.series_title,
120
+ }),
116
121
  };
117
122
  (0, market_utils_1.addBinaryOutcomes)(um);
118
123
  return um;
@@ -401,14 +401,16 @@ class KalshiWebSocket {
401
401
  // The resolver will be fulfilled once the connection is (re)established
402
402
  // and data arrives.
403
403
  if (!this.isConnected) {
404
- this.connect().catch(() => {
405
- // Connection failed scheduleReconnect is already queued via the
406
- // close handler, so we intentionally swallow here. The pending
407
- // resolver will be resolved when data arrives on the next connection.
404
+ this.connect().catch((err) => {
405
+ logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbook", {
406
+ error: String(err),
407
+ });
408
+ if (!this.isTerminated) {
409
+ this.scheduleReconnect();
410
+ }
408
411
  });
409
412
  }
410
413
  else {
411
- // Already connected — ensure subscription message is sent
412
414
  this.subscribeToOrderbook([ticker]);
413
415
  }
414
416
  // Return a promise that resolves on the next orderbook update
@@ -430,8 +432,13 @@ class KalshiWebSocket {
430
432
  }
431
433
  // Attempt connection — if it fails, scheduleReconnect handles recovery.
432
434
  if (!this.isConnected) {
433
- this.connect().catch(() => {
434
- // Swallow scheduleReconnect will retry. Resolvers stay pending.
435
+ this.connect().catch((err) => {
436
+ logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbooks", {
437
+ error: String(err),
438
+ });
439
+ if (!this.isTerminated) {
440
+ this.scheduleReconnect();
441
+ }
435
442
  });
436
443
  }
437
444
  else if (newTickers.length > 0) {
@@ -463,8 +470,13 @@ class KalshiWebSocket {
463
470
  this.subscribedTradeTickers.add(ticker);
464
471
  }
465
472
  if (!this.isConnected) {
466
- this.connect().catch(() => {
467
- // Swallow scheduleReconnect will retry. Resolvers stay pending.
473
+ this.connect().catch((err) => {
474
+ logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToTrades", {
475
+ error: String(err),
476
+ });
477
+ if (!this.isTerminated) {
478
+ this.scheduleReconnect();
479
+ }
468
480
  });
469
481
  }
470
482
  else {
@@ -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-06-04T11:40:47.289Z
3
+ * Generated at: 2026-06-08T19:06:11.859Z
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-06-04T11:40:47.289Z
6
+ * Generated at: 2026-06-08T19:06:11.859Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -153,7 +153,7 @@ class LimitlessClient {
153
153
  // Sign with the EOA private key.
154
154
  const orderSigner = new sdk_1.OrderSigner(wallet);
155
155
  const signature = await orderSigner.signOrder(unsignedOrder, {
156
- chainId: 8453,
156
+ chainId: config_1.LIMITLESS_CHAIN_ID,
157
157
  contractAddress: market.venue.exchange,
158
158
  });
159
159
  // Submit with ownerId (required by Limitless API).
@@ -307,7 +307,7 @@ class LimitlessClient {
307
307
  const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
308
308
  const ABI = ["function balanceOf(address) view returns (uint256)", "function decimals() view returns (uint8)"];
309
309
  const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
310
- chainId: 8453,
310
+ chainId: config_1.LIMITLESS_CHAIN_ID,
311
311
  name: 'base',
312
312
  });
313
313
  const contract = new ethers_1.Contract(USDC_ADDRESS, ABI, provider);
@@ -1 +1,3 @@
1
+ export declare const LIMITLESS_BASE_URL: string;
1
2
  export declare const LIMITLESS_RPC_URL: string;
3
+ export declare const LIMITLESS_CHAIN_ID = 8453;
@@ -1,4 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LIMITLESS_RPC_URL = void 0;
3
+ exports.LIMITLESS_CHAIN_ID = exports.LIMITLESS_RPC_URL = exports.LIMITLESS_BASE_URL = void 0;
4
+ exports.LIMITLESS_BASE_URL = process.env.LIMITLESS_BASE_URL || 'https://api.limitless.exchange';
4
5
  exports.LIMITLESS_RPC_URL = process.env.LIMITLESS_RPC_URL || 'https://mainnet.base.org';
6
+ exports.LIMITLESS_CHAIN_ID = 8453; // Base mainnet
@@ -8,6 +8,16 @@ export interface LimitlessRawMarket {
8
8
  description?: string;
9
9
  tokens?: Record<string, string>;
10
10
  prices?: number[];
11
+ tradePrices?: {
12
+ buy?: {
13
+ market?: number[];
14
+ limit?: number[];
15
+ };
16
+ sell?: {
17
+ market?: number[];
18
+ limit?: number[];
19
+ };
20
+ };
11
21
  expirationTimestamp?: string;
12
22
  volumeFormatted?: number;
13
23
  volume?: number;
@@ -60,6 +60,7 @@ class LimitlessFetcher {
60
60
  const httpClient = new HttpClient({
61
61
  baseURL: this.apiUrl,
62
62
  apiKey: this.apiKey,
63
+ timeout: 30000,
63
64
  });
64
65
  const marketFetcher = new MarketFetcher(httpClient);
65
66
  if (params?.marketId) {
@@ -196,7 +197,7 @@ class LimitlessFetcher {
196
197
  }
197
198
  async fetchRawEventBySlug(slug) {
198
199
  const { HttpClient, MarketFetcher } = await Promise.resolve().then(() => __importStar(require('@limitless-exchange/sdk')));
199
- const httpClient = new HttpClient({ baseURL: this.apiUrl });
200
+ const httpClient = new HttpClient({ baseURL: this.apiUrl, timeout: 30000 });
200
201
  const marketFetcher = new MarketFetcher(httpClient);
201
202
  const market = await marketFetcher.getMarket(slug);
202
203
  return market ? [market] : [];
@@ -501,7 +501,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
501
501
  // Static network avoids ethers v5 auto-detect (eth_chainId), which can throw
502
502
  // noNetwork / NETWORK_ERROR on flaky public RPCs (#92).
503
503
  const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
504
- chainId: 8453,
504
+ chainId: config_1.LIMITLESS_CHAIN_ID,
505
505
  name: 'base',
506
506
  });
507
507
  // Get USDC contract address for Base
@@ -63,9 +63,9 @@ function mapMarketToUnified(market, context = {}) {
63
63
  if (!market.tokens.yes || !market.tokens.no) {
64
64
  throw new Error(`[limitless] Market "${market.slug}" is missing token addresses`);
65
65
  }
66
- const prices = Array.isArray(market.prices) ? market.prices : [];
67
- const yesPrice = prices[0] || 0;
68
- const noPrice = prices[1] || 0;
66
+ const legacyPrices = Array.isArray(market.prices) ? market.prices : [];
67
+ const yesPrice = market.tradePrices?.buy?.market?.[0] ?? legacyPrices[0] ?? 0.5;
68
+ const noPrice = market.tradePrices?.sell?.market?.[0] ?? legacyPrices[1] ?? Math.max(0, Math.min(1, 1 - yesPrice));
69
69
  const yesLabel = hasParentContext ? rawTitle : 'Yes';
70
70
  const noLabel = hasParentContext ? `Not ${rawTitle}` : 'No';
71
71
  outcomes.push({
@@ -73,8 +73,8 @@ class LimitlessWebSocket {
73
73
  if (!this.client.isConnected()) {
74
74
  await this.client.connect();
75
75
  }
76
- // Subscribe to market
77
- await this.client.subscribe('orderbook', { marketSlugs: [marketSlug] });
76
+ // Subscribe to market orderbook updates through the SDK market-price channel.
77
+ await this.client.subscribe('subscribe_market_prices', { marketSlugs: [marketSlug] });
78
78
  if (callback) {
79
79
  this.orderbookCallbacks.set(marketSlug, callback);
80
80
  }
@@ -170,7 +170,7 @@ class LimitlessWebSocket {
170
170
  await this.client.connect();
171
171
  }
172
172
  // Subscribe to market prices
173
- await this.client.subscribe('prices', { marketAddresses: [marketAddress] });
173
+ await this.client.subscribe('subscribe_market_prices', { marketAddresses: [marketAddress] });
174
174
  this.priceCallbacks.set(marketAddress, callback);
175
175
  }
176
176
  /**
@@ -184,11 +184,7 @@ class LimitlessWebSocket {
184
184
  if (!this.client.isConnected()) {
185
185
  await this.client.connect();
186
186
  }
187
- await this.client.subscribe('orders'); // SDK uses 'orders' channel for user positional updates too?
188
- // Actually, the channel type has 'subscribe_positions'. Let's check.
189
- // Wait, I saw 'orders' in SubscriptionChannel.
190
- // Let's use 'orders' as it's common for user data.
191
- // Wait, I saw 'subscribe_positions' in the type.
187
+ await this.client.subscribe('subscribe_order_events');
192
188
  await this.client.subscribe('subscribe_positions');
193
189
  this.client.on('positions', callback);
194
190
  }
@@ -220,11 +216,12 @@ class LimitlessWebSocket {
220
216
  async unsubscribe(marketSlugOrAddress) {
221
217
  this.orderbookCallbacks.delete(marketSlugOrAddress);
222
218
  this.priceCallbacks.delete(marketSlugOrAddress);
223
- // Unsubscribe from SDK
224
- await this.client.unsubscribe('orderbook', {
219
+ // The current SDK only exposes explicit unsubscribe lifecycle channels;
220
+ // cast market-price unsubscribe channels until the SDK publishes them.
221
+ await this.client.unsubscribe('subscribe_market_prices', {
225
222
  marketSlugs: [marketSlugOrAddress],
226
223
  });
227
- await this.client.unsubscribe('prices', {
224
+ await this.client.unsubscribe('subscribe_market_prices', {
228
225
  marketAddresses: [marketSlugOrAddress],
229
226
  });
230
227
  }
@@ -135,7 +135,6 @@ function buildOutcomes(question, postId, medianProb) {
135
135
  aggregations: latest,
136
136
  resolution: question?.resolution ?? null,
137
137
  scaling: question?.scaling ?? null,
138
- possibilities: question?.possibilities ?? null,
139
138
  };
140
139
  // Multiple choice: one outcome per option, each independently forecastable
141
140
  if (type === "multiple_choice") {
@@ -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-06-04T11:40:47.300Z
3
+ * Generated at: 2026-06-08T19:06:11.872Z
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-06-04T11:40:47.300Z
6
+ * Generated at: 2026-06-08T19:06:11.872Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.myriadApiSpec = {
@@ -166,10 +166,18 @@ exports.myriadApiSpec = {
166
166
  "enum": [
167
167
  "open",
168
168
  "closed",
169
- "resolved"
169
+ "resolved",
170
+ "voided"
170
171
  ]
171
172
  }
172
173
  },
174
+ {
175
+ "name": "trading_model",
176
+ "in": "query",
177
+ "schema": {
178
+ "type": "string"
179
+ }
180
+ },
173
181
  {
174
182
  "name": "token_address",
175
183
  "in": "query",
@@ -655,6 +663,13 @@ exports.myriadApiSpec = {
655
663
  "type": "string"
656
664
  }
657
665
  },
666
+ {
667
+ "name": "trading_model",
668
+ "in": "query",
669
+ "schema": {
670
+ "type": "string"
671
+ }
672
+ },
658
673
  {
659
674
  "name": "token_address",
660
675
  "in": "query",
@@ -15,6 +15,12 @@ const MYRIAD_PROMOTED_MARKET_KEYS = [
15
15
  const MYRIAD_PROMOTED_EVENT_KEYS = [
16
16
  'id', 'title', 'markets',
17
17
  ];
18
+ const WEI_SCALE = BigInt('1000000000000000000');
19
+ const PRECISION_SCALE = BigInt('1000000000000');
20
+ function weiStringToNumber(value) {
21
+ const scaled = (BigInt(value) * PRECISION_SCALE) / WEI_SCALE;
22
+ return Number(scaled) / Number(PRECISION_SCALE);
23
+ }
18
24
  function selectTimeframe(interval) {
19
25
  switch (interval) {
20
26
  case '1m':
@@ -183,15 +189,14 @@ class MyriadNormalizer {
183
189
  };
184
190
  }
185
191
  normalizeClobOrderBook(raw) {
186
- const WEI = 1e18;
187
192
  return {
188
193
  bids: raw.bids.map(([priceWei, sizeWei]) => ({
189
- price: Number(priceWei) / WEI,
190
- size: Number(sizeWei) / WEI,
194
+ price: weiStringToNumber(priceWei),
195
+ size: weiStringToNumber(sizeWei),
191
196
  })),
192
197
  asks: raw.asks.map(([priceWei, sizeWei]) => ({
193
- price: Number(priceWei) / WEI,
194
- size: Number(sizeWei) / WEI,
198
+ price: weiStringToNumber(priceWei),
199
+ size: weiStringToNumber(sizeWei),
195
200
  })),
196
201
  timestamp: Date.now(),
197
202
  };
@@ -47,6 +47,8 @@ function mapMarketState(state) {
47
47
  return 'inactive';
48
48
  case 'resolved':
49
49
  return 'closed';
50
+ case 'voided':
51
+ return 'closed';
50
52
  default:
51
53
  return 'active';
52
54
  }
@@ -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-06-04T11:40:47.305Z
3
+ * Generated at: 2026-06-08T19:06:11.875Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const opinionApiSpec: {
@@ -113,6 +113,21 @@ export declare const opinionApiSpec: {
113
113
  }[];
114
114
  };
115
115
  };
116
+ "/market/slug/{slug}": {
117
+ get: {
118
+ tags: string[];
119
+ summary: string;
120
+ operationId: string;
121
+ parameters: {
122
+ name: string;
123
+ in: string;
124
+ required: boolean;
125
+ schema: {
126
+ type: string;
127
+ };
128
+ }[];
129
+ };
130
+ };
116
131
  "/market/categorical/{marketId}": {
117
132
  get: {
118
133
  tags: string[];