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.
- package/API_REFERENCE.md +17 -28
- package/bin/pmxt-ensure-server +22 -1
- package/bin/pmxt-ensure-server.js +64 -3
- package/dist/BaseExchange.d.ts +34 -0
- package/dist/BaseExchange.js +44 -2
- package/dist/exchanges/hunch/fetcher.d.ts +4 -0
- package/dist/exchanges/hunch/fetcher.js +19 -6
- package/dist/exchanges/hunch/utils.d.ts +8 -0
- package/dist/exchanges/hunch/utils.js +61 -7
- package/dist/exchanges/hyperliquid/normalizer.js +1 -1
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetcher.js +13 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.js +4 -1
- package/dist/exchanges/metaculus/index.js +2 -1
- package/dist/exchanges/mock/index.d.ts +1 -1
- package/dist/exchanges/mock/index.js +37 -3
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/index.js +1 -1
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/opinion/index.js +1 -1
- package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
- package/dist/exchanges/polymarket/api-clob.js +1 -1
- package/dist/exchanges/polymarket/api-data.d.ts +1 -1
- package/dist/exchanges/polymarket/api-data.js +1 -1
- package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
- package/dist/exchanges/polymarket/api-gamma.js +1 -1
- package/dist/exchanges/polymarket_us/config.js +4 -2
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.js +1 -1
- package/dist/exchanges/rain/fetcher.d.ts +2 -1
- package/dist/exchanges/rain/fetcher.js +5 -6
- package/dist/exchanges/rain/index.js +5 -1
- package/dist/exchanges/smarkets/config.js +2 -1
- package/dist/exchanges/smarkets/fetcher.js +16 -6
- package/dist/server/app.d.ts +9 -0
- package/dist/server/app.js +34 -16
- package/dist/server/exchange-factory.js +9 -3
- package/dist/server/openapi.yaml +14 -2
- package/dist/types.d.ts +2 -0
- 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
|
|
382
|
-
|
|
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(
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
|
20
|
-
gatewayUrl
|
|
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-
|
|
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-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
|
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
|
|
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
|
|
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 {
|
package/dist/server/app.d.ts
CHANGED
|
@@ -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
|
package/dist/server/app.js
CHANGED
|
@@ -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
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
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 =
|
|
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
|
-
|
|
344
|
-
|
|
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 ||
|
|
58
|
-
|
|
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({
|
package/dist/server/openapi.yaml
CHANGED
|
@@ -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:
|
|
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:
|
|
4548
|
+
className: SuiBets
|
|
4537
4549
|
params:
|
|
4538
4550
|
- name: pmxt_api_key
|
|
4539
4551
|
tsName: pmxtApiKey
|
package/dist/types.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmxt-core",
|
|
3
|
-
"version": "2.51.
|
|
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.
|
|
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.
|
|
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",
|