pmxt-core 2.51.1 → 2.51.3

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 (46) hide show
  1. package/API_REFERENCE.md +17 -28
  2. package/bin/pmxt-ensure-server +22 -1
  3. package/bin/pmxt-ensure-server.js +64 -3
  4. package/dist/BaseExchange.d.ts +34 -0
  5. package/dist/BaseExchange.js +44 -2
  6. package/dist/exchanges/hunch/fetcher.d.ts +4 -0
  7. package/dist/exchanges/hunch/fetcher.js +19 -6
  8. package/dist/exchanges/hunch/utils.d.ts +8 -0
  9. package/dist/exchanges/hunch/utils.js +61 -7
  10. package/dist/exchanges/hyperliquid/normalizer.js +1 -1
  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.js +13 -0
  14. package/dist/exchanges/limitless/api.d.ts +1 -1
  15. package/dist/exchanges/limitless/api.js +1 -1
  16. package/dist/exchanges/limitless/index.js +4 -1
  17. package/dist/exchanges/metaculus/index.js +2 -1
  18. package/dist/exchanges/mock/index.d.ts +1 -1
  19. package/dist/exchanges/mock/index.js +37 -3
  20. package/dist/exchanges/myriad/api.d.ts +1 -1
  21. package/dist/exchanges/myriad/api.js +1 -1
  22. package/dist/exchanges/myriad/index.js +1 -1
  23. package/dist/exchanges/opinion/api.d.ts +1 -1
  24. package/dist/exchanges/opinion/api.js +1 -1
  25. package/dist/exchanges/opinion/index.js +1 -1
  26. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  27. package/dist/exchanges/polymarket/api-clob.js +1 -1
  28. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  29. package/dist/exchanges/polymarket/api-data.js +1 -1
  30. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  31. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  32. package/dist/exchanges/polymarket_us/config.js +4 -2
  33. package/dist/exchanges/probable/api.d.ts +1 -1
  34. package/dist/exchanges/probable/api.js +1 -1
  35. package/dist/exchanges/probable/index.js +1 -1
  36. package/dist/exchanges/rain/fetcher.d.ts +2 -1
  37. package/dist/exchanges/rain/fetcher.js +5 -6
  38. package/dist/exchanges/rain/index.js +5 -1
  39. package/dist/exchanges/smarkets/config.js +2 -1
  40. package/dist/exchanges/smarkets/fetcher.js +16 -6
  41. package/dist/server/app.d.ts +9 -0
  42. package/dist/server/app.js +34 -16
  43. package/dist/server/exchange-factory.js +9 -3
  44. package/dist/server/openapi.yaml +14 -2
  45. package/dist/types.d.ts +2 -0
  46. package/package.json +3 -3
@@ -277,12 +277,25 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
277
277
  const q = params.query.toLowerCase();
278
278
  markets = markets.filter(m => m.title.toLowerCase().includes(q));
279
279
  }
280
+ if (params?.slug) {
281
+ markets = markets.filter(m => m.slug === params.slug);
282
+ }
280
283
  if (params?.eventId) {
281
284
  markets = markets.filter(m => m.eventId === params.eventId);
282
285
  }
283
286
  if (params?.marketId) {
284
287
  markets = markets.filter(m => m.marketId === params.marketId);
285
288
  }
289
+ if (params?.outcomeId) {
290
+ markets = markets.filter(m => m.outcomes.some(o => o.outcomeId === params.outcomeId));
291
+ }
292
+ if (params?.status && params.status !== 'all') {
293
+ markets = markets.filter(m => (m.status ?? 'active') === params.status);
294
+ }
295
+ const sourceExchange = params?.sourceExchange ?? params?.exchange;
296
+ if (sourceExchange) {
297
+ markets = markets.filter(m => m.sourceExchange === sourceExchange);
298
+ }
286
299
  const offset = params?.offset ?? 0;
287
300
  const limit = params?.limit;
288
301
  return limit !== undefined ? markets.slice(offset, offset + limit) : markets.slice(offset);
@@ -296,6 +309,19 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
296
309
  if (params?.eventId) {
297
310
  events = events.filter(e => e.id === params.eventId);
298
311
  }
312
+ if (params?.slug) {
313
+ events = events.filter(e => e.slug === params.slug);
314
+ }
315
+ if (params?.series) {
316
+ events = events.filter(e => e.sourceMetadata?.series === params.series);
317
+ }
318
+ if (params?.status && params.status !== 'all') {
319
+ events = events.filter(e => (e.status ?? 'active') === params.status);
320
+ }
321
+ const sourceExchange = params?.sourceExchange ?? params?.exchange;
322
+ if (sourceExchange) {
323
+ events = events.filter(e => e.sourceExchange === sourceExchange);
324
+ }
299
325
  const offset = params?.offset ?? 0;
300
326
  const limit = params?.limit;
301
327
  return limit !== undefined ? events.slice(offset, offset + limit) : events.slice(offset);
@@ -378,8 +404,15 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
378
404
  outcomeId: id,
379
405
  });
380
406
  }
381
- const sorted = trades.sort((a, b) => b.timestamp - a.timestamp);
382
- return params.limit === undefined ? sorted : sorted.slice(0, Math.max(0, Math.floor(params.limit)));
407
+ const start = toTimestamp(params.start);
408
+ const end = toTimestamp(params.end);
409
+ const filtered = [...trades]
410
+ .sort((a, b) => b.timestamp - a.timestamp)
411
+ .filter((trade) => start === undefined || trade.timestamp >= start)
412
+ .filter((trade) => end === undefined || trade.timestamp <= end);
413
+ return params.limit === undefined
414
+ ? filtered
415
+ : filtered.slice(0, Math.max(0, Math.floor(params.limit)));
383
416
  }
384
417
  async fetchBalance(_address) {
385
418
  const locked = this._locked();
@@ -609,9 +642,10 @@ class MockExchange extends BaseExchange_1.PredictionMarketExchange {
609
642
  throw new Error(`Order not found: ${orderId}`);
610
643
  return { ...o };
611
644
  }
612
- async fetchOpenOrders(_marketId) {
645
+ async fetchOpenOrders(marketId) {
613
646
  return Array.from(this._orders.values())
614
647
  .filter(o => o.status === 'open' || o.status === 'pending')
648
+ .filter(o => marketId === undefined || o.marketId === marketId)
615
649
  .map(o => ({ ...o }));
616
650
  }
617
651
  async fetchMyTrades(_params) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
3
- * Generated at: 2026-06-23T06:11:27.071Z
3
+ * Generated at: 2026-06-24T09:17:49.618Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const myriadApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.myriadApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/myriad/myriad.yaml
6
- * Generated at: 2026-06-23T06:11:27.071Z
6
+ * Generated at: 2026-06-24T09:17:49.618Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.myriadApiSpec = {
@@ -35,7 +35,7 @@ class MyriadExchange extends BaseExchange_1.PredictionMarketExchange {
35
35
  if (credentials?.apiKey || credentials?.privateKey) {
36
36
  this.auth = new auth_1.MyriadAuth(credentials);
37
37
  }
38
- const myriadBaseUrl = credentials?.baseUrl || utils_1.DEFAULT_BASE_URL;
38
+ const myriadBaseUrl = credentials?.baseUrl || process.env.MYRIAD_BASE_URL || utils_1.DEFAULT_BASE_URL;
39
39
  const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.myriadApiSpec, myriadBaseUrl);
40
40
  this.defineImplicitApi(descriptor);
41
41
  const ctx = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
3
- * Generated at: 2026-06-23T06:11:27.074Z
3
+ * Generated at: 2026-06-24T09:17:49.621Z
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-06-23T06:11:27.074Z
6
+ * Generated at: 2026-06-24T09:17:49.621Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -82,7 +82,7 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
82
82
  if (credentials?.apiKey) {
83
83
  this.auth = new auth_1.OpinionAuth(credentials);
84
84
  }
85
- const opinionBaseUrl = credentials?.baseUrl || config_1.DEFAULT_OPINION_API_URL;
85
+ const opinionBaseUrl = credentials?.baseUrl || process.env.OPINION_BASE_URL || config_1.DEFAULT_OPINION_API_URL;
86
86
  const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.opinionApiSpec, opinionBaseUrl);
87
87
  this.defineImplicitApi(descriptor);
88
88
  const ctx = {
@@ -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-06-23T06:11:27.025Z
3
+ * Generated at: 2026-06-24T09:17:49.575Z
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-06-23T06:11:27.025Z
6
+ * Generated at: 2026-06-24T09:17:49.575Z
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-06-23T06:11:27.041Z
3
+ * Generated at: 2026-06-24T09:17:49.588Z
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-06-23T06:11:27.041Z
6
+ * Generated at: 2026-06-24T09:17:49.588Z
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-06-23T06:11:27.039Z
3
+ * Generated at: 2026-06-24T09:17:49.586Z
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-06-23T06:11:27.039Z
6
+ * Generated at: 2026-06-24T09:17:49.586Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -15,8 +15,10 @@ exports.POLYMARKET_US_GATEWAY_BASE_URL = process.env.POLYMARKET_US_GATEWAY_URL |
15
15
  * Return a typed config object for the Polymarket US API.
16
16
  */
17
17
  function getPolymarketUSConfig(baseUrlOverride) {
18
+ const apiUrl = baseUrlOverride || process.env.POLYMARKET_US_BASE_URL || exports.POLYMARKET_US_API_BASE_URL;
19
+ const gatewayUrl = process.env.POLYMARKET_US_GATEWAY_URL || exports.POLYMARKET_US_GATEWAY_BASE_URL;
18
20
  return {
19
- apiUrl: baseUrlOverride || exports.POLYMARKET_US_API_BASE_URL,
20
- gatewayUrl: exports.POLYMARKET_US_GATEWAY_BASE_URL,
21
+ apiUrl,
22
+ gatewayUrl,
21
23
  };
22
24
  }
@@ -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-06-23T06:11:27.066Z
3
+ * Generated at: 2026-06-24T09:17:49.613Z
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-06-23T06:11:27.066Z
6
+ * Generated at: 2026-06-24T09:17:49.613Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -31,7 +31,7 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
31
31
  if (credentials?.privateKey && credentials?.apiKey && credentials?.apiSecret && credentials?.passphrase) {
32
32
  this.auth = new auth_1.ProbableAuth(credentials);
33
33
  }
34
- const probableBaseUrl = credentials?.baseUrl || utils_1.DEFAULT_BASE_URL;
34
+ const probableBaseUrl = credentials?.baseUrl || process.env.PROBABLE_BASE_URL || utils_1.DEFAULT_BASE_URL;
35
35
  const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.probableApiSpec, probableBaseUrl);
36
36
  this.defineImplicitApi(descriptor);
37
37
  const ctx = {
@@ -6,6 +6,7 @@ export interface RainFetcherConfig {
6
6
  subgraphApiKey?: string;
7
7
  rpcUrl?: string;
8
8
  wsRpcUrl?: string;
9
+ sdk?: RainSdk;
9
10
  }
10
11
  export type RainRawMarket = Awaited<ReturnType<RainClient['getPublicMarkets']>>[number];
11
12
  export type RainRawMarketDetails = Awaited<ReturnType<RainClient['getMarketDetails']>>;
@@ -38,7 +39,7 @@ export declare class RainFetcher {
38
39
  status?: string;
39
40
  }): Promise<RainMarketWithDetails[]>;
40
41
  fetchRawMarket(marketId: string): Promise<RainMarketWithDetails | null>;
41
- fetchRawOHLCV(marketId: string, optionIndex: number, interval: string, limit?: number): Promise<RainRawPriceHistory | null>;
42
+ fetchRawOHLCV(marketAddress: string, optionIndex: number, _interval: string, _limit?: number): Promise<RainRawPriceHistory | null>;
42
43
  fetchRawPositions(walletAddress: string): Promise<RainRawPositions>;
43
44
  fetchRawBalance(walletAddress: string, tokenAddresses: string[]): Promise<RainRawBalance>;
44
45
  fetchRawMarketTrades(marketAddress: string, limit?: number): Promise<RainRawMarketTransactions | null>;
@@ -23,7 +23,7 @@ class RainFetcher {
23
23
  }
24
24
  async getClient() {
25
25
  if (!this.client) {
26
- const sdk = await loadSdk();
26
+ const sdk = this.config.sdk ?? await loadSdk();
27
27
  this.client = new sdk.Rain({
28
28
  environment: this.config.environment ?? 'production',
29
29
  rpcUrl: this.config.rpcUrl,
@@ -91,16 +91,15 @@ class RainFetcher {
91
91
  throw errors_1.rainErrorMapper.mapError(error);
92
92
  }
93
93
  }
94
- async fetchRawOHLCV(marketId, optionIndex, interval, limit) {
94
+ async fetchRawOHLCV(marketAddress, optionIndex, _interval, _limit) {
95
95
  if (!this.config.subgraphUrl)
96
96
  return null;
97
97
  try {
98
98
  const client = await this.getClient();
99
99
  return await client.getPriceHistory({
100
- marketId,
101
- optionIndex,
102
- interval: interval,
103
- limit,
100
+ marketAddress: marketAddress,
101
+ interval: _interval,
102
+ option: optionIndex,
104
103
  });
105
104
  }
106
105
  catch (error) {
@@ -96,7 +96,11 @@ class RainExchange extends BaseExchange_1.PredictionMarketExchange {
96
96
  const marketId = parts[1];
97
97
  const choiceIndex = Number(parts[2]);
98
98
  const interval = normalizer_1.RainNormalizer.mapInterval(params.resolution);
99
- const raw = await this.fetcher.fetchRawOHLCV(marketId, choiceIndex, interval, params.limit);
99
+ const market = await this.fetcher.fetchRawMarket(marketId);
100
+ const contractAddress = market?.details?.contractAddress;
101
+ if (!contractAddress)
102
+ return [];
103
+ const raw = await this.fetcher.fetchRawOHLCV(contractAddress, choiceIndex, interval, params.limit);
100
104
  return this.normalizer.normalizeOHLCV(raw, params.limit);
101
105
  }
102
106
  async fetchOrderBook(outcomeId, _limit, _params) {
@@ -42,7 +42,8 @@ exports.SMARKETS_PATHS = {
42
42
  * Return a typed config object for the Smarkets API.
43
43
  */
44
44
  function getSmarketsConfig(baseUrlOverride) {
45
+ const apiUrl = baseUrlOverride || process.env.SMARKETS_BASE_URL || exports.DEFAULT_SMARKETS_BASE_URL;
45
46
  return {
46
- apiUrl: baseUrlOverride || exports.DEFAULT_SMARKETS_BASE_URL,
47
+ apiUrl,
47
48
  };
48
49
  }
@@ -40,12 +40,19 @@ class SmarketsFetcher {
40
40
  if (params.eventId) {
41
41
  return this.fetchEnrichedEventById(params.eventId);
42
42
  }
43
+ if (params.limit !== undefined && params.limit <= 0) {
44
+ return [];
45
+ }
43
46
  const stateFilter = this.mapEventStatus(params?.status || 'active');
47
+ const requestedLimit = params.limit === undefined
48
+ ? undefined
49
+ : Math.max(1, Math.floor(params.limit));
44
50
  const rawEvents = await this.fetchPaginatedEvents({
45
51
  state: stateFilter,
46
52
  type_scope: ['single_event'],
47
53
  with_new_type: true,
48
- });
54
+ ...(requestedLimit === undefined ? {} : { limit: Math.min(requestedLimit, BATCH_SIZE) }),
55
+ }, requestedLimit);
49
56
  return this.enrichEvents(rawEvents);
50
57
  }
51
58
  catch (error) {
@@ -153,11 +160,8 @@ class SmarketsFetcher {
153
160
  const contracts = contractsData.contracts || [];
154
161
  if (contracts.length === 0)
155
162
  return [];
156
- // Step 2: Fetch volumes for this market
157
- const volumeData = await this.ctx.callApi('get_volumes_by_market_ids', {
158
- market_ids: [marketId],
159
- });
160
- const volumes = volumeData.volumes || [];
163
+ // Step 2: Fetch optional authenticated volumes for this market.
164
+ const volumes = await this.fetchVolumesByMarketIds([marketId]);
161
165
  // Step 3: We need the event_id. The contracts have market_id but not event_id.
162
166
  // Use the events/markets endpoint by searching for events that contain this market.
163
167
  // The most reliable approach: fetch all active events and find the one that owns
@@ -201,6 +205,8 @@ class SmarketsFetcher {
201
205
  }];
202
206
  }
203
207
  async fetchAllEnrichedEvents(params) {
208
+ if (params?.limit !== undefined && params.limit <= 0)
209
+ return [];
204
210
  const stateFilter = this.mapEventStatus(params?.status || 'active');
205
211
  const limit = params?.limit || 1000;
206
212
  const rawEvents = await this.fetchPaginatedEvents({
@@ -318,6 +324,10 @@ class SmarketsFetcher {
318
324
  async fetchVolumesByMarketIds(marketIds) {
319
325
  if (marketIds.length === 0)
320
326
  return [];
327
+ const headers = this.ctx.getHeaders();
328
+ const hasAuthHeader = Object.entries(headers).some(([key, value]) => key.toLowerCase() === 'authorization' && String(value).trim().length > 0);
329
+ if (!hasAuthHeader)
330
+ return [];
321
331
  const batches = this.batchArray(marketIds, MARKET_ID_BATCH_SIZE);
322
332
  const results = await Promise.all(batches.map(async (batch) => {
323
333
  try {
@@ -1,6 +1,7 @@
1
1
  import { Express } from "express";
2
2
  import { Server as HttpServer } from "http";
3
3
  import { createWebSocketHandler, CreateWebSocketHandlerOptions } from "./ws-handler";
4
+ import { PredictionMarketExchange } from "../BaseExchange";
4
5
  /**
5
6
  * Options accepted by {@link createApp}.
6
7
  */
@@ -25,6 +26,14 @@ export interface CreateAppOptions {
25
26
  * Defaults to false.
26
27
  */
27
28
  skipBaseMiddleware?: boolean;
29
+ /**
30
+ * Exchange instances scoped to this app.
31
+ *
32
+ * By default, unauthenticated local usage reuses process-wide singleton
33
+ * exchanges. Embedders and tests can provide explicit instances here when
34
+ * they need isolated state or non-default mock exchange options.
35
+ */
36
+ localExchanges?: Partial<Record<string, PredictionMarketExchange>>;
28
37
  }
29
38
  /**
30
39
  * Build an Express app that serves the PMXT sidecar API surface without
@@ -162,6 +162,15 @@ function normalizeDateFields(params, fields, exchange) {
162
162
  }
163
163
  return normalized;
164
164
  }
165
+ function normalizeArrayFields(params, fields) {
166
+ return fields.reduce((normalized, field) => {
167
+ const value = normalized[field];
168
+ if (typeof value === "string") {
169
+ return { ...normalized, [field]: [value] };
170
+ }
171
+ return normalized;
172
+ }, { ...params });
173
+ }
165
174
  function isMissing(value) {
166
175
  return value === undefined || value === null || value === "";
167
176
  }
@@ -200,9 +209,12 @@ function validateCreateOrderParams(params, exchange) {
200
209
  }
201
210
  function normalizeDispatchArgs(methodName, args, exchange) {
202
211
  const normalized = [...args];
203
- if (methodName === "fetchOHLCV" && isRecord(normalized[1])) {
212
+ if ((methodName === "fetchOHLCV" || methodName === "fetchTrades") && isRecord(normalized[1])) {
204
213
  normalized[1] = normalizeDateFields(normalized[1], ["start", "end"], exchange);
205
214
  }
215
+ if ((methodName === "fetchMarkets" || methodName === "fetchEvents") && isRecord(normalized[0])) {
216
+ normalized[0] = normalizeArrayFields(normalized[0], ["tags"]);
217
+ }
206
218
  if (methodName === "createOrder") {
207
219
  normalized[0] = validateCreateOrderParams(normalized[0], exchange);
208
220
  }
@@ -268,8 +280,9 @@ function getDefaultExchange(exchangeName) {
268
280
  * ```
269
281
  */
270
282
  function createApp(options = {}) {
271
- const { accessToken, skipBaseMiddleware = false } = options;
283
+ const { accessToken, skipBaseMiddleware = false, localExchanges = {}, } = options;
272
284
  const app = (0, express_1.default)();
285
+ const getAppExchange = (exchangeName) => (localExchanges[exchangeName] ?? getDefaultExchange(exchangeName));
273
286
  if (!skipBaseMiddleware) {
274
287
  app.use((0, cors_1.default)());
275
288
  app.use(express_1.default.json({ limit: "2mb" }));
@@ -307,16 +320,22 @@ function createApp(options = {}) {
307
320
  const exchangeName = req.params.exchange.toLowerCase();
308
321
  let exchange;
309
322
  if (exchangeName === "router") {
310
- // Router uses the caller's Bearer token for its internal /v0/
311
- // calls — not a server-side env var. Each request may carry a
312
- // different key, so Router is never cached as a singleton.
313
- const bearer = req.headers.authorization?.replace(/^Bearer\s+/i, "") || "";
314
- exchange = new router_1.Router({
315
- apiKey: bearer,
316
- localExchanges: {
317
- mock: getDefaultExchange("mock"),
318
- },
319
- });
323
+ const localRouter = localExchanges[exchangeName];
324
+ if (localRouter) {
325
+ exchange = localRouter;
326
+ }
327
+ else {
328
+ // Router uses the caller's Bearer token for its internal /v0/
329
+ // calls — not a server-side env var. Each request may carry a
330
+ // different key, so Router is never cached as a singleton.
331
+ const bearer = req.headers.authorization?.replace(/^Bearer\s+/i, "") || "";
332
+ exchange = new router_1.Router({
333
+ apiKey: bearer,
334
+ localExchanges: {
335
+ mock: getAppExchange("mock"),
336
+ },
337
+ });
338
+ }
320
339
  }
321
340
  else if (credentials &&
322
341
  (credentials.privateKey ||
@@ -325,7 +344,7 @@ function createApp(options = {}) {
325
344
  exchange = (0, exchange_factory_1.createExchange)(exchangeName, credentials);
326
345
  }
327
346
  else {
328
- exchange = getDefaultExchange(exchangeName);
347
+ exchange = getAppExchange(exchangeName);
329
348
  }
330
349
  if (req.headers["x-pmxt-verbose"] === "true") {
331
350
  exchange.verbose = true;
@@ -340,9 +359,8 @@ function createApp(options = {}) {
340
359
  });
341
360
  return;
342
361
  }
343
- if (exchange.has &&
344
- methodName in exchange.has &&
345
- exchange.has[methodName] === false) {
362
+ const capability = exchange.has?.[methodName];
363
+ if (capability === false && methodName !== "fetchSeries") {
346
364
  res.status(501).json({
347
365
  success: false,
348
366
  error: `Method '${methodName}' is not supported by '${exchangeName}'. ` +
@@ -54,8 +54,12 @@ function createExchange(name, credentials, bearerToken) {
54
54
  case "kalshi-demo":
55
55
  return new kalshi_demo_1.KalshiDemoExchange({
56
56
  credentials: {
57
- apiKey: credentials?.apiKey || process.env.KALSHI_API_KEY,
58
- privateKey: credentials?.privateKey || process.env.KALSHI_PRIVATE_KEY,
57
+ apiKey: credentials?.apiKey ||
58
+ process.env.KALSHI_DEMO_API_KEY ||
59
+ process.env.KALSHI_API_KEY,
60
+ privateKey: credentials?.privateKey ||
61
+ process.env.KALSHI_DEMO_PRIVATE_KEY ||
62
+ process.env.KALSHI_PRIVATE_KEY,
59
63
  },
60
64
  });
61
65
  case "probable":
@@ -80,7 +84,9 @@ function createExchange(name, credentials, bearerToken) {
80
84
  return new opinion_1.OpinionExchange({
81
85
  apiKey: credentials?.apiKey || process.env.OPINION_API_KEY,
82
86
  privateKey: credentials?.privateKey || process.env.OPINION_PRIVATE_KEY,
83
- funderAddress: credentials?.funderAddress,
87
+ funderAddress: credentials?.funderAddress ||
88
+ process.env.OPINION_FUNDER_ADDRESS ||
89
+ process.env.OPINION_PROXY_ADDRESS,
84
90
  });
85
91
  case "metaculus":
86
92
  return new metaculus_1.MetaculusExchange({
@@ -1142,6 +1142,7 @@ paths:
1142
1142
  type: array
1143
1143
  items:
1144
1144
  $ref: '#/components/schemas/UserTrade'
1145
+ description: Fetch authenticated user trade history.
1145
1146
  '/api/{exchange}/fetchClosedOrders':
1146
1147
  get:
1147
1148
  summary: Fetch Closed Orders
@@ -1194,6 +1195,7 @@ paths:
1194
1195
  type: array
1195
1196
  items:
1196
1197
  $ref: '#/components/schemas/Order'
1198
+ description: Fetch authenticated closed orders.
1197
1199
  '/api/{exchange}/fetchAllOrders':
1198
1200
  get:
1199
1201
  summary: Fetch All Orders
@@ -1246,6 +1248,7 @@ paths:
1246
1248
  type: array
1247
1249
  items:
1248
1250
  $ref: '#/components/schemas/Order'
1251
+ description: Fetch authenticated order history across open and closed orders.
1249
1252
  '/api/{exchange}/fetchPositions':
1250
1253
  get:
1251
1254
  summary: Fetch Positions
@@ -3639,6 +3642,15 @@ components:
3639
3642
  price:
3640
3643
  type: number
3641
3644
  description: Required for limit orders
3645
+ denom:
3646
+ type: string
3647
+ enum:
3648
+ - usdc
3649
+ - shares
3650
+ description: 'Hosted mode: amount unit.'
3651
+ slippage_pct:
3652
+ type: number
3653
+ description: 'Hosted mode: maximum market-order slippage percentage.'
3642
3654
  fee:
3643
3655
  type: number
3644
3656
  description: 'Optional fee rate (e.g., 1000 for 0.1%)'
@@ -4488,7 +4500,7 @@ x-sdk-constructors:
4488
4500
  type: string
4489
4501
  description: Private key for authentication
4490
4502
  polymarket_us:
4491
- className: PolymarketUs
4503
+ className: PolymarketUS
4492
4504
  params:
4493
4505
  - name: pmxt_api_key
4494
4506
  tsName: pmxtApiKey
@@ -4533,7 +4545,7 @@ x-sdk-constructors:
4533
4545
  type: string
4534
4546
  description: API secret for authentication
4535
4547
  suibets:
4536
- className: Suibets
4548
+ className: SuiBets
4537
4549
  params:
4538
4550
  - name: pmxt_api_key
4539
4551
  tsName: pmxtApiKey
package/dist/types.d.ts CHANGED
@@ -287,6 +287,8 @@ export interface CreateOrderParams {
287
287
  /** Size of the order in contracts/shares. */
288
288
  amount: number;
289
289
  price?: number;
290
+ denom?: 'usdc' | 'shares';
291
+ slippage_pct?: number;
290
292
  fee?: number;
291
293
  tickSize?: number;
292
294
  negRisk?: boolean;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.51.1",
3
+ "version": "2.51.3",
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.51.1,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.51.1,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.51.3,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.51.3,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",