pmxt-core 2.44.4 → 2.44.6

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 (38) hide show
  1. package/dist/errors.d.ts +6 -0
  2. package/dist/errors.js +10 -1
  3. package/dist/exchanges/kalshi/api.d.ts +1 -1
  4. package/dist/exchanges/kalshi/api.js +1 -1
  5. package/dist/exchanges/kalshi/fetcher.d.ts +11 -1
  6. package/dist/exchanges/kalshi/fetcher.js +49 -17
  7. package/dist/exchanges/kalshi/normalizer.d.ts +12 -0
  8. package/dist/exchanges/kalshi/normalizer.js +125 -1
  9. package/dist/exchanges/limitless/api.d.ts +1 -1
  10. package/dist/exchanges/limitless/api.js +1 -1
  11. package/dist/exchanges/mock/index.d.ts +3 -2
  12. package/dist/exchanges/mock/index.js +14 -5
  13. package/dist/exchanges/myriad/api.d.ts +1 -1
  14. package/dist/exchanges/myriad/api.js +1 -1
  15. package/dist/exchanges/opinion/api.d.ts +1 -1
  16. package/dist/exchanges/opinion/api.js +1 -1
  17. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  18. package/dist/exchanges/polymarket/api-clob.js +1 -1
  19. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  20. package/dist/exchanges/polymarket/api-data.js +1 -1
  21. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  22. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  23. package/dist/exchanges/probable/api.d.ts +1 -1
  24. package/dist/exchanges/probable/api.js +1 -1
  25. package/dist/feeds/binance/binance-feed.d.ts +9 -0
  26. package/dist/feeds/binance/binance-feed.js +34 -7
  27. package/dist/feeds/chainlink/chainlink-feed.d.ts +14 -0
  28. package/dist/feeds/chainlink/chainlink-feed.js +62 -7
  29. package/dist/feeds/interfaces.d.ts +10 -0
  30. package/dist/router/Router.d.ts +9 -0
  31. package/dist/router/Router.js +153 -2
  32. package/dist/router/types.d.ts +5 -0
  33. package/dist/server/app.d.ts +26 -2
  34. package/dist/server/app.js +50 -9
  35. package/dist/server/feed-routes.js +34 -12
  36. package/dist/server/sql-route.d.ts +2 -0
  37. package/dist/server/sql-route.js +277 -0
  38. package/package.json +3 -3
package/dist/errors.d.ts CHANGED
@@ -84,6 +84,12 @@ export declare class ValidationError extends BaseError {
84
84
  readonly field?: string;
85
85
  constructor(message: string, field?: string, exchange?: string);
86
86
  }
87
+ /**
88
+ * 501 Not Implemented - The requested operation is not supported
89
+ */
90
+ export declare class NotSupported extends BaseError {
91
+ constructor(message: string, exchange?: string);
92
+ }
87
93
  /**
88
94
  * 503 Service Unavailable - Network connectivity issues (retryable)
89
95
  */
package/dist/errors.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ExchangeNotAvailable = exports.NetworkError = exports.ValidationError = exports.InsufficientFunds = exports.InvalidOrder = exports.RateLimitExceeded = exports.EventNotFound = exports.MarketNotFound = exports.OrderNotFound = exports.NotFound = exports.PermissionDenied = exports.AuthenticationError = exports.BadRequest = exports.BaseError = void 0;
3
+ exports.ExchangeNotAvailable = exports.NetworkError = exports.NotSupported = exports.ValidationError = exports.InsufficientFunds = exports.InvalidOrder = exports.RateLimitExceeded = exports.EventNotFound = exports.MarketNotFound = exports.OrderNotFound = exports.NotFound = exports.PermissionDenied = exports.AuthenticationError = exports.BadRequest = exports.BaseError = void 0;
4
4
  /**
5
5
  * Base error class for all PMXT errors
6
6
  *
@@ -140,6 +140,15 @@ exports.ValidationError = ValidationError;
140
140
  // ============================================================================
141
141
  // 5xx Server/Network Errors
142
142
  // ============================================================================
143
+ /**
144
+ * 501 Not Implemented - The requested operation is not supported
145
+ */
146
+ class NotSupported extends BaseError {
147
+ constructor(message, exchange) {
148
+ super(message, 501, 'NOT_SUPPORTED', false, exchange);
149
+ }
150
+ }
151
+ exports.NotSupported = NotSupported;
143
152
  /**
144
153
  * 503 Service Unavailable - Network connectivity issues (retryable)
145
154
  */
@@ -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-24T20:52:41.818Z
3
+ * Generated at: 2026-05-25T13:34:08.496Z
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-24T20:52:41.818Z
6
+ * Generated at: 2026-05-25T13:34:08.496Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -30,13 +30,20 @@ export interface KalshiRawMarket {
30
30
  export interface KalshiRawEvent {
31
31
  event_ticker: string;
32
32
  title: string;
33
+ sub_title?: string;
33
34
  image_url?: string;
34
35
  category?: string;
35
36
  tags?: string[];
36
37
  series_ticker?: string;
38
+ series_title?: string;
39
+ mutually_exclusive?: boolean;
37
40
  markets?: KalshiRawMarket[];
38
41
  [key: string]: unknown;
39
42
  }
43
+ interface KalshiSeriesInfo {
44
+ title?: string;
45
+ tags?: string[];
46
+ }
40
47
  export interface KalshiRawEventPage {
41
48
  events: KalshiRawEvent[];
42
49
  cursor?: string | null;
@@ -146,10 +153,13 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
146
153
  }>;
147
154
  fetchRawOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
148
155
  fetchRawHistoricalOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
149
- fetchRawSeriesMap(): Promise<Map<string, string[]>>;
156
+ fetchRawSeriesMap(): Promise<Map<string, KalshiSeriesInfo>>;
150
157
  fetchRawEventByTicker(eventTicker: string): Promise<KalshiRawEvent[]>;
151
158
  private fetchRawEventsDefault;
159
+ private enrichEventsWithSeriesList;
160
+ private enrichEventsWithSeriesMap;
152
161
  private fetchActiveEvents;
153
162
  private fetchAllWithStatus;
154
163
  private fetchPageWithStatus;
155
164
  }
165
+ export {};
@@ -65,16 +65,16 @@ class KalshiFetcher {
65
65
  this.fetchAllWithStatus('closed'),
66
66
  this.fetchAllWithStatus('settled'),
67
67
  ]);
68
- return [...openEvents, ...closedEvents, ...settledEvents];
68
+ return this.enrichEventsWithSeriesList([...openEvents, ...closedEvents, ...settledEvents]);
69
69
  }
70
70
  else if (status === 'closed' || status === 'inactive') {
71
71
  const [closedEvents, settledEvents] = await Promise.all([
72
72
  this.fetchAllWithStatus('closed'),
73
73
  this.fetchAllWithStatus('settled'),
74
74
  ]);
75
- return [...closedEvents, ...settledEvents];
75
+ return this.enrichEventsWithSeriesList([...closedEvents, ...settledEvents]);
76
76
  }
77
- return this.fetchAllWithStatus('open');
77
+ return this.enrichEventsWithSeriesList(await this.fetchAllWithStatus('open'));
78
78
  }
79
79
  catch (error) {
80
80
  throw errors_1.kalshiErrorMapper.mapError(error);
@@ -92,7 +92,9 @@ class KalshiFetcher {
92
92
  if (status === 'settled')
93
93
  apiStatus = 'settled';
94
94
  const limit = Math.max(1, Math.floor(params.limit || BATCH_SIZE));
95
- return this.fetchPageWithStatus(apiStatus, limit, params.cursor);
95
+ const page = await this.fetchPageWithStatus(apiStatus, limit, params.cursor);
96
+ await this.enrichEventsWithSeriesList(page.events);
97
+ return page;
96
98
  }
97
99
  catch (error) {
98
100
  throw errors_1.kalshiErrorMapper.mapError(error);
@@ -217,8 +219,11 @@ class KalshiFetcher {
217
219
  const seriesList = data.series || [];
218
220
  const map = new Map();
219
221
  for (const series of seriesList) {
220
- if (series.tags && series.tags.length > 0) {
221
- map.set(series.ticker, series.tags);
222
+ if (series.ticker) {
223
+ map.set(series.ticker, {
224
+ title: typeof series.title === 'string' ? series.title : undefined,
225
+ tags: Array.isArray(series.tags) ? series.tags : undefined,
226
+ });
222
227
  }
223
228
  }
224
229
  return map;
@@ -237,20 +242,24 @@ class KalshiFetcher {
237
242
  const event = data.event;
238
243
  if (!event)
239
244
  return [];
240
- // Enrich with series tags
245
+ // Enrich with series metadata. The series title helps the normalizer
246
+ // avoid polluted event titles on multi-market futures.
241
247
  if (event.series_ticker) {
242
248
  try {
243
249
  const seriesData = await this.ctx.callApi('GetSeries', {
244
250
  series_ticker: event.series_ticker,
245
251
  });
246
252
  const series = seriesData.series;
253
+ if (typeof series?.title === 'string' && series.title.trim()) {
254
+ event.series_title = series.title.trim();
255
+ }
247
256
  if (series?.tags?.length > 0 && (!event.tags || event.tags.length === 0)) {
248
257
  event.tags = series.tags;
249
258
  }
250
259
  }
251
260
  catch (err) {
252
- // Non-critical — tags are enrichment only.
253
- logger_1.logger.warn('kalshi: series tag fetch failed', {
261
+ // Non-critical — series metadata is enrichment only.
262
+ logger_1.logger.warn('kalshi: series metadata fetch failed', {
254
263
  series_ticker: event.series_ticker,
255
264
  error: err instanceof Error ? err.message : String(err),
256
265
  });
@@ -275,14 +284,7 @@ class KalshiFetcher {
275
284
  this.fetchActiveEvents(fetchLimit, apiStatus),
276
285
  this.fetchRawSeriesMap(),
277
286
  ]);
278
- // Enrich events with series tags
279
- for (const event of allEvents) {
280
- if (event.series_ticker && fetchedSeriesMap.has(event.series_ticker)) {
281
- if (!event.tags || event.tags.length === 0) {
282
- event.tags = fetchedSeriesMap.get(event.series_ticker);
283
- }
284
- }
285
- }
287
+ this.enrichEventsWithSeriesMap(allEvents, fetchedSeriesMap);
286
288
  if (fetchLimit >= 1000 && useCache) {
287
289
  this.cachedEvents = allEvents;
288
290
  this.cachedSeriesMap = fetchedSeriesMap;
@@ -290,6 +292,36 @@ class KalshiFetcher {
290
292
  }
291
293
  return allEvents;
292
294
  }
295
+ async enrichEventsWithSeriesList(events) {
296
+ if (events.length === 0)
297
+ return events;
298
+ try {
299
+ const seriesMap = await this.fetchRawSeriesMap();
300
+ this.enrichEventsWithSeriesMap(events, seriesMap);
301
+ }
302
+ catch (err) {
303
+ // Non-critical — callers can still normalize the venue-native title.
304
+ logger_1.logger.warn('kalshi: series list enrichment failed', {
305
+ error: err instanceof Error ? err.message : String(err),
306
+ });
307
+ }
308
+ return events;
309
+ }
310
+ enrichEventsWithSeriesMap(events, seriesMap) {
311
+ for (const event of events) {
312
+ if (!event.series_ticker)
313
+ continue;
314
+ const seriesInfo = seriesMap.get(event.series_ticker);
315
+ if (!seriesInfo)
316
+ continue;
317
+ if (seriesInfo.title) {
318
+ event.series_title = seriesInfo.title;
319
+ }
320
+ if (seriesInfo.tags?.length && (!event.tags || event.tags.length === 0)) {
321
+ event.tags = seriesInfo.tags;
322
+ }
323
+ }
324
+ }
293
325
  async fetchActiveEvents(targetMarketCount, status = 'open') {
294
326
  let allEvents = [];
295
327
  let totalMarketCount = 0;
@@ -21,6 +21,18 @@ export declare class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEv
21
21
  }): Balance[];
22
22
  private mapOrderStatus;
23
23
  private deriveEventDescription;
24
+ private deriveEventTitle;
25
+ private shouldUseSeriesTitle;
26
+ private deriveCommonEventTitle;
27
+ private extractEventTitleFromMarketTitle;
28
+ private composeSeriesTitle;
29
+ private ensureWinnerTitle;
30
+ private hasResolutionTerm;
31
+ private extractEventTitlePrefix;
32
+ private extractResolutionTerm;
33
+ private hasWinVerb;
34
+ private normalizeTitleText;
35
+ private escapeRegExp;
24
36
  private deriveOutcomeLabel;
25
37
  private cleanLabel;
26
38
  private templateRule;
@@ -102,7 +102,7 @@ class KalshiNormalizer {
102
102
  const markets = this.normalizeMarketsFromEvent(raw);
103
103
  return {
104
104
  id: raw.event_ticker,
105
- title: raw.title,
105
+ title: this.deriveEventTitle(raw),
106
106
  description: this.deriveEventDescription(raw.markets || []),
107
107
  slug: raw.event_ticker,
108
108
  markets,
@@ -299,6 +299,130 @@ class KalshiNormalizer {
299
299
  }
300
300
  return texts[0];
301
301
  }
302
+ deriveEventTitle(event) {
303
+ const rawTitle = this.cleanLabel(event.title) || event.event_ticker;
304
+ const seriesTitle = this.cleanLabel(event.series_title);
305
+ const markets = event.markets || [];
306
+ if (!seriesTitle || !this.shouldUseSeriesTitle(event, markets)) {
307
+ return rawTitle;
308
+ }
309
+ return this.composeSeriesTitle(seriesTitle, this.deriveCommonEventTitle(event, markets));
310
+ }
311
+ shouldUseSeriesTitle(event, markets) {
312
+ if (event.mutually_exclusive !== true)
313
+ return false;
314
+ if (markets.length < 4)
315
+ return false;
316
+ const rawTitle = this.cleanLabel(event.title);
317
+ if (!rawTitle)
318
+ return false;
319
+ const titleLooksScoped = /(?:\bvs\.?\b|\bversus\b|:)/i.test(rawTitle);
320
+ if (!titleLooksScoped)
321
+ return false;
322
+ const candidateLabels = markets
323
+ .map((market) => this.deriveOutcomeLabel(market))
324
+ .filter((label) => label != null && label.length >= 3);
325
+ if (candidateLabels.length < 4)
326
+ return false;
327
+ const normalizedTitle = this.normalizeTitleText(rawTitle);
328
+ const containedLabels = new Set();
329
+ for (const label of candidateLabels) {
330
+ const normalizedLabel = this.normalizeTitleText(label);
331
+ if (normalizedLabel && normalizedTitle.includes(normalizedLabel)) {
332
+ containedLabels.add(normalizedLabel);
333
+ }
334
+ }
335
+ return containedLabels.size >= 2;
336
+ }
337
+ deriveCommonEventTitle(event, markets) {
338
+ const eventTitlePrefix = this.extractEventTitlePrefix(event.title);
339
+ if (eventTitlePrefix) {
340
+ if (this.hasWinVerb(eventTitlePrefix))
341
+ return 'Winner';
342
+ if (this.hasResolutionTerm(eventTitlePrefix))
343
+ return eventTitlePrefix;
344
+ }
345
+ const candidates = new Map();
346
+ for (const market of markets) {
347
+ const marketTitle = this.cleanLabel(market.title);
348
+ if (!marketTitle)
349
+ continue;
350
+ const outcomeLabel = this.deriveOutcomeLabel(market);
351
+ const candidate = this.extractEventTitleFromMarketTitle(marketTitle, outcomeLabel);
352
+ if (!candidate)
353
+ continue;
354
+ candidates.set(candidate, (candidates.get(candidate) ?? 0) + 1);
355
+ }
356
+ let best = null;
357
+ let bestCount = 0;
358
+ for (const [candidate, count] of candidates.entries()) {
359
+ if (count > bestCount) {
360
+ best = candidate;
361
+ bestCount = count;
362
+ }
363
+ }
364
+ return best;
365
+ }
366
+ extractEventTitleFromMarketTitle(title, outcomeLabel) {
367
+ const escapedOutcome = outcomeLabel ? this.escapeRegExp(outcomeLabel) : '[^?]+?';
368
+ const winPattern = new RegExp(`^Will (?:the )?${escapedOutcome} win (?:the )?(.+?)\\??$`, 'i');
369
+ const winMatch = title.match(winPattern);
370
+ if (winMatch?.[1]) {
371
+ return this.ensureWinnerTitle(winMatch[1].trim());
372
+ }
373
+ const plainWinnerMatch = title.match(/^(.+? Winner)\??$/i);
374
+ if (plainWinnerMatch?.[1])
375
+ return plainWinnerMatch[1].trim();
376
+ const championMatch = title.match(/^(.+? Champion(?:ship)?)\??$/i);
377
+ if (championMatch?.[1])
378
+ return championMatch[1].trim();
379
+ return null;
380
+ }
381
+ composeSeriesTitle(seriesTitle, commonTitle) {
382
+ if (!commonTitle)
383
+ return seriesTitle;
384
+ let title = seriesTitle;
385
+ const year = commonTitle.match(/^\s*(20\d{2})\b/)?.[1];
386
+ if (year && !new RegExp(`\\b${year}\\b`).test(title)) {
387
+ title = `${year} ${title}`;
388
+ }
389
+ if (this.hasResolutionTerm(title)) {
390
+ return title;
391
+ }
392
+ const resolutionTerm = this.extractResolutionTerm(commonTitle);
393
+ if (resolutionTerm) {
394
+ return `${title} ${resolutionTerm}`;
395
+ }
396
+ return title;
397
+ }
398
+ ensureWinnerTitle(title) {
399
+ if (this.hasResolutionTerm(title))
400
+ return title;
401
+ return `${title} Winner`;
402
+ }
403
+ hasResolutionTerm(title) {
404
+ return /\b(winner|champion|championship|nominee|nomination|election|finals?|cup|award)\b/i.test(title);
405
+ }
406
+ extractEventTitlePrefix(title) {
407
+ const match = title.match(/^(.+?)(?:\s*:\s*|\s+[-\u2013\u2014]\s+)(.+)$/u);
408
+ const prefix = match?.[1]?.trim();
409
+ if (prefix && this.normalizeTitleText(prefix) === 'series winner')
410
+ return null;
411
+ return prefix || null;
412
+ }
413
+ extractResolutionTerm(title) {
414
+ const match = title.match(/\b(Winner|Champion|Championship|Nominee|Nomination|Election|Finals?|Cup|Award)\b\s*$/i);
415
+ return match?.[1] || null;
416
+ }
417
+ hasWinVerb(title) {
418
+ return /\bwin(?:s|ning)?\b/i.test(title);
419
+ }
420
+ normalizeTitleText(value) {
421
+ return value.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, ' ').trim();
422
+ }
423
+ escapeRegExp(value) {
424
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
425
+ }
302
426
  deriveOutcomeLabel(market) {
303
427
  const yesSubtitle = this.cleanLabel(market.yes_sub_title);
304
428
  if (yesSubtitle)
@@ -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-24T20:52:41.861Z
3
+ * Generated at: 2026-05-25T13:34:08.535Z
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-24T20:52:41.861Z
6
+ * Generated at: 2026-05-25T13:34:08.535Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -28,9 +28,10 @@ export declare class MockExchange extends PredictionMarketExchange {
28
28
  private _buildEvents;
29
29
  protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
30
30
  protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
31
- fetchOrderBook(id: string, _limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
31
+ fetchOrderBook(id: string, limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
32
+ fetchOrderBooks(outcomeIds: string[]): Promise<Record<string, OrderBook>>;
32
33
  fetchOHLCV(id: string, params: OHLCVParams): Promise<PriceCandle[]>;
33
- fetchTrades(id: string, _params: TradesParams): Promise<Trade[]>;
34
+ fetchTrades(id: string, params?: TradesParams): Promise<Trade[]>;
34
35
  fetchBalance(_address?: string): Promise<Balance[]>;
35
36
  fetchPositions(_address?: string): Promise<Position[]>;
36
37
  private _nextOrderId;
@@ -289,12 +289,13 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
289
289
  const limit = params?.limit;
290
290
  return limit !== undefined ? events.slice(offset, offset + limit) : events.slice(offset);
291
291
  }
292
- async fetchOrderBook(id, _limit, _params) {
292
+ async fetchOrderBook(id, limit, _params) {
293
293
  const resolved = await this.resolveOutcomeAlias(id, _params);
294
294
  id = resolved.outcomeId;
295
295
  const f = new seededRng_1.SeededRng(id);
296
296
  const midPrice = round(f.float(0.1, 0.9), 3);
297
297
  const spread = round(f.float(0.005, 0.03), 3);
298
+ const depth = limit === undefined ? 8 : Math.max(0, Math.floor(limit));
298
299
  const buildLevels = (startPrice, direction, count) => {
299
300
  const levels = [];
300
301
  let price = startPrice;
@@ -308,11 +309,18 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
308
309
  const askStart = clamp(round(midPrice + spread / 2, 3), 0.01, 0.99);
309
310
  const bidStart = clamp(round(midPrice - spread / 2, 3), 0.01, 0.99);
310
311
  return {
311
- bids: buildLevels(bidStart, -1, 8).sort((a, b) => b.price - a.price),
312
- asks: buildLevels(askStart, 1, 8).sort((a, b) => a.price - b.price),
312
+ bids: buildLevels(bidStart, -1, depth).sort((a, b) => b.price - a.price),
313
+ asks: buildLevels(askStart, 1, depth).sort((a, b) => a.price - b.price),
313
314
  timestamp: Date.now(),
314
315
  };
315
316
  }
317
+ async fetchOrderBooks(outcomeIds) {
318
+ const books = {};
319
+ for (const outcomeId of outcomeIds) {
320
+ books[outcomeId] = await this.fetchOrderBook(outcomeId);
321
+ }
322
+ return books;
323
+ }
316
324
  async fetchOHLCV(id, params) {
317
325
  const f = new seededRng_1.SeededRng(id);
318
326
  const resolutionMs = {
@@ -343,7 +351,7 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
343
351
  }
344
352
  return candles;
345
353
  }
346
- async fetchTrades(id, _params) {
354
+ async fetchTrades(id, params = {}) {
347
355
  const f = new seededRng_1.SeededRng(id);
348
356
  const count = f.int(5, 30);
349
357
  const trades = [];
@@ -359,7 +367,8 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
359
367
  outcomeId: id,
360
368
  });
361
369
  }
362
- return trades.sort((a, b) => b.timestamp - a.timestamp);
370
+ const sorted = trades.sort((a, b) => b.timestamp - a.timestamp);
371
+ return params.limit === undefined ? sorted : sorted.slice(0, Math.max(0, Math.floor(params.limit)));
363
372
  }
364
373
  async fetchBalance(_address) {
365
374
  const locked = this._locked();
@@ -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-24T20:52:41.872Z
3
+ * Generated at: 2026-05-25T13:34:08.546Z
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-24T20:52:41.872Z
6
+ * Generated at: 2026-05-25T13:34:08.546Z
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-24T20:52:41.878Z
3
+ * Generated at: 2026-05-25T13:34:08.549Z
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-24T20:52:41.878Z
6
+ * Generated at: 2026-05-25T13:34:08.549Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -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-24T20:52:41.826Z
3
+ * Generated at: 2026-05-25T13:34:08.504Z
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-24T20:52:41.826Z
6
+ * Generated at: 2026-05-25T13:34:08.504Z
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-24T20:52:41.843Z
3
+ * Generated at: 2026-05-25T13:34:08.517Z
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-24T20:52:41.843Z
6
+ * Generated at: 2026-05-25T13:34:08.517Z
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-24T20:52:41.840Z
3
+ * Generated at: 2026-05-25T13:34:08.515Z
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-24T20:52:41.840Z
6
+ * Generated at: 2026-05-25T13:34:08.515Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -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-24T20:52:41.866Z
3
+ * Generated at: 2026-05-25T13:34:08.541Z
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-24T20:52:41.866Z
6
+ * Generated at: 2026-05-25T13:34:08.541Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -4,6 +4,14 @@ import { BinanceFeedConfig } from './types';
4
4
  export declare class BinanceFeed extends BaseDataFeed {
5
5
  readonly name = "binance";
6
6
  readonly description = "Binance spot trade firehose via obdata relay";
7
+ readonly has: {
8
+ readonly loadMarkets: true;
9
+ readonly fetchTicker: true;
10
+ readonly fetchTickers: true;
11
+ readonly watchTicker: true;
12
+ readonly fetchOHLCV: false;
13
+ readonly fetchOrderBook: false;
14
+ };
7
15
  private readonly wsUrl;
8
16
  private readonly apiKey;
9
17
  private readonly reconnectIntervalMs;
@@ -26,4 +34,5 @@ export declare class BinanceFeed extends BaseDataFeed {
26
34
  private establishConnection;
27
35
  private handleMessage;
28
36
  private scheduleReconnect;
37
+ private validateRelayWsUrl;
29
38
  }