pmxt-core 2.47.0 → 2.48.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 (67) hide show
  1. package/dist/BaseExchange.d.ts +42 -1
  2. package/dist/BaseExchange.js +29 -2
  3. package/dist/exchanges/baozi/index.d.ts +1 -0
  4. package/dist/exchanges/baozi/index.js +5 -0
  5. package/dist/exchanges/gemini-titan/index.d.ts +6 -2
  6. package/dist/exchanges/gemini-titan/index.js +81 -1
  7. package/dist/exchanges/gemini-titan/normalizer.d.ts +7 -1
  8. package/dist/exchanges/gemini-titan/normalizer.js +23 -0
  9. package/dist/exchanges/hyperliquid/index.d.ts +3 -0
  10. package/dist/exchanges/hyperliquid/index.js +7 -0
  11. package/dist/exchanges/kalshi/api.d.ts +1 -1
  12. package/dist/exchanges/kalshi/api.js +1 -1
  13. package/dist/exchanges/kalshi/fetcher.d.ts +10 -0
  14. package/dist/exchanges/kalshi/fetcher.js +36 -0
  15. package/dist/exchanges/kalshi/index.d.ts +3 -2
  16. package/dist/exchanges/kalshi/index.js +33 -0
  17. package/dist/exchanges/kalshi/normalizer.d.ts +3 -2
  18. package/dist/exchanges/kalshi/normalizer.js +14 -0
  19. package/dist/exchanges/limitless/api.d.ts +1 -1
  20. package/dist/exchanges/limitless/api.js +1 -1
  21. package/dist/exchanges/limitless/index.d.ts +1 -0
  22. package/dist/exchanges/limitless/index.js +5 -0
  23. package/dist/exchanges/metaculus/fetchEvents.js +4 -0
  24. package/dist/exchanges/metaculus/index.d.ts +3 -0
  25. package/dist/exchanges/metaculus/index.js +3 -0
  26. package/dist/exchanges/myriad/api.d.ts +1 -1
  27. package/dist/exchanges/myriad/api.js +1 -1
  28. package/dist/exchanges/myriad/index.d.ts +1 -0
  29. package/dist/exchanges/myriad/index.js +5 -0
  30. package/dist/exchanges/opinion/api.d.ts +1 -1
  31. package/dist/exchanges/opinion/api.js +1 -1
  32. package/dist/exchanges/opinion/index.d.ts +6 -2
  33. package/dist/exchanges/opinion/index.js +54 -1
  34. package/dist/exchanges/opinion/normalizer.d.ts +8 -2
  35. package/dist/exchanges/opinion/normalizer.js +16 -0
  36. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  37. package/dist/exchanges/polymarket/api-clob.js +1 -1
  38. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  39. package/dist/exchanges/polymarket/api-data.js +1 -1
  40. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  41. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  42. package/dist/exchanges/polymarket/index.d.ts +13 -2
  43. package/dist/exchanges/polymarket/index.js +49 -0
  44. package/dist/exchanges/polymarket/normalizer.d.ts +2 -1
  45. package/dist/exchanges/polymarket/normalizer.js +33 -0
  46. package/dist/exchanges/polymarket_us/index.d.ts +3 -2
  47. package/dist/exchanges/polymarket_us/index.js +57 -0
  48. package/dist/exchanges/polymarket_us/normalizer.d.ts +9 -2
  49. package/dist/exchanges/polymarket_us/normalizer.js +27 -0
  50. package/dist/exchanges/probable/api.d.ts +1 -1
  51. package/dist/exchanges/probable/api.js +1 -1
  52. package/dist/exchanges/probable/index.d.ts +3 -0
  53. package/dist/exchanges/probable/index.js +7 -0
  54. package/dist/exchanges/smarkets/index.d.ts +1 -0
  55. package/dist/exchanges/smarkets/index.js +5 -0
  56. package/dist/exchanges/suibets/index.d.ts +1 -0
  57. package/dist/exchanges/suibets/index.js +5 -0
  58. package/dist/router/Router.d.ts +29 -2
  59. package/dist/router/Router.js +145 -0
  60. package/dist/router/index.d.ts +1 -0
  61. package/dist/router/index.js +1 -0
  62. package/dist/router/series-map.d.ts +32 -0
  63. package/dist/router/series-map.js +146 -0
  64. package/dist/server/method-verbs.json +10 -0
  65. package/dist/server/openapi.yaml +118 -0
  66. package/dist/types.d.ts +31 -0
  67. package/package.json +3 -3
@@ -1,6 +1,6 @@
1
1
  import { AxiosInstance } from 'axios';
2
2
  import { SubscribedAddressSnapshot, SubscriptionOption } from './subscriber/base';
3
- import { Balance, BuiltOrder, CandleInterval, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from './types';
3
+ import { Balance, BuiltOrder, CandleInterval, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UnifiedSeries, UserTrade } from './types';
4
4
  import { ExecutionPriceResult } from './utils/math';
5
5
  import type { FetchMarketMatchesParams, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams, FetchMatchedMarketsParams, FetchMatchedPricesParams, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity, MatchedMarketPair, MatchedPricePair } from './router/types';
6
6
  export interface ApiEndpoint {
@@ -74,6 +74,8 @@ export interface EventFetchParams {
74
74
  searchIn?: 'title' | 'description' | 'both';
75
75
  eventId?: string;
76
76
  slug?: string;
77
+ /** Filter events by their parent series. Accepts the venue-native series id / ticker / slug (e.g. Kalshi `"KXATPMATCH"`, Polymarket `"wta"`). Passed through to the vendor where supported, otherwise applied to `sourceMetadata` after fetch. */
78
+ series?: string;
77
79
  /** Optional client-side filter applied after fetching */
78
80
  filter?: EventFilterCriteria;
79
81
  /** Filter by category. Each event belongs to a venue-assigned category such as "Sports", "Politics", "Crypto", "Bitcoin", "Soccer", "Economic Policy" (Polymarket) or "Sports", "Mentions" (Kalshi). */
@@ -81,6 +83,24 @@ export interface EventFetchParams {
81
83
  /** Filter by tags. Returns events matching ANY of the provided tags. Tags are more specific than categories -- for example a "Politics" event might carry tags ["Politics", "Geopolitics", "Middle East", "Iran"]. Common tags include "Crypto", "Elections", "Fed Rates", "FIFA World Cup", "Trump". */
82
84
  tags?: string[];
83
85
  }
86
+ /**
87
+ * Parameters for `fetchSeries`. Venues that don't expose a series concept
88
+ * return an empty array regardless of the filters.
89
+ */
90
+ export interface SeriesFetchParams {
91
+ /** Direct lookup by venue-native series id (e.g. "KXATPMATCH" on Kalshi, "atp" or "1" on Polymarket Gamma). When set, the result is the matching series with its events populated where the venue supports it. */
92
+ id?: string;
93
+ /** Lookup by series slug (e.g. "wta", "nfl"). */
94
+ slug?: string;
95
+ /** Keyword search across series title / description. */
96
+ query?: string;
97
+ /** Filter by recurrence cadence ('daily', 'weekly', 'annual', ...). */
98
+ recurrence?: string;
99
+ /** Maximum number of results to return. */
100
+ limit?: number;
101
+ /** Pagination offset. */
102
+ offset?: number;
103
+ }
84
104
  /**
85
105
  * Deprecated - use OHLCVParams or TradesParams instead. Resolution is optional for backward compatibility.
86
106
  */
@@ -219,6 +239,8 @@ export interface ExchangeHas {
219
239
  fetchMarkets: ExchangeCapability;
220
240
  /** Whether this exchange supports fetching events. */
221
241
  fetchEvents: ExchangeCapability;
242
+ /** Whether this exchange exposes a recurring-series concept (Series -> Event -> Market -> Outcome). Venues without one return `false` and an empty array from `fetchSeries`. */
243
+ fetchSeries: ExchangeCapability;
222
244
  /** Whether this exchange supports fetching OHLCV candles. */
223
245
  fetchOHLCV: ExchangeCapability;
224
246
  /** Whether this exchange supports fetching the order book. */
@@ -451,6 +473,18 @@ export declare abstract class PredictionMarketExchange {
451
473
  * @note Some exchanges (like Limitless) may only support status 'active' for search results.
452
474
  */
453
475
  fetchEvents(params?: EventFetchParams): Promise<UnifiedEvent[]>;
476
+ /**
477
+ * Fetch the recurring series (fourth tier above Event -> Market -> Outcome)
478
+ * that this venue exposes. Returns an empty array on venues without a
479
+ * series concept (Limitless, Smarkets, Probable, Metaculus, Baozi,
480
+ * Hyperliquid, SuiBets, Polymarket US).
481
+ *
482
+ * - `params.id` -> a single matching series with its events populated where supported.
483
+ * - no params -> the full list, typically without nested events for payload size.
484
+ *
485
+ * @returns Array of unified series. Always an array, including the singular-lookup case.
486
+ */
487
+ fetchSeries(params?: SeriesFetchParams): Promise<UnifiedSeries[]>;
454
488
  /**
455
489
  * Fetch a single market by lookup parameters.
456
490
  * Convenience wrapper around fetchMarkets() that returns a single result or throws MarketNotFound.
@@ -782,6 +816,13 @@ export declare abstract class PredictionMarketExchange {
782
816
  * Implementation for searching events by keyword.
783
817
  */
784
818
  protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
819
+ /**
820
+ * @internal
821
+ * Implementation for fetching recurring series. Override in venue adapters
822
+ * that expose a series concept; the default returns an empty array so
823
+ * venues without one are silently a no-op.
824
+ */
825
+ protected fetchSeriesImpl(_params: SeriesFetchParams): Promise<UnifiedSeries[]>;
785
826
  /**
786
827
  * Call an implicit API method by its operationId (or auto-generated name).
787
828
  * Provides a typed entry point so unified methods can delegate to the implicit API
@@ -361,6 +361,23 @@ class PredictionMarketExchange {
361
361
  const start = offset ?? 0;
362
362
  return limit !== undefined ? events.slice(start, start + limit) : events.slice(start);
363
363
  }
364
+ /**
365
+ * Fetch the recurring series (fourth tier above Event -> Market -> Outcome)
366
+ * that this venue exposes. Returns an empty array on venues without a
367
+ * series concept (Limitless, Smarkets, Probable, Metaculus, Baozi,
368
+ * Hyperliquid, SuiBets, Polymarket US).
369
+ *
370
+ * - `params.id` -> a single matching series with its events populated where supported.
371
+ * - no params -> the full list, typically without nested events for payload size.
372
+ *
373
+ * @returns Array of unified series. Always an array, including the singular-lookup case.
374
+ */
375
+ async fetchSeries(params) {
376
+ const { limit, offset, ...venueParams } = params ?? {};
377
+ const series = await this.fetchSeriesImpl(venueParams);
378
+ const start = offset ?? 0;
379
+ return limit !== undefined ? series.slice(start, start + limit) : series.slice(start);
380
+ }
364
381
  /**
365
382
  * Fetch a single market by lookup parameters.
366
383
  * Convenience wrapper around fetchMarkets() that returns a single result or throws MarketNotFound.
@@ -1056,6 +1073,15 @@ class PredictionMarketExchange {
1056
1073
  async fetchEventsImpl(params) {
1057
1074
  throw new Error("Method fetchEventsImpl not implemented.");
1058
1075
  }
1076
+ /**
1077
+ * @internal
1078
+ * Implementation for fetching recurring series. Override in venue adapters
1079
+ * that expose a series concept; the default returns an empty array so
1080
+ * venues without one are silently a no-op.
1081
+ */
1082
+ async fetchSeriesImpl(_params) {
1083
+ return [];
1084
+ }
1059
1085
  /**
1060
1086
  * Call an implicit API method by its operationId (or auto-generated name).
1061
1087
  * Provides a typed entry point so unified methods can delegate to the implicit API
@@ -1163,7 +1189,7 @@ class PredictionMarketExchange {
1163
1189
  // ----------------------------------------------------------------------------
1164
1190
  /** All keys that appear in ExchangeHas -- kept in sync via the exhaustive check below. */
1165
1191
  static _capabilityKeys = [
1166
- 'fetchMarkets', 'fetchEvents', 'fetchOHLCV', 'fetchOrderBook', 'fetchOrderBooks',
1192
+ 'fetchMarkets', 'fetchEvents', 'fetchSeries', 'fetchOHLCV', 'fetchOrderBook', 'fetchOrderBooks',
1167
1193
  'fetchTrades', 'createOrder', 'cancelOrder', 'fetchOrder',
1168
1194
  'fetchOpenOrders', 'fetchPositions', 'fetchBalance',
1169
1195
  'watchAddress', 'unwatchAddress', 'watchOrderBook', 'watchOrderBooks',
@@ -1175,7 +1201,7 @@ class PredictionMarketExchange {
1175
1201
  // Compile-time exhaustiveness check: fails tsc if a key exists in
1176
1202
  // ExchangeHas but is missing from _capabilityKeys above.
1177
1203
  static _exhaustiveCheck = {
1178
- fetchMarkets: true, fetchEvents: true, fetchOHLCV: true,
1204
+ fetchMarkets: true, fetchEvents: true, fetchSeries: true, fetchOHLCV: true,
1179
1205
  fetchOrderBook: true, fetchOrderBooks: true, fetchTrades: true, createOrder: true,
1180
1206
  cancelOrder: true, fetchOrder: true, fetchOpenOrders: true,
1181
1207
  fetchPositions: true, fetchBalance: true, watchAddress: true,
@@ -1194,6 +1220,7 @@ class PredictionMarketExchange {
1194
1220
  static _capabilityDelegates = {
1195
1221
  fetchMarkets: 'fetchMarketsImpl',
1196
1222
  fetchEvents: 'fetchEventsImpl',
1223
+ fetchSeries: 'fetchSeriesImpl',
1197
1224
  watchOrderBooks: 'watchOrderBook',
1198
1225
  fetchMatches: 'fetchMarketMatches',
1199
1226
  fetchHedges: 'fetchRelatedMarkets',
@@ -12,6 +12,7 @@ export declare class BaoziExchange extends PredictionMarketExchange {
12
12
  fetchOpenOrders: "emulated";
13
13
  cancelOrder: false;
14
14
  watchTrades: false;
15
+ fetchSeries: false;
15
16
  };
16
17
  private auth?;
17
18
  private connection;
@@ -19,6 +19,7 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
19
19
  fetchOpenOrders: 'emulated',
20
20
  cancelOrder: false,
21
21
  watchTrades: false,
22
+ fetchSeries: false,
22
23
  };
23
24
  auth;
24
25
  connection;
@@ -59,6 +60,10 @@ class BaoziExchange extends BaseExchange_1.PredictionMarketExchange {
59
60
  return this.normalizer.normalizeMarkets(rawMarkets, params);
60
61
  }
61
62
  async fetchEventsImpl(params) {
63
+ // Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
64
+ if (params.series !== undefined) {
65
+ return [];
66
+ }
62
67
  const rawMarkets = await this.fetcher.fetchRawEvents(params);
63
68
  return this.normalizer.normalizeEvents(rawMarkets, {
64
69
  query: params.query,
@@ -1,5 +1,5 @@
1
- import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, ExchangeCredentials } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, OrderBook, Trade, Order, Position, CreateOrderParams, BuiltOrder } from '../../types';
1
+ import { PredictionMarketExchange, MarketFilterParams, EventFetchParams, SeriesFetchParams, ExchangeCredentials } from '../../BaseExchange';
2
+ import { UnifiedMarket, UnifiedEvent, UnifiedSeries, OrderBook, Trade, Order, Position, CreateOrderParams, BuiltOrder } from '../../types';
3
3
  export interface GeminiTitanExchangeOptions {
4
4
  credentials?: ExchangeCredentials;
5
5
  sandbox?: boolean;
@@ -10,11 +10,15 @@ export declare class GeminiTitanExchange extends PredictionMarketExchange {
10
10
  private readonly normalizer;
11
11
  private readonly geminiAuth?;
12
12
  private geminiWs?;
13
+ protected readonly capabilityOverrides: {
14
+ fetchSeries: "emulated";
15
+ };
13
16
  constructor(credentials?: ExchangeCredentials | GeminiTitanExchangeOptions);
14
17
  get name(): string;
15
18
  private requireAuth;
16
19
  protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
17
20
  protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
21
+ protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
18
22
  fetchOrderBook(outcomeId: string, _limit?: number, _params?: Record<string, any>): Promise<OrderBook>;
19
23
  buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
20
24
  submitOrder(built: BuiltOrder): Promise<Order>;
@@ -16,6 +16,9 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
16
16
  normalizer;
17
17
  geminiAuth;
18
18
  geminiWs;
19
+ capabilityOverrides = {
20
+ fetchSeries: 'emulated',
21
+ };
19
22
  constructor(credentials) {
20
23
  const opts = credentials && 'credentials' in credentials
21
24
  ? credentials
@@ -63,10 +66,87 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
63
66
  }
64
67
  async fetchEventsImpl(params) {
65
68
  const rawEvents = await this.fetcher.fetchRawEvents(params);
66
- return rawEvents
69
+ let filtered = rawEvents;
70
+ // Client-side series filter: keep only events whose series id matches.
71
+ if (params.series) {
72
+ const seriesId = params.series;
73
+ filtered = rawEvents.filter((e) => {
74
+ if (e.series == null)
75
+ return false;
76
+ const s = e.series;
77
+ const id = String(s['id'] ?? s['ticker'] ?? s['symbol'] ?? '');
78
+ return id === seriesId;
79
+ });
80
+ }
81
+ return filtered
67
82
  .map(e => this.normalizer.normalizeEventWithMarkets(e))
68
83
  .filter((e) => e !== null);
69
84
  }
85
+ async fetchSeriesImpl(params) {
86
+ // Gemini-Titan has no dedicated /series endpoint. Derive the catalog by
87
+ // fetching all events and grouping by the series id in event.series.
88
+ const rawEvents = await this.fetcher.fetchRawEvents({});
89
+ // Build a map from series id -> { rawSeries, rawEvents[] }
90
+ const seriesMap = new Map();
91
+ for (const event of rawEvents) {
92
+ if (event.series == null)
93
+ continue;
94
+ const s = event.series;
95
+ const id = String(s['id'] ?? s['ticker'] ?? s['symbol'] ?? '');
96
+ if (!id)
97
+ continue;
98
+ const existing = seriesMap.get(id);
99
+ if (existing) {
100
+ existing.raws.push(event);
101
+ }
102
+ else {
103
+ seriesMap.set(id, { raw: s, raws: [event] });
104
+ }
105
+ }
106
+ let entries = Array.from(seriesMap.entries()).map(([id, v]) => ({
107
+ id,
108
+ raw: v.raw,
109
+ raws: v.raws,
110
+ }));
111
+ // Apply params.id filter
112
+ if (params.id) {
113
+ entries = entries.filter((e) => e.id === params.id);
114
+ }
115
+ // Apply params.slug filter (treat slug same as id for Gemini series)
116
+ if (params.slug) {
117
+ const slug = params.slug;
118
+ entries = entries.filter((e) => {
119
+ const rawSlug = e.raw['slug'] != null ? String(e.raw['slug']) : e.id;
120
+ return rawSlug === slug;
121
+ });
122
+ }
123
+ // Apply params.query filter (title match)
124
+ if (params.query) {
125
+ const lowerQuery = params.query.toLowerCase();
126
+ entries = entries.filter((e) => {
127
+ const title = String(e.raw['title'] ?? e.raw['name'] ?? '');
128
+ return title.toLowerCase().includes(lowerQuery);
129
+ });
130
+ }
131
+ // Apply params.recurrence filter
132
+ if (params.recurrence) {
133
+ const recurrence = params.recurrence;
134
+ entries = entries.filter((e) => {
135
+ const freq = e.raw['frequency'] ?? e.raw['recurrence'];
136
+ return freq != null && String(freq) === recurrence;
137
+ });
138
+ }
139
+ return entries.map((e) => {
140
+ let events;
141
+ // When fetching by id, populate the events field.
142
+ if (params.id) {
143
+ events = e.raws
144
+ .map((raw) => this.normalizer.normalizeEventWithMarkets(raw))
145
+ .filter((ev) => ev !== null);
146
+ }
147
+ return this.normalizer.normalizeSeries(e.raw, events);
148
+ });
149
+ }
70
150
  async fetchOrderBook(outcomeId, _limit, _params) {
71
151
  const resolved = await this.resolveOutcomeAlias(outcomeId, _params);
72
152
  outcomeId = resolved.outcomeId;
@@ -1,10 +1,16 @@
1
- import { UnifiedMarket, UnifiedEvent, OrderBook, Order, Position } from '../../types';
1
+ import { UnifiedMarket, UnifiedEvent, UnifiedSeries, OrderBook, Order, Position } from '../../types';
2
2
  import { IExchangeNormalizer } from '../interfaces';
3
3
  import { GeminiRawEvent, GeminiRawContract, GeminiRawOrder, GeminiRawPosition, GeminiRawOrderBook } from './types';
4
4
  export declare class GeminiNormalizer implements IExchangeNormalizer<GeminiRawEvent, GeminiRawEvent> {
5
5
  normalizeMarket(raw: GeminiRawEvent): UnifiedMarket | null;
6
6
  normalizeEvent(raw: GeminiRawEvent): UnifiedEvent | null;
7
7
  normalizeEventWithMarkets(raw: GeminiRawEvent): UnifiedEvent | null;
8
+ /**
9
+ * Produce a UnifiedSeries from a Gemini `series` object (raw Record).
10
+ * The series id is taken from the first non-null of: id, ticker, symbol.
11
+ * `events` is optionally injected by the caller when doing a single-id lookup.
12
+ */
13
+ normalizeSeries(raw: Record<string, unknown>, events?: UnifiedEvent[]): UnifiedSeries;
8
14
  normalizeMarketsFromEvent(raw: GeminiRawEvent): UnifiedMarket[];
9
15
  normalizeContract(contract: GeminiRawContract, event: GeminiRawEvent): UnifiedMarket | null;
10
16
  normalizeOrderBook(raw: GeminiRawOrderBook, _outcomeId: string): OrderBook;
@@ -141,6 +141,29 @@ class GeminiNormalizer {
141
141
  volume24h: markets.reduce((sum, m) => sum + m.volume24h, 0),
142
142
  };
143
143
  }
144
+ /**
145
+ * Produce a UnifiedSeries from a Gemini `series` object (raw Record).
146
+ * The series id is taken from the first non-null of: id, ticker, symbol.
147
+ * `events` is optionally injected by the caller when doing a single-id lookup.
148
+ */
149
+ normalizeSeries(raw, events) {
150
+ const id = String(raw['id'] ?? raw['ticker'] ?? raw['symbol'] ?? '');
151
+ const ticker = raw['ticker'] != null ? String(raw['ticker']) : undefined;
152
+ const title = String(raw['title'] ?? raw['name'] ?? id);
153
+ const recurrence = raw['frequency'] != null
154
+ ? String(raw['frequency'])
155
+ : raw['recurrence'] != null
156
+ ? String(raw['recurrence'])
157
+ : null;
158
+ return {
159
+ id,
160
+ ticker,
161
+ title,
162
+ recurrence,
163
+ events,
164
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, ['id', 'ticker', 'title', 'name', 'frequency', 'recurrence']),
165
+ };
166
+ }
144
167
  normalizeMarketsFromEvent(raw) {
145
168
  const markets = [];
146
169
  for (const contract of raw.contracts) {
@@ -5,6 +5,9 @@ export interface HyperliquidExchangeOptions {
5
5
  testnet?: boolean;
6
6
  }
7
7
  export declare class HyperliquidExchange extends PredictionMarketExchange {
8
+ protected readonly capabilityOverrides: {
9
+ fetchSeries: false;
10
+ };
8
11
  private readonly config;
9
12
  private readonly fetcher;
10
13
  private readonly normalizer;
@@ -9,6 +9,9 @@ const normalizer_1 = require("./normalizer");
9
9
  const auth_1 = require("./auth");
10
10
  const errors_2 = require("./errors");
11
11
  class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
12
+ capabilityOverrides = {
13
+ fetchSeries: false,
14
+ };
12
15
  config;
13
16
  fetcher;
14
17
  normalizer;
@@ -69,6 +72,10 @@ class HyperliquidExchange extends BaseExchange_1.PredictionMarketExchange {
69
72
  .filter((m) => m !== null);
70
73
  }
71
74
  async fetchEventsImpl(params) {
75
+ // Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
76
+ if (params.series !== undefined) {
77
+ return [];
78
+ }
72
79
  const [rawQuestions, meta, mids] = await Promise.all([
73
80
  this.fetcher.fetchRawEvents(params),
74
81
  this.fetcher.fetchOutcomeMeta(),
@@ -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-30T12:19:28.168Z
3
+ * Generated at: 2026-05-30T13:35:15.520Z
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-30T12:19:28.168Z
6
+ * Generated at: 2026-05-30T13:35:15.520Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -44,6 +44,14 @@ interface KalshiSeriesInfo {
44
44
  title?: string;
45
45
  tags?: string[];
46
46
  }
47
+ export interface KalshiRawSeries {
48
+ ticker: string;
49
+ title?: string;
50
+ tags?: string[];
51
+ frequency?: string;
52
+ category?: string;
53
+ [key: string]: unknown;
54
+ }
47
55
  export interface KalshiRawEventPage {
48
56
  events: KalshiRawEvent[];
49
57
  cursor?: string | null;
@@ -153,6 +161,7 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
153
161
  }>;
154
162
  fetchRawOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
155
163
  fetchRawHistoricalOrders(queryParams: Record<string, any>): Promise<KalshiRawOrder[]>;
164
+ fetchRawSeriesList(): Promise<KalshiRawSeries[]>;
156
165
  fetchRawSeriesMap(): Promise<Map<string, KalshiSeriesInfo>>;
157
166
  fetchRawEventByTicker(eventTicker: string): Promise<KalshiRawEvent[]>;
158
167
  private fetchRawEventsDefault;
@@ -160,6 +169,7 @@ export declare class KalshiFetcher implements IExchangeFetcher<KalshiRawEvent, K
160
169
  private enrichEventsWithSeriesMap;
161
170
  private fetchActiveEvents;
162
171
  private fetchAllWithStatus;
172
+ private fetchAllWithSeriesTicker;
163
173
  private fetchPageWithStatus;
164
174
  }
165
175
  export {};
@@ -58,6 +58,11 @@ class KalshiFetcher {
58
58
  if (params.slug) {
59
59
  return this.fetchRawEventByTicker(params.slug);
60
60
  }
61
+ // When a series filter is present, delegate to server-side filtering so
62
+ // only events belonging to that series are returned — no double-filtering.
63
+ if (params.series) {
64
+ return this.fetchAllWithSeriesTicker(params.series);
65
+ }
61
66
  const status = params?.status || 'active';
62
67
  if (status === 'all') {
63
68
  const [openEvents, closedEvents, settledEvents] = await Promise.all([
@@ -213,6 +218,15 @@ class KalshiFetcher {
213
218
  const data = await this.ctx.callApi('GetHistoricalOrders', queryParams);
214
219
  return data.orders || [];
215
220
  }
221
+ async fetchRawSeriesList() {
222
+ try {
223
+ const data = await this.ctx.callApi('GetSeriesList');
224
+ return (data.series || []);
225
+ }
226
+ catch (e) {
227
+ throw errors_1.kalshiErrorMapper.mapError(e);
228
+ }
229
+ }
216
230
  async fetchRawSeriesMap() {
217
231
  try {
218
232
  const data = await this.ctx.callApi('GetSeriesList');
@@ -381,6 +395,28 @@ class KalshiFetcher {
381
395
  } while (cursor && page < MAX_PAGES);
382
396
  return allEvents;
383
397
  }
398
+ async fetchAllWithSeriesTicker(seriesTicker) {
399
+ let allEvents = [];
400
+ let cursor = null;
401
+ let page = 0;
402
+ do {
403
+ const queryParams = {
404
+ series_ticker: seriesTicker,
405
+ with_nested_markets: true,
406
+ limit: BATCH_SIZE,
407
+ };
408
+ if (cursor)
409
+ queryParams.cursor = cursor;
410
+ const data = await this.ctx.callApi('GetEvents', queryParams);
411
+ const events = data.events || [];
412
+ if (events.length === 0)
413
+ break;
414
+ allEvents = [...allEvents, ...events];
415
+ cursor = data.cursor;
416
+ page++;
417
+ } while (cursor && page < MAX_PAGES);
418
+ return allEvents;
419
+ }
384
420
  async fetchPageWithStatus(apiStatus, maxEvents, initialCursor) {
385
421
  let allEvents = [];
386
422
  let cursor = initialCursor || null;
@@ -1,5 +1,5 @@
1
- import { EventFetchParams, ExchangeCredentials, HistoryFilterParams, MarketFilterParams, MyTradesParams, OHLCVParams, OrderHistoryParams, PredictionMarketExchange, TradesParams } from '../../BaseExchange';
2
- import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from '../../types';
1
+ import { EventFetchParams, ExchangeCredentials, HistoryFilterParams, MarketFilterParams, MyTradesParams, OHLCVParams, OrderHistoryParams, PredictionMarketExchange, SeriesFetchParams, TradesParams } from '../../BaseExchange';
2
+ import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UnifiedSeries, UserTrade } from '../../types';
3
3
  import { KalshiWebSocketConfig } from './websocket';
4
4
  export type { KalshiWebSocketConfig };
5
5
  export interface KalshiExchangeOptions {
@@ -23,6 +23,7 @@ export declare class KalshiExchange extends PredictionMarketExchange {
23
23
  private ensureAuth;
24
24
  protected fetchMarketsImpl(params?: MarketFilterParams): Promise<UnifiedMarket[]>;
25
25
  protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
26
+ protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
26
27
  fetchEventsPage(params?: EventFetchParams): Promise<{
27
28
  events: UnifiedEvent[];
28
29
  cursor: string | null;
@@ -118,6 +118,39 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
118
118
  .filter((e) => e !== null)
119
119
  .slice(0, limit);
120
120
  }
121
+ async fetchSeriesImpl(params) {
122
+ const rawList = await this.fetcher.fetchRawSeriesList();
123
+ // If a specific series id is requested, fetch its events and attach them.
124
+ // We still iterate the full list so filters apply uniformly.
125
+ let eventsById = null;
126
+ if (params.id) {
127
+ const rawEvents = await this.fetcher.fetchRawEvents({ series: params.id });
128
+ const normalizedEvents = rawEvents
129
+ .map((raw) => this.normalizer.normalizeEvent(raw))
130
+ .filter((e) => e !== null);
131
+ eventsById = new Map([[params.id, normalizedEvents]]);
132
+ }
133
+ const normalized = rawList.map((raw) => {
134
+ const events = eventsById?.get(raw.ticker);
135
+ return this.normalizer.normalizeSeries(raw, events);
136
+ });
137
+ // Client-side filters
138
+ let filtered = normalized;
139
+ if (params.id) {
140
+ filtered = filtered.filter((s) => s.id === params.id);
141
+ }
142
+ else if (params.slug) {
143
+ filtered = filtered.filter((s) => s.ticker === params.slug || s.slug === params.slug);
144
+ }
145
+ if (params.query) {
146
+ const lowerQuery = params.query.toLowerCase();
147
+ filtered = filtered.filter((s) => s.title.toLowerCase().includes(lowerQuery));
148
+ }
149
+ if (params.recurrence) {
150
+ filtered = filtered.filter((s) => s.recurrence === params.recurrence);
151
+ }
152
+ return filtered;
153
+ }
121
154
  async fetchEventsPage(params = {}) {
122
155
  const page = await this.fetcher.fetchRawEventPage(params);
123
156
  const query = (params?.query || '').toLowerCase();
@@ -1,12 +1,13 @@
1
1
  import { OHLCVParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Position, Balance } from '../../types';
2
+ import { UnifiedMarket, UnifiedEvent, UnifiedSeries, PriceCandle, OrderBook, Trade, UserTrade, Position, Balance } from '../../types';
3
3
  import { IExchangeNormalizer } from '../interfaces';
4
- import { KalshiRawEvent, KalshiRawMarket, KalshiRawCandlestick, KalshiRawTrade, KalshiRawFill, KalshiRawOrder, KalshiRawPosition, KalshiRawOrderBookFp } from './fetcher';
4
+ import { KalshiRawEvent, KalshiRawMarket, KalshiRawCandlestick, KalshiRawTrade, KalshiRawFill, KalshiRawOrder, KalshiRawPosition, KalshiRawOrderBookFp, KalshiRawSeries } from './fetcher';
5
5
  export declare class KalshiNormalizer implements IExchangeNormalizer<KalshiRawEvent, KalshiRawEvent> {
6
6
  normalizeMarket(raw: KalshiRawEvent): UnifiedMarket | null;
7
7
  normalizeMarketsFromEvent(rawEvent: KalshiRawEvent): UnifiedMarket[];
8
8
  normalizeRawMarket(event: KalshiRawEvent, market: KalshiRawMarket): UnifiedMarket | null;
9
9
  normalizeEvent(raw: KalshiRawEvent): UnifiedEvent | null;
10
+ normalizeSeries(raw: KalshiRawSeries, events?: UnifiedEvent[]): UnifiedSeries;
10
11
  normalizeOHLCV(rawCandles: KalshiRawCandlestick[], params: OHLCVParams): PriceCandle[];
11
12
  normalizeOrderBook(raw: {
12
13
  orderbook_fp: KalshiRawOrderBookFp;
@@ -10,6 +10,9 @@ const price_1 = require("./price");
10
10
  const KALSHI_PROMOTED_EVENT_KEYS = [
11
11
  'event_ticker', 'title', 'markets', 'category', 'image_url', 'tags',
12
12
  ];
13
+ const KALSHI_PROMOTED_SERIES_KEYS = [
14
+ 'ticker', 'title', 'tags', 'frequency', 'category',
15
+ ];
13
16
  const KALSHI_PROMOTED_MARKET_KEYS = [
14
17
  'ticker', 'title', 'rules_primary', 'rules_secondary', 'expiration_time',
15
18
  'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
@@ -137,6 +140,17 @@ class KalshiNormalizer {
137
140
  sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_EVENT_KEYS),
138
141
  };
139
142
  }
143
+ normalizeSeries(raw, events) {
144
+ return {
145
+ id: raw.ticker,
146
+ ticker: raw.ticker,
147
+ title: (typeof raw.title === 'string' && raw.title.trim()) ? raw.title.trim() : raw.ticker,
148
+ recurrence: (typeof raw.frequency === 'string' && raw.frequency.trim()) ? raw.frequency.trim() : null,
149
+ url: `https://kalshi.com/events?series=${raw.ticker}`,
150
+ events: events ?? undefined,
151
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, KALSHI_PROMOTED_SERIES_KEYS),
152
+ };
153
+ }
140
154
  normalizeOHLCV(rawCandles, params) {
141
155
  const candles = rawCandles.map((c) => {
142
156
  const p = c.price || {};
@@ -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-30T12:19:28.209Z
3
+ * Generated at: 2026-05-30T13:35:15.569Z
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-30T12:19:28.209Z
6
+ * Generated at: 2026-05-30T13:35:15.569Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -13,6 +13,7 @@ export interface LimitlessExchangeOptions {
13
13
  export declare class LimitlessExchange extends PredictionMarketExchange {
14
14
  protected readonly capabilityOverrides: {
15
15
  fetchOrder: false;
16
+ fetchSeries: false;
16
17
  };
17
18
  private auth?;
18
19
  private client?;
@@ -22,6 +22,7 @@ const logger_1 = require("../../utils/logger");
22
22
  class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
23
23
  capabilityOverrides = {
24
24
  fetchOrder: false,
25
+ fetchSeries: false,
25
26
  };
26
27
  auth;
27
28
  client;
@@ -146,6 +147,10 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
146
147
  return marketsAfterOffset.slice(0, limit);
147
148
  }
148
149
  async fetchEventsImpl(params) {
150
+ // Venue does not expose a series concept; honoring `params.series` by
151
+ // returning [] rather than ignoring the filter.
152
+ if (params.series !== undefined)
153
+ return [];
149
154
  const rawEvents = await this.fetcher.fetchRawEvents(params);
150
155
  return rawEvents
151
156
  .map((raw) => this.normalizer.normalizeEvent(raw))
@@ -143,6 +143,10 @@ async function fetchEventBySlug(slug, callApi) {
143
143
  return [];
144
144
  }
145
145
  async function fetchEvents(params, callApi) {
146
+ // Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
147
+ if (params.series !== undefined) {
148
+ return [];
149
+ }
146
150
  try {
147
151
  // Direct lookup by slug (post ID, tournament slug, or url_title)
148
152
  if (params.slug) {