pmxt-core 2.30.8 → 2.31.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.
@@ -46,6 +46,12 @@ export interface MarketFilterParams {
46
46
  similarityThreshold?: number;
47
47
  }
48
48
  export interface MarketFetchParams extends MarketFilterParams {
49
+ /** Optional client-side filter applied after fetching */
50
+ filter?: MarketFilterCriteria;
51
+ /** Shorthand for filter.category -- merged into filter (takes precedence) */
52
+ category?: string;
53
+ /** Shorthand for filter.tags -- merged into filter (takes precedence) */
54
+ tags?: string[];
49
55
  }
50
56
  export interface EventFetchParams {
51
57
  query?: string;
@@ -60,6 +66,12 @@ export interface EventFetchParams {
60
66
  searchIn?: 'title' | 'description' | 'both';
61
67
  eventId?: string;
62
68
  slug?: string;
69
+ /** Optional client-side filter applied after fetching */
70
+ filter?: EventFilterCriteria;
71
+ /** Shorthand for filter.category -- merged into filter (takes precedence) */
72
+ category?: string;
73
+ /** Shorthand for filter.tags -- merged into filter (takes precedence) */
74
+ tags?: string[];
63
75
  }
64
76
  /**
65
77
  * Deprecated - use OHLCVParams or TradesParams instead. Resolution is optional for backward compatibility.
@@ -115,7 +127,7 @@ export interface OrderHistoryParams {
115
127
  /** Opaque pagination cursor from a previous response */
116
128
  cursor?: string;
117
129
  }
118
- export type MarketFilterCriteria = {
130
+ export interface MarketFilterCriteria {
119
131
  text?: string;
120
132
  searchIn?: ('title' | 'description' | 'category' | 'tags' | 'outcomes')[];
121
133
  volume24h?: {
@@ -153,9 +165,9 @@ export type MarketFilterCriteria = {
153
165
  min?: number;
154
166
  max?: number;
155
167
  };
156
- };
168
+ }
157
169
  export type MarketFilterFunction = (market: UnifiedMarket) => boolean;
158
- export type EventFilterCriteria = {
170
+ export interface EventFilterCriteria {
159
171
  text?: string;
160
172
  searchIn?: ('title' | 'description' | 'category' | 'tags')[];
161
173
  category?: string;
@@ -169,7 +181,7 @@ export type EventFilterCriteria = {
169
181
  min?: number;
170
182
  max?: number;
171
183
  };
172
- };
184
+ }
173
185
  export type EventFilterFunction = (event: UnifiedEvent) => boolean;
174
186
  export type ExchangeCapability = true | false | 'emulated';
175
187
  export interface ExchangeHas {
@@ -338,6 +350,7 @@ export declare abstract class PredictionMarketExchange {
338
350
  fetchMarketsPaginated(params?: {
339
351
  limit?: number;
340
352
  cursor?: string;
353
+ filter?: MarketFilterCriteria;
341
354
  }): Promise<PaginatedMarketsResult>;
342
355
  /**
343
356
  * Fetch events with optional keyword search.
@@ -175,7 +175,27 @@ class PredictionMarketExchange {
175
175
  * @note Some exchanges (like Limitless) may only support status 'active' for search results.
176
176
  */
177
177
  async fetchMarkets(params) {
178
- return this.fetchMarketsImpl(params);
178
+ const { filter, category, tags, ...fetchParams } = params ?? {};
179
+ // Merge explicit category/tags into the filter (explicit params take precedence)
180
+ const mergedFilter = {
181
+ ...(filter ?? {}),
182
+ ...(category !== undefined ? { category } : {}),
183
+ ...(tags !== undefined ? { tags } : {}),
184
+ };
185
+ const hasFilter = Object.keys(mergedFilter).length > 0;
186
+ if (hasFilter) {
187
+ // When filtering, pull limit/offset out of the venue fetch so
188
+ // the venue returns enough data for the filter to work with.
189
+ // Apply limit/offset after filtering so the caller gets the
190
+ // number of results they asked for.
191
+ const { limit, offset, ...venueParams } = fetchParams;
192
+ const markets = await this.fetchMarketsImpl(Object.keys(venueParams).length > 0 ? venueParams : undefined);
193
+ const filtered = this.filterMarkets(markets, mergedFilter);
194
+ const start = offset ?? 0;
195
+ return limit !== undefined ? filtered.slice(start, start + limit) : filtered.slice(start);
196
+ }
197
+ const markets = await this.fetchMarketsImpl(Object.keys(fetchParams).length > 0 ? fetchParams : undefined);
198
+ return markets;
179
199
  }
180
200
  /**
181
201
  * Fetch markets with cursor-based pagination backed by a stable in-memory snapshot.
@@ -195,6 +215,8 @@ class PredictionMarketExchange {
195
215
  async fetchMarketsPaginated(params) {
196
216
  const limit = params?.limit;
197
217
  const cursor = params?.cursor;
218
+ const filter = params?.filter;
219
+ const applyFilter = (markets) => filter ? this.filterMarkets(markets, filter) : markets;
198
220
  if (cursor) {
199
221
  // Cursor encodes: snapshotId:offset
200
222
  const sep = cursor.indexOf(':');
@@ -209,7 +231,7 @@ class PredictionMarketExchange {
209
231
  const slice = limit !== undefined ? markets.slice(offset, offset + limit) : markets.slice(offset);
210
232
  const nextOffset = offset + slice.length;
211
233
  const nextCursor = nextOffset < markets.length ? `${snapshotId}:${nextOffset}` : undefined;
212
- return { data: slice, total: markets.length, nextCursor };
234
+ return { data: applyFilter(slice), total: markets.length, nextCursor };
213
235
  }
214
236
  // No cursor — (re)fetch snapshot
215
237
  if (!this._snapshot ||
@@ -224,11 +246,11 @@ class PredictionMarketExchange {
224
246
  }
225
247
  const markets = this._snapshot.markets;
226
248
  if (!limit) {
227
- return { data: markets, total: markets.length, nextCursor: undefined };
249
+ return { data: applyFilter(markets), total: markets.length, nextCursor: undefined };
228
250
  }
229
251
  const slice = markets.slice(0, limit);
230
252
  const nextCursor = limit < markets.length ? `${this._snapshot.id}:${limit}` : undefined;
231
- return { data: slice, total: markets.length, nextCursor };
253
+ return { data: applyFilter(slice), total: markets.length, nextCursor };
232
254
  }
233
255
  /**
234
256
  * Fetch events with optional keyword search.
@@ -244,7 +266,23 @@ class PredictionMarketExchange {
244
266
  * @note Some exchanges (like Limitless) may only support status 'active' for search results.
245
267
  */
246
268
  async fetchEvents(params) {
247
- return this.fetchEventsImpl(params ?? {});
269
+ const { filter, category, tags, ...fetchParams } = params ?? {};
270
+ // Merge explicit category/tags into the filter (explicit params take precedence)
271
+ const mergedFilter = {
272
+ ...(filter ?? {}),
273
+ ...(category !== undefined ? { category } : {}),
274
+ ...(tags !== undefined ? { tags } : {}),
275
+ };
276
+ const hasFilter = Object.keys(mergedFilter).length > 0;
277
+ if (hasFilter) {
278
+ const { limit, offset, ...venueParams } = fetchParams;
279
+ const events = await this.fetchEventsImpl(venueParams);
280
+ const filtered = this.filterEvents(events, mergedFilter);
281
+ const start = offset ?? 0;
282
+ return limit !== undefined ? filtered.slice(start, start + limit) : filtered.slice(start);
283
+ }
284
+ const events = await this.fetchEventsImpl(fetchParams);
285
+ return events;
248
286
  }
249
287
  /**
250
288
  * Fetch a single market by lookup parameters.
@@ -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-04-14T08:11:59.575Z
3
+ * Generated at: 2026-04-14T11:53:52.793Z
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-04-14T08:11:59.575Z
6
+ * Generated at: 2026-04-14T11:53:52.793Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -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-04-14T08:11:59.613Z
3
+ * Generated at: 2026-04-14T11:53:52.834Z
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-04-14T08:11:59.613Z
6
+ * Generated at: 2026-04-14T11:53:52.834Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -432,18 +432,12 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
432
432
  fetches.push(this.fetchPositions(address)
433
433
  .then((positions) => {
434
434
  result.positions = positions;
435
- })
436
- .catch(() => {
437
- result.positions = [];
438
435
  }));
439
436
  }
440
437
  if (types.includes('balances')) {
441
438
  fetches.push(this.getAddressOnChainBalance(address)
442
439
  .then((balances) => {
443
440
  result.balances = balances;
444
- })
445
- .catch(() => {
446
- result.balances = [];
447
441
  }));
448
442
  }
449
443
  await Promise.all(fetches);
@@ -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-04-14T08:11:59.625Z
3
+ * Generated at: 2026-04-14T11:53:52.846Z
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-04-14T08:11:59.625Z
6
+ * Generated at: 2026-04-14T11:53:52.846Z
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-04-14T08:11:59.629Z
3
+ * Generated at: 2026-04-14T11:53:52.851Z
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-04-14T08:11:59.629Z
6
+ * Generated at: 2026-04-14T11:53:52.851Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -154,7 +154,7 @@ class OpinionNormalizer {
154
154
  return {
155
155
  id: raw.orderId || '',
156
156
  marketId: String(raw.marketId),
157
- outcomeId: String(raw.marketId),
157
+ outcomeId: raw.outcome || (raw.outcomeSide === 1 ? 'Yes' : 'No'),
158
158
  side: raw.side === 1 ? 'buy' : 'sell',
159
159
  type: raw.tradingMethod === 2 ? 'limit' : 'market',
160
160
  price: (0, utils_1.parseNumStr)(raw.price),
@@ -170,14 +170,20 @@ class OpinionNormalizer {
170
170
  if (!raw || raw.marketType !== 0)
171
171
  return null;
172
172
  const marketId = String(raw.marketId);
173
+ if (!raw.yesTokenId) {
174
+ throw new Error(`Opinion market ${marketId} is missing yesTokenId`);
175
+ }
176
+ if (!raw.noTokenId) {
177
+ throw new Error(`Opinion market ${marketId} is missing noTokenId`);
178
+ }
173
179
  const yesOutcome = {
174
- outcomeId: raw.yesTokenId || '',
180
+ outcomeId: raw.yesTokenId,
175
181
  marketId,
176
182
  label: raw.yesLabel || 'Yes',
177
183
  price: 0.5,
178
184
  };
179
185
  const noOutcome = {
180
- outcomeId: raw.noTokenId || '',
186
+ outcomeId: raw.noTokenId,
181
187
  marketId,
182
188
  label: raw.noLabel || 'No',
183
189
  price: 0.5,
@@ -200,14 +206,20 @@ class OpinionNormalizer {
200
206
  if (!child)
201
207
  return null;
202
208
  const marketId = String(child.marketId);
209
+ if (!child.yesTokenId) {
210
+ throw new Error(`Opinion child market ${marketId} is missing yesTokenId`);
211
+ }
212
+ if (!child.noTokenId) {
213
+ throw new Error(`Opinion child market ${marketId} is missing noTokenId`);
214
+ }
203
215
  const yesOutcome = {
204
- outcomeId: child.yesTokenId || '',
216
+ outcomeId: child.yesTokenId,
205
217
  marketId,
206
218
  label: child.yesLabel || 'Yes',
207
219
  price: 0.5,
208
220
  };
209
221
  const noOutcome = {
210
- outcomeId: child.noTokenId || '',
222
+ outcomeId: child.noTokenId,
211
223
  marketId,
212
224
  label: child.noLabel || 'No',
213
225
  price: 0.5,
@@ -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-04-14T08:11:59.581Z
3
+ * Generated at: 2026-04-14T11:53:52.802Z
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-04-14T08:11:59.581Z
6
+ * Generated at: 2026-04-14T11:53:52.802Z
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-04-14T08:11:59.593Z
3
+ * Generated at: 2026-04-14T11:53:52.816Z
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-04-14T08:11:59.593Z
6
+ * Generated at: 2026-04-14T11:53:52.816Z
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-04-14T08:11:59.591Z
3
+ * Generated at: 2026-04-14T11:53:52.814Z
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-04-14T08:11:59.591Z
6
+ * Generated at: 2026-04-14T11:53:52.814Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -130,7 +130,10 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
130
130
  limit: params?.limit ?? 250,
131
131
  offset: params?.offset ?? 0,
132
132
  });
133
- let markets = (resp.markets || []).map(m => this.normalizer.normalizeMarket(m));
133
+ if (!resp.markets) {
134
+ throw new Error('PolymarketUS markets.list response missing required "markets" field');
135
+ }
136
+ let markets = resp.markets.map(m => this.normalizer.normalizeMarket(m));
134
137
  if (params?.query) {
135
138
  const q = params.query.toLowerCase();
136
139
  markets = markets.filter(m => m.title.toLowerCase().includes(q) ||
@@ -151,7 +154,10 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
151
154
  limit: params?.limit ?? 100,
152
155
  offset: params?.offset ?? 0,
153
156
  });
154
- let events = (resp.events || []).map(e => this.normalizer.normalizeEvent(e));
157
+ if (!resp.events) {
158
+ throw new Error('PolymarketUS events.list response missing required "events" field');
159
+ }
160
+ let events = resp.events.map(e => this.normalizer.normalizeEvent(e));
155
161
  if (params?.query) {
156
162
  const q = params.query.toLowerCase();
157
163
  events = events.filter(e => e.title.toLowerCase().includes(q) ||
@@ -184,7 +190,10 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
184
190
  this.requireAuth();
185
191
  return this.run(async () => {
186
192
  const resp = await this.client.portfolio.positions({});
187
- return this.normalizer.normalizePositions(resp.positions || {});
193
+ if (!resp.positions) {
194
+ throw new Error('PolymarketUS portfolio.positions response missing required "positions" field');
195
+ }
196
+ return this.normalizer.normalizePositions(resp.positions);
188
197
  });
189
198
  }
190
199
  async fetchMyTrades(params) {
@@ -195,7 +204,10 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
195
204
  limit: params?.limit ?? 100,
196
205
  marketSlug: params?.marketId,
197
206
  });
198
- const activities = resp.activities || [];
207
+ if (!resp.activities) {
208
+ throw new Error('PolymarketUS portfolio.activities response missing required "activities" field');
209
+ }
210
+ const activities = resp.activities;
199
211
  const trades = [];
200
212
  activities.forEach((activity, idx) => {
201
213
  const trade = this.normalizer.normalizeUserTradeFromActivity(activity, idx);
@@ -214,7 +226,10 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
214
226
  const resp = await this.client.orders.list({
215
227
  slugs: marketId ? [marketId] : undefined,
216
228
  });
217
- const raws = resp.orders || [];
229
+ if (!resp.orders) {
230
+ throw new Error('PolymarketUS orders.list response missing required "orders" field');
231
+ }
232
+ const raws = resp.orders;
218
233
  return raws.map(raw => {
219
234
  const normalized = this.normalizer.normalizeOrder(raw);
220
235
  this.cacheOrder(normalized.id, raw.marketSlug);
@@ -274,26 +289,7 @@ class PolymarketUSExchange extends BaseExchange_1.PredictionMarketExchange {
274
289
  const response = await this.run(() => this.client.orders.create(sdkParams));
275
290
  const newId = response.id;
276
291
  this.cacheOrder(newId, built.params.marketId);
277
- try {
278
- return await this.fetchOrder(newId);
279
- }
280
- catch {
281
- // Order may not yet be visible via retrieve. Fall back to a
282
- // synthetic Order built from the original params.
283
- return {
284
- id: newId,
285
- marketId: built.params.marketId,
286
- outcomeId: built.params.outcomeId,
287
- side: built.params.side,
288
- type: built.params.type,
289
- price: built.params.price ?? 0,
290
- amount: built.params.amount,
291
- status: 'open',
292
- filled: 0,
293
- remaining: built.params.amount,
294
- timestamp: Date.now(),
295
- };
296
- }
292
+ return await this.fetchOrder(newId);
297
293
  }
298
294
  async createOrder(params) {
299
295
  const built = await this.buildOrder(params);
@@ -274,11 +274,12 @@ class PolymarketUSNormalizer {
274
274
  * needing the short-side view must invert prices themselves.
275
275
  */
276
276
  normalizeOrderBook(book, _marketId) {
277
- const bids = (book.bids || []).map(level => ({
277
+ // Missing bids/offers is a valid state (no liquidity), not broken data
278
+ const bids = (book.bids ?? []).map(level => ({
278
279
  price: (0, price_1.fromAmount)(level.px),
279
280
  size: parseFloat(level.qty || '0'),
280
281
  }));
281
- const asks = (book.offers || []).map(level => ({
282
+ const asks = (book.offers ?? []).map(level => ({
282
283
  price: (0, price_1.fromAmount)(level.px),
283
284
  size: parseFloat(level.qty || '0'),
284
285
  }));
@@ -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-04-14T08:11:59.617Z
3
+ * Generated at: 2026-04-14T11:53:52.838Z
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-04-14T08:11:59.617Z
6
+ * Generated at: 2026-04-14T11:53:52.838Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -57,30 +57,21 @@ export interface ProbableRawTrade {
57
57
  id?: string | number;
58
58
  tradeId?: string | number;
59
59
  time?: number;
60
- timestamp?: number;
61
60
  price?: string | number;
62
- qty?: string | number;
63
61
  size?: string | number;
64
- amount?: string | number;
65
62
  side?: string;
66
63
  orderId?: string;
67
64
  [key: string]: unknown;
68
65
  }
69
66
  export interface ProbableRawPosition {
70
- conditionId?: string;
71
67
  condition_id?: string;
72
- asset?: string;
73
68
  token_id?: string;
74
69
  outcome?: string;
75
70
  title?: string;
76
71
  size?: string | number;
77
- avgPrice?: string | number;
78
72
  avg_price?: string | number;
79
- curPrice?: string | number;
80
73
  cur_price?: string | number;
81
- cashPnl?: string | number;
82
74
  cash_pnl?: string | number;
83
- realizedPnl?: string | number;
84
75
  realized_pnl?: string | number;
85
76
  [key: string]: unknown;
86
77
  }
@@ -109,7 +109,14 @@ class ProbableFetcher {
109
109
  if (params.end)
110
110
  queryParams.endTs = Math.floor(params.end.getTime() / 1000);
111
111
  const data = await this.ctx.callApi('getPublicApiV1PricesHistory', queryParams);
112
- return data?.history || data || [];
112
+ if (data != null && typeof data === 'object' && !Array.isArray(data) && Array.isArray(data.history)) {
113
+ return data.history;
114
+ }
115
+ if (Array.isArray(data)) {
116
+ return data;
117
+ }
118
+ throw new Error(`Probable OHLCV: unexpected response shape (got ${typeof data}). ` +
119
+ `Expected { history: [...] } or an array.`);
113
120
  }
114
121
  // -----------------------------------------------------------------------
115
122
  // Trades
@@ -122,23 +129,30 @@ class ProbableFetcher {
122
129
  // client's getTrades through callApi or directly. For now, this goes
123
130
  // through the implicit API.
124
131
  const data = await this.ctx.callApi('getPublicApiV1Trades', queryParams);
125
- const trades = Array.isArray(data) ? data : (data?.data || []);
126
- return trades;
132
+ if (!Array.isArray(data)) {
133
+ throw new Error(`Probable trades: unexpected response shape (got ${typeof data}). Expected an array.`);
134
+ }
135
+ return data;
127
136
  }
128
137
  async fetchRawMyTrades(params, walletAddress) {
129
138
  const queryParams = { user: walletAddress };
130
139
  if (params?.limit)
131
140
  queryParams.limit = params.limit;
132
141
  const data = await this.ctx.callApi('getPublicApiV1Trades', queryParams);
133
- const trades = Array.isArray(data) ? data : (data?.data || []);
134
- return trades;
142
+ if (!Array.isArray(data)) {
143
+ throw new Error(`Probable myTrades: unexpected response shape (got ${typeof data}). Expected an array.`);
144
+ }
145
+ return data;
135
146
  }
136
147
  // -----------------------------------------------------------------------
137
148
  // Positions & Balance
138
149
  // -----------------------------------------------------------------------
139
150
  async fetchRawPositions(walletAddress) {
140
151
  const result = await this.ctx.callApi('getPublicApiV1PositionCurrent', { user: walletAddress, limit: 500 });
141
- return Array.isArray(result) ? result : (result?.data || []);
152
+ if (!Array.isArray(result)) {
153
+ throw new Error(`Probable positions: unexpected response shape (got ${typeof result}). Expected an array.`);
154
+ }
155
+ return result;
142
156
  }
143
157
  // -----------------------------------------------------------------------
144
158
  // Midpoint (price enrichment)
@@ -183,7 +197,10 @@ class ProbableFetcher {
183
197
  const response = await this.ctx.http.get(`${utils_1.BASE_URL}${utils_1.MARKETS_PATH}`, {
184
198
  params: { page: 1, limit: 100, active: true },
185
199
  });
186
- const markets = response.data?.markets || [];
200
+ if (!response.data?.markets || !Array.isArray(response.data.markets)) {
201
+ throw new Error(`Probable markets list: unexpected response shape. Expected { markets: [...] }.`);
202
+ }
203
+ const markets = response.data.markets;
187
204
  return markets.filter(m => String(m.id) === cleanSlug);
188
205
  }
189
206
  throw error;
@@ -215,7 +232,10 @@ class ProbableFetcher {
215
232
  queryParams.event_id = params.eventId;
216
233
  }
217
234
  const response = await this.ctx.http.get(`${utils_1.BASE_URL}${utils_1.MARKETS_PATH}`, { params: queryParams });
218
- return response.data?.markets || [];
235
+ if (!response.data?.markets || !Array.isArray(response.data.markets)) {
236
+ throw new Error(`Probable markets list: unexpected response shape. Expected { markets: [...] }.`);
237
+ }
238
+ return response.data.markets;
219
239
  }
220
240
  async fetchRawMarketsViaSearch(query, params) {
221
241
  const limit = params?.limit || 20;
@@ -264,7 +284,10 @@ class ProbableFetcher {
264
284
  }
265
285
  }
266
286
  const searchData = await this.ctx.callApi('getPublicApiV1PublicSearch', queryParams);
267
- const events = searchData?.events || [];
287
+ if (!searchData?.events || !Array.isArray(searchData.events)) {
288
+ throw new Error(`Probable search: unexpected response shape. Expected { events: [...] }.`);
289
+ }
290
+ const events = searchData.events;
268
291
  const rawMarkets = [];
269
292
  for (const event of events) {
270
293
  if (event.markets && Array.isArray(event.markets)) {
@@ -300,8 +323,10 @@ class ProbableFetcher {
300
323
  queryParams.ascending = false;
301
324
  const response = await this.ctx.http.get(`${utils_1.BASE_URL}${utils_1.EVENTS_PATH}`, { params: queryParams });
302
325
  const data = response.data;
303
- // API returns either a raw array or { events: [...] }
304
- return Array.isArray(data) ? data : (data?.events || []);
326
+ if (!Array.isArray(data)) {
327
+ throw new Error(`Probable events list: unexpected response shape (got ${typeof data}). Expected an array.`);
328
+ }
329
+ return data;
305
330
  }
306
331
  async fetchRawEventsViaSearch(params) {
307
332
  const limit = params.limit || 20;
@@ -314,7 +339,10 @@ class ProbableFetcher {
314
339
  keep_closed_markets: params.status === 'all' || params.status === 'inactive' || params.status === 'closed' ? 1 : 0,
315
340
  };
316
341
  const searchData = await this.ctx.callApi('getPublicApiV1PublicSearch', queryParams);
317
- return searchData?.events || [];
342
+ if (!searchData?.events || !Array.isArray(searchData.events)) {
343
+ throw new Error(`Probable event search: unexpected response shape. Expected { events: [...] }.`);
344
+ }
345
+ return searchData.events;
318
346
  }
319
347
  }
320
348
  exports.ProbableFetcher = ProbableFetcher;
@@ -65,40 +65,72 @@ class ProbableNormalizer {
65
65
  return candles;
66
66
  }
67
67
  normalizeTrade(raw, index) {
68
+ if (raw.id == null && raw.tradeId == null) {
69
+ throw new Error(`Probable trade at index ${index}: missing required field "id" or "tradeId".`);
70
+ }
71
+ if (raw.price == null) {
72
+ throw new Error(`Probable trade at index ${index}: missing required field "price".`);
73
+ }
74
+ if (raw.size == null) {
75
+ throw new Error(`Probable trade at index ${index}: missing required field "size".`);
76
+ }
77
+ if (raw.time == null) {
78
+ throw new Error(`Probable trade at index ${index}: missing required field "time".`);
79
+ }
68
80
  return {
69
- id: String(raw.id || raw.tradeId || `${raw.time}-${raw.price}`),
70
- timestamp: typeof raw.time === 'number'
71
- ? (raw.time < 1e12 ? raw.time * 1000 : raw.time)
72
- : Date.now(),
73
- price: parseFloat(String(raw.price || '0')),
74
- amount: parseFloat(String(raw.qty || raw.size || raw.amount || '0')),
81
+ id: String(raw.id ?? raw.tradeId),
82
+ timestamp: raw.time < 1e12 ? raw.time * 1000 : raw.time,
83
+ price: parseFloat(String(raw.price)),
84
+ amount: parseFloat(String(raw.size)),
75
85
  side: raw.side === 'BUY' ? 'buy'
76
86
  : raw.side === 'SELL' ? 'sell'
77
87
  : 'unknown',
78
88
  };
79
89
  }
80
90
  normalizeUserTrade(raw, index) {
91
+ if (raw.tradeId == null && raw.id == null) {
92
+ throw new Error(`Probable user trade at index ${index}: missing required field "tradeId" or "id".`);
93
+ }
94
+ if (raw.price == null) {
95
+ throw new Error(`Probable user trade at index ${index}: missing required field "price".`);
96
+ }
97
+ if (raw.size == null) {
98
+ throw new Error(`Probable user trade at index ${index}: missing required field "size".`);
99
+ }
100
+ if (raw.time == null) {
101
+ throw new Error(`Probable user trade at index ${index}: missing required field "time".`);
102
+ }
103
+ if (raw.side == null) {
104
+ throw new Error(`Probable user trade at index ${index}: missing required field "side".`);
105
+ }
81
106
  return {
82
- id: String(raw.tradeId || raw.id || raw.timestamp),
83
- timestamp: typeof raw.time === 'number'
84
- ? (raw.time > 1e12 ? raw.time : raw.time * 1000)
85
- : Date.now(),
86
- price: parseFloat(String(raw.price || '0')),
87
- amount: parseFloat(String(raw.qty || raw.size || raw.amount || '0')),
88
- side: (String(raw.side || '')).toLowerCase() === 'buy' ? 'buy' : 'sell',
107
+ id: String(raw.tradeId ?? raw.id),
108
+ timestamp: raw.time > 1e12 ? raw.time : raw.time * 1000,
109
+ price: parseFloat(String(raw.price)),
110
+ amount: parseFloat(String(raw.size)),
111
+ side: String(raw.side).toLowerCase() === 'buy' ? 'buy' : 'sell',
89
112
  orderId: raw.orderId,
90
113
  };
91
114
  }
92
115
  normalizePosition(raw) {
116
+ if (raw.condition_id == null) {
117
+ throw new Error(`Probable position: missing required field "condition_id".`);
118
+ }
119
+ if (raw.token_id == null) {
120
+ throw new Error(`Probable position: missing required field "token_id".`);
121
+ }
122
+ if (raw.size == null) {
123
+ throw new Error(`Probable position: missing required field "size".`);
124
+ }
93
125
  return {
94
- marketId: String(raw.conditionId || raw.condition_id || ''),
95
- outcomeId: String(raw.asset || raw.token_id || ''),
126
+ marketId: String(raw.condition_id),
127
+ outcomeId: String(raw.token_id),
96
128
  outcomeLabel: raw.outcome || raw.title || 'Unknown',
97
- size: parseFloat(String(raw.size || '0')),
98
- entryPrice: parseFloat(String(raw.avgPrice || raw.avg_price || '0')),
99
- currentPrice: parseFloat(String(raw.curPrice || raw.cur_price || '0')),
100
- unrealizedPnL: parseFloat(String(raw.cashPnl || raw.cash_pnl || '0')),
101
- realizedPnL: parseFloat(String(raw.realizedPnl || raw.realized_pnl || '0')),
129
+ size: parseFloat(String(raw.size)),
130
+ entryPrice: parseFloat(String(raw.avg_price ?? '0')),
131
+ currentPrice: parseFloat(String(raw.cur_price ?? '0')),
132
+ unrealizedPnL: parseFloat(String(raw.cash_pnl ?? '0')),
133
+ realizedPnL: parseFloat(String(raw.realized_pnl ?? '0')),
102
134
  };
103
135
  }
104
136
  // -- Price enrichment helper (used by SDK class) --
@@ -61,6 +61,13 @@ const METHOD_VERBS = loadMethodVerbs();
61
61
  function coerceQueryValue(raw, kind) {
62
62
  if (Array.isArray(raw))
63
63
  return raw.map((v) => coerceQueryValue(v, kind));
64
+ if (typeof raw === "object" && raw !== null && !Array.isArray(raw)) {
65
+ const result = {};
66
+ for (const [k, v] of Object.entries(raw)) {
67
+ result[k] = coerceQueryValue(v);
68
+ }
69
+ return result;
70
+ }
64
71
  if (typeof raw !== "string")
65
72
  return raw;
66
73
  if (kind === "string")
@@ -86,6 +93,12 @@ function coerceQueryValue(raw, kind) {
86
93
  return false;
87
94
  if (raw === "null")
88
95
  return null;
96
+ if (raw.startsWith("{") || raw.startsWith("[")) {
97
+ try {
98
+ return JSON.parse(raw);
99
+ }
100
+ catch { /* fall through */ }
101
+ }
89
102
  if (/^-?\d+$/.test(raw))
90
103
  return parseInt(raw, 10);
91
104
  if (/^-?\d*\.\d+$/.test(raw))
@@ -191,6 +191,11 @@ paths:
191
191
  required: false
192
192
  schema:
193
193
  type: string
194
+ - in: query
195
+ name: filter
196
+ required: false
197
+ schema:
198
+ $ref: '#/components/schemas/MarketFilterCriteria'
194
199
  responses:
195
200
  '200':
196
201
  description: Fetch Markets Paginated response
@@ -276,6 +281,27 @@ paths:
276
281
  schema:
277
282
  type: string
278
283
  description: Lookup by event slug
284
+ - in: query
285
+ name: filter
286
+ required: false
287
+ schema:
288
+ allOf:
289
+ - $ref: '#/components/schemas/EventFilterCriteria'
290
+ description: Optional client-side filter applied after fetching
291
+ - in: query
292
+ name: category
293
+ required: false
294
+ schema:
295
+ type: string
296
+ description: Shorthand for filter.category -- merged into filter (takes precedence)
297
+ - in: query
298
+ name: tags
299
+ required: false
300
+ schema:
301
+ type: array
302
+ items:
303
+ type: string
304
+ description: Shorthand for filter.tags -- merged into filter (takes precedence)
279
305
  responses:
280
306
  '200':
281
307
  description: Fetch Events response
@@ -466,6 +492,27 @@ paths:
466
492
  schema:
467
493
  type: string
468
494
  description: Lookup by event slug
495
+ - in: query
496
+ name: filter
497
+ required: false
498
+ schema:
499
+ allOf:
500
+ - $ref: '#/components/schemas/EventFilterCriteria'
501
+ description: Optional client-side filter applied after fetching
502
+ - in: query
503
+ name: category
504
+ required: false
505
+ schema:
506
+ type: string
507
+ description: Shorthand for filter.category -- merged into filter (takes precedence)
508
+ - in: query
509
+ name: tags
510
+ required: false
511
+ schema:
512
+ type: array
513
+ items:
514
+ type: string
515
+ description: Shorthand for filter.tags -- merged into filter (takes precedence)
469
516
  responses:
470
517
  '200':
471
518
  description: Fetch Event response
@@ -1131,95 +1178,7 @@ paths:
1131
1178
  items:
1132
1179
  $ref: '#/components/schemas/UnifiedMarket'
1133
1180
  - type: string
1134
- - type: object
1135
- properties:
1136
- text:
1137
- type: string
1138
- searchIn:
1139
- type: array
1140
- items:
1141
- type: string
1142
- enum:
1143
- - title
1144
- - description
1145
- - category
1146
- - tags
1147
- - outcomes
1148
- volume24h:
1149
- type: object
1150
- properties:
1151
- min:
1152
- type: number
1153
- max:
1154
- type: number
1155
- volume:
1156
- type: object
1157
- properties:
1158
- min:
1159
- type: number
1160
- max:
1161
- type: number
1162
- liquidity:
1163
- type: object
1164
- properties:
1165
- min:
1166
- type: number
1167
- max:
1168
- type: number
1169
- openInterest:
1170
- type: object
1171
- properties:
1172
- min:
1173
- type: number
1174
- max:
1175
- type: number
1176
- resolutionDate:
1177
- type: object
1178
- properties:
1179
- before:
1180
- type: string
1181
- format: date-time
1182
- after:
1183
- type: string
1184
- format: date-time
1185
- category:
1186
- type: string
1187
- tags:
1188
- type: array
1189
- items:
1190
- type: string
1191
- price:
1192
- type: object
1193
- properties:
1194
- outcome:
1195
- type: string
1196
- enum:
1197
- - 'yes'
1198
- - 'no'
1199
- - up
1200
- - down
1201
- min:
1202
- type: number
1203
- max:
1204
- type: number
1205
- required:
1206
- - outcome
1207
- priceChange24h:
1208
- type: object
1209
- properties:
1210
- outcome:
1211
- type: string
1212
- enum:
1213
- - 'yes'
1214
- - 'no'
1215
- - up
1216
- - down
1217
- min:
1218
- type: number
1219
- max:
1220
- type: number
1221
- required:
1222
- - outcome
1181
+ - $ref: '#/components/schemas/MarketFilterCriteria'
1223
1182
  - type: object
1224
1183
  credentials:
1225
1184
  $ref: '#/components/schemas/ExchangeCredentials'
@@ -1265,39 +1224,7 @@ paths:
1265
1224
  items:
1266
1225
  $ref: '#/components/schemas/UnifiedEvent'
1267
1226
  - type: string
1268
- - type: object
1269
- properties:
1270
- text:
1271
- type: string
1272
- searchIn:
1273
- type: array
1274
- items:
1275
- type: string
1276
- enum:
1277
- - title
1278
- - description
1279
- - category
1280
- - tags
1281
- category:
1282
- type: string
1283
- tags:
1284
- type: array
1285
- items:
1286
- type: string
1287
- marketCount:
1288
- type: object
1289
- properties:
1290
- min:
1291
- type: number
1292
- max:
1293
- type: number
1294
- totalVolume:
1295
- type: object
1296
- properties:
1297
- min:
1298
- type: number
1299
- max:
1300
- type: number
1227
+ - $ref: '#/components/schemas/EventFilterCriteria'
1301
1228
  - type: object
1302
1229
  credentials:
1303
1230
  $ref: '#/components/schemas/ExchangeCredentials'
@@ -2111,6 +2038,18 @@ components:
2111
2038
  slug:
2112
2039
  type: string
2113
2040
  description: Lookup by event slug
2041
+ filter:
2042
+ allOf:
2043
+ - $ref: '#/components/schemas/EventFilterCriteria'
2044
+ description: Optional client-side filter applied after fetching
2045
+ category:
2046
+ type: string
2047
+ description: Shorthand for filter.category -- merged into filter (takes precedence)
2048
+ tags:
2049
+ type: array
2050
+ items:
2051
+ type: string
2052
+ description: Shorthand for filter.tags -- merged into filter (takes precedence)
2114
2053
  HistoryFilterParams:
2115
2054
  type: object
2116
2055
  description: Deprecated - use OHLCVParams or TradesParams instead. Resolution is optional for backward compatibility.
@@ -2301,6 +2240,138 @@ components:
2301
2240
  cursor:
2302
2241
  type: string
2303
2242
  description: Opaque pagination cursor from a previous response
2243
+ MarketFilterCriteria:
2244
+ type: object
2245
+ properties:
2246
+ text:
2247
+ type: string
2248
+ searchIn:
2249
+ type: array
2250
+ items:
2251
+ type: string
2252
+ enum:
2253
+ - title
2254
+ - description
2255
+ - category
2256
+ - tags
2257
+ - outcomes
2258
+ description: 'Default: [''title'']'
2259
+ volume24h:
2260
+ type: object
2261
+ properties:
2262
+ min:
2263
+ type: number
2264
+ max:
2265
+ type: number
2266
+ volume:
2267
+ type: object
2268
+ properties:
2269
+ min:
2270
+ type: number
2271
+ max:
2272
+ type: number
2273
+ description: Filter by total (lifetime) volume range
2274
+ liquidity:
2275
+ type: object
2276
+ properties:
2277
+ min:
2278
+ type: number
2279
+ max:
2280
+ type: number
2281
+ description: Filter by current liquidity range
2282
+ openInterest:
2283
+ type: object
2284
+ properties:
2285
+ min:
2286
+ type: number
2287
+ max:
2288
+ type: number
2289
+ description: Filter by open interest range
2290
+ resolutionDate:
2291
+ type: object
2292
+ properties:
2293
+ before:
2294
+ type: string
2295
+ format: date-time
2296
+ after:
2297
+ type: string
2298
+ format: date-time
2299
+ category:
2300
+ type: string
2301
+ tags:
2302
+ type: array
2303
+ items:
2304
+ type: string
2305
+ description: Match if market has ANY of these tags
2306
+ price:
2307
+ type: object
2308
+ properties:
2309
+ outcome:
2310
+ type: string
2311
+ enum:
2312
+ - 'yes'
2313
+ - 'no'
2314
+ - up
2315
+ - down
2316
+ min:
2317
+ type: number
2318
+ max:
2319
+ type: number
2320
+ required:
2321
+ - outcome
2322
+ priceChange24h:
2323
+ type: object
2324
+ properties:
2325
+ outcome:
2326
+ type: string
2327
+ enum:
2328
+ - 'yes'
2329
+ - 'no'
2330
+ - up
2331
+ - down
2332
+ min:
2333
+ type: number
2334
+ max:
2335
+ type: number
2336
+ required:
2337
+ - outcome
2338
+ EventFilterCriteria:
2339
+ type: object
2340
+ properties:
2341
+ text:
2342
+ type: string
2343
+ searchIn:
2344
+ type: array
2345
+ items:
2346
+ type: string
2347
+ enum:
2348
+ - title
2349
+ - description
2350
+ - category
2351
+ - tags
2352
+ description: 'Default: [''title'']'
2353
+ category:
2354
+ type: string
2355
+ tags:
2356
+ type: array
2357
+ items:
2358
+ type: string
2359
+ description: Match events that have any of these tags
2360
+ marketCount:
2361
+ type: object
2362
+ properties:
2363
+ min:
2364
+ type: number
2365
+ max:
2366
+ type: number
2367
+ totalVolume:
2368
+ type: object
2369
+ properties:
2370
+ min:
2371
+ type: number
2372
+ max:
2373
+ type: number
2374
+ description: Sum of market volumes
2304
2375
  ExchangeCredentials:
2305
2376
  type: object
2306
2377
  description: Optional authentication credentials for exchange operations.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.30.8",
3
+ "version": "2.31.0",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "test": "jest -c jest.config.js",
30
30
  "server": "tsx watch src/server/index.ts",
31
31
  "server:prod": "node dist/server/index.js",
32
- "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.30.8,library=urllib3",
33
- "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.30.8,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
32
+ "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.31.0,library=urllib3",
33
+ "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.31.0,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
34
34
  "fetch:openapi": "node scripts/fetch-openapi-specs.js",
35
35
  "extract:jsdoc": "node ../scripts/extract-jsdoc.js",
36
36
  "generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",