pmxt-core 2.47.0 → 2.48.1
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/dist/BaseExchange.d.ts +42 -1
- package/dist/BaseExchange.js +29 -2
- package/dist/exchanges/baozi/index.d.ts +1 -0
- package/dist/exchanges/baozi/index.js +5 -0
- package/dist/exchanges/gemini-titan/index.d.ts +6 -2
- package/dist/exchanges/gemini-titan/index.js +81 -1
- package/dist/exchanges/gemini-titan/normalizer.d.ts +7 -1
- package/dist/exchanges/gemini-titan/normalizer.js +23 -0
- package/dist/exchanges/hyperliquid/index.d.ts +3 -0
- package/dist/exchanges/hyperliquid/index.js +7 -0
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +1 -1
- package/dist/exchanges/kalshi/fetcher.d.ts +10 -0
- package/dist/exchanges/kalshi/fetcher.js +36 -0
- package/dist/exchanges/kalshi/index.d.ts +3 -2
- package/dist/exchanges/kalshi/index.js +33 -0
- package/dist/exchanges/kalshi/normalizer.d.ts +3 -2
- package/dist/exchanges/kalshi/normalizer.js +14 -0
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/index.d.ts +1 -0
- package/dist/exchanges/limitless/index.js +5 -0
- package/dist/exchanges/metaculus/fetchEvents.js +4 -0
- package/dist/exchanges/metaculus/index.d.ts +3 -0
- package/dist/exchanges/metaculus/index.js +3 -0
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +1 -1
- package/dist/exchanges/myriad/index.d.ts +1 -0
- package/dist/exchanges/myriad/index.js +5 -0
- package/dist/exchanges/opinion/api.d.ts +1 -1
- package/dist/exchanges/opinion/api.js +1 -1
- package/dist/exchanges/opinion/index.d.ts +6 -2
- package/dist/exchanges/opinion/index.js +54 -1
- package/dist/exchanges/opinion/normalizer.d.ts +8 -2
- package/dist/exchanges/opinion/normalizer.js +16 -0
- 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/index.d.ts +13 -2
- package/dist/exchanges/polymarket/index.js +49 -0
- package/dist/exchanges/polymarket/normalizer.d.ts +2 -1
- package/dist/exchanges/polymarket/normalizer.js +33 -0
- package/dist/exchanges/polymarket_us/index.d.ts +3 -2
- package/dist/exchanges/polymarket_us/index.js +57 -0
- package/dist/exchanges/polymarket_us/normalizer.d.ts +9 -2
- package/dist/exchanges/polymarket_us/normalizer.js +27 -0
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/index.d.ts +3 -0
- package/dist/exchanges/probable/index.js +7 -0
- package/dist/exchanges/smarkets/index.d.ts +1 -0
- package/dist/exchanges/smarkets/index.js +5 -0
- package/dist/exchanges/suibets/index.d.ts +1 -0
- package/dist/exchanges/suibets/index.js +5 -0
- package/dist/router/Router.d.ts +29 -2
- package/dist/router/Router.js +145 -0
- package/dist/router/index.d.ts +1 -0
- package/dist/router/index.js +1 -0
- package/dist/router/series-map.d.ts +32 -0
- package/dist/router/series-map.js +146 -0
- package/dist/server/app.js +20 -3
- package/dist/server/method-verbs.json +10 -0
- package/dist/server/openapi.yaml +106 -0
- package/dist/types.d.ts +31 -0
- package/package.json +3 -3
|
@@ -2,6 +2,9 @@ import { PredictionMarketExchange, MarketFetchParams, EventFetchParams, Exchange
|
|
|
2
2
|
import { UnifiedMarket, UnifiedEvent, OrderBook, PriceCandle, Trade, UserTrade, Order, Position, Balance, CreateOrderParams } from '../../types';
|
|
3
3
|
import { ProbableWebSocketConfig } from './websocket';
|
|
4
4
|
export declare class ProbableExchange extends PredictionMarketExchange {
|
|
5
|
+
protected readonly capabilityOverrides: {
|
|
6
|
+
fetchSeries: false;
|
|
7
|
+
};
|
|
5
8
|
private auth?;
|
|
6
9
|
private ws?;
|
|
7
10
|
private wsConfig?;
|
|
@@ -16,6 +16,9 @@ const normalizer_1 = require("./normalizer");
|
|
|
16
16
|
const logger_1 = require("../../utils/logger");
|
|
17
17
|
const BSC_USDT_ADDRESS = '0x55d398326f99059fF775485246999027B3197955';
|
|
18
18
|
class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
19
|
+
capabilityOverrides = {
|
|
20
|
+
fetchSeries: false,
|
|
21
|
+
};
|
|
19
22
|
auth;
|
|
20
23
|
ws;
|
|
21
24
|
wsConfig;
|
|
@@ -78,6 +81,10 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
78
81
|
return filtered;
|
|
79
82
|
}
|
|
80
83
|
async fetchEventsImpl(params) {
|
|
84
|
+
// Venue does not expose a series concept; honoring `params.series` by
|
|
85
|
+
// returning [] rather than ignoring the filter.
|
|
86
|
+
if (params.series !== undefined)
|
|
87
|
+
return [];
|
|
81
88
|
const rawEvents = await this.fetcher.fetchRawEvents(params);
|
|
82
89
|
const events = rawEvents
|
|
83
90
|
.map((raw) => this.normalizer.normalizeEvent(raw))
|
|
@@ -3,6 +3,7 @@ import { UnifiedMarket, UnifiedEvent, OrderBook, Trade, UserTrade, Balance, Orde
|
|
|
3
3
|
export declare class SmarketsExchange extends PredictionMarketExchange {
|
|
4
4
|
protected readonly capabilityOverrides: {
|
|
5
5
|
fetchPositions: "emulated";
|
|
6
|
+
fetchSeries: false;
|
|
6
7
|
};
|
|
7
8
|
private auth?;
|
|
8
9
|
private loginPromise;
|
|
@@ -16,6 +16,7 @@ const logger_1 = require("../../utils/logger");
|
|
|
16
16
|
class SmarketsExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
17
17
|
capabilityOverrides = {
|
|
18
18
|
fetchPositions: 'emulated',
|
|
19
|
+
fetchSeries: false,
|
|
19
20
|
};
|
|
20
21
|
auth;
|
|
21
22
|
loginPromise = null;
|
|
@@ -149,6 +150,10 @@ class SmarketsExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
149
150
|
return allMarkets.slice(offset, offset + limit);
|
|
150
151
|
}
|
|
151
152
|
async fetchEventsImpl(params) {
|
|
153
|
+
// Venue does not expose a series concept; honoring `params.series` by
|
|
154
|
+
// returning [] rather than ignoring the filter.
|
|
155
|
+
if (params.series !== undefined)
|
|
156
|
+
return [];
|
|
152
157
|
const rawEvents = await this.fetcher.fetchRawEvents(params);
|
|
153
158
|
const limit = params?.limit || 250000;
|
|
154
159
|
const query = (params?.query || '').toLowerCase();
|
|
@@ -34,6 +34,7 @@ class SuiBetsExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
34
34
|
fetchPositions: true,
|
|
35
35
|
watchOrderBook: false,
|
|
36
36
|
watchTrades: false,
|
|
37
|
+
fetchSeries: false,
|
|
37
38
|
};
|
|
38
39
|
config;
|
|
39
40
|
fetcher;
|
|
@@ -72,6 +73,10 @@ class SuiBetsExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
72
73
|
.filter((m) => m !== null);
|
|
73
74
|
}
|
|
74
75
|
async fetchEventsImpl(params) {
|
|
76
|
+
// Venue does not expose a series concept; honoring `params.series` by returning [] rather than ignoring the filter.
|
|
77
|
+
if (params.series !== undefined) {
|
|
78
|
+
return [];
|
|
79
|
+
}
|
|
75
80
|
const raw = await this.fetcher.fetchRawEvents(params);
|
|
76
81
|
return raw
|
|
77
82
|
.map(r => this.normalizer.normalizeEvent(r))
|
package/dist/router/Router.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { PredictionMarketExchange, type MarketFetchParams, type EventFetchParams } from '../BaseExchange';
|
|
2
|
-
import type { UnifiedMarket, UnifiedEvent, OrderBook } from '../types';
|
|
1
|
+
import { PredictionMarketExchange, type MarketFetchParams, type EventFetchParams, type SeriesFetchParams } from '../BaseExchange';
|
|
2
|
+
import type { UnifiedMarket, UnifiedEvent, UnifiedSeries, OrderBook } from '../types';
|
|
3
3
|
import type { RouterOptions, MatchResult, EventMatchResult, PriceComparison, ArbitrageOpportunity, MatchedMarketPair, MatchedPricePair, FetchMarketMatchesParams, FetchMatchesParams, FetchEventMatchesParams, FetchArbitrageParams, FetchMatchedMarketsParams, FetchMatchedPricesParams } from './types';
|
|
4
4
|
export declare class Router extends PredictionMarketExchange {
|
|
5
5
|
private readonly client;
|
|
@@ -9,6 +9,33 @@ export declare class Router extends PredictionMarketExchange {
|
|
|
9
9
|
get name(): string;
|
|
10
10
|
protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
|
|
11
11
|
protected fetchEventsImpl(params?: EventFetchParams): Promise<UnifiedEvent[]>;
|
|
12
|
+
/**
|
|
13
|
+
* Fan out fetchEvents({series: venueSeriesId}) to each venue in the
|
|
14
|
+
* mapping, collect all results, and tag each event with its sourceExchange.
|
|
15
|
+
*/
|
|
16
|
+
private fetchEventsForMappedSeries;
|
|
17
|
+
/**
|
|
18
|
+
* Cross-venue series fetch.
|
|
19
|
+
*
|
|
20
|
+
* - If `params.id` matches a normalized id in SERIES_MAP, returns a single
|
|
21
|
+
* synthesized `UnifiedSeries` whose `events` is the concatenation of
|
|
22
|
+
* `fetchEvents({series: venueSeriesId})` results from each mapped venue.
|
|
23
|
+
* - Otherwise fans out `fetchSeries(params)` to all venue instances with
|
|
24
|
+
* `has.fetchSeries !== false`, collects, and returns deduplicated results
|
|
25
|
+
* tagged with their `sourceExchange`.
|
|
26
|
+
*/
|
|
27
|
+
protected fetchSeriesImpl(params: SeriesFetchParams): Promise<UnifiedSeries[]>;
|
|
28
|
+
/**
|
|
29
|
+
* Build a single synthesized `UnifiedSeries` from the SERIES_MAP entry by
|
|
30
|
+
* fetching events from all mapped venues and concatenating them.
|
|
31
|
+
*/
|
|
32
|
+
private fetchSynthesizedSeries;
|
|
33
|
+
/**
|
|
34
|
+
* Fan out `fetchSeries(params)` to all venue instances whose
|
|
35
|
+
* `has.fetchSeries` is not `false`. Tag each result with its originating
|
|
36
|
+
* venue name.
|
|
37
|
+
*/
|
|
38
|
+
private fanOutFetchSeries;
|
|
12
39
|
fetchOrderBook(outcomeId: string, limit?: number, params?: Record<string, any>): Promise<OrderBook>;
|
|
13
40
|
fetchMarketMatches(params?: FetchMarketMatchesParams): Promise<MatchResult[]>;
|
|
14
41
|
/**
|
package/dist/router/Router.js
CHANGED
|
@@ -5,6 +5,7 @@ const BaseExchange_1 = require("../BaseExchange");
|
|
|
5
5
|
const errors_1 = require("../errors");
|
|
6
6
|
const logger_1 = require("../utils/logger");
|
|
7
7
|
const client_1 = require("./client");
|
|
8
|
+
const series_map_1 = require("./series-map");
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
9
10
|
// Orderbook merge utilities
|
|
10
11
|
// ---------------------------------------------------------------------------
|
|
@@ -127,6 +128,18 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
|
|
|
127
128
|
return response;
|
|
128
129
|
}
|
|
129
130
|
async fetchEventsImpl(params) {
|
|
131
|
+
// When a normalized series id is requested, fan out to each mapped venue
|
|
132
|
+
// using the venue-native series id and aggregate the results.
|
|
133
|
+
if (params?.series !== undefined) {
|
|
134
|
+
const normalized = params.series;
|
|
135
|
+
const entry = series_map_1.SERIES_MAP.find((e) => e.id === normalized);
|
|
136
|
+
if (entry !== undefined) {
|
|
137
|
+
return this.fetchEventsForMappedSeries(entry.venues, params);
|
|
138
|
+
}
|
|
139
|
+
// Not a known normalized id — fall through and pass the raw value to
|
|
140
|
+
// the hosted search endpoint (single-venue callers using vendor-native
|
|
141
|
+
// ids still work).
|
|
142
|
+
}
|
|
130
143
|
const response = await this.client.searchEvents({
|
|
131
144
|
query: params?.query,
|
|
132
145
|
category: params?.category,
|
|
@@ -138,6 +151,138 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
|
|
|
138
151
|
}
|
|
139
152
|
return response;
|
|
140
153
|
}
|
|
154
|
+
/**
|
|
155
|
+
* Fan out fetchEvents({series: venueSeriesId}) to each venue in the
|
|
156
|
+
* mapping, collect all results, and tag each event with its sourceExchange.
|
|
157
|
+
*/
|
|
158
|
+
async fetchEventsForMappedSeries(venueMap, baseParams) {
|
|
159
|
+
const venueEntries = Object.entries(venueMap);
|
|
160
|
+
if (venueEntries.length === 0)
|
|
161
|
+
return [];
|
|
162
|
+
const fetchResults = await Promise.all(venueEntries.map(async ([venueName, venueSeriesId]) => {
|
|
163
|
+
const exchange = this.localExchanges[venueName];
|
|
164
|
+
if (!exchange) {
|
|
165
|
+
logger_1.logger.debug(`Router.fetchEventsForMappedSeries: no exchange instance for venue "${venueName}", skipping`);
|
|
166
|
+
return [];
|
|
167
|
+
}
|
|
168
|
+
try {
|
|
169
|
+
const events = await exchange.fetchEvents({
|
|
170
|
+
...baseParams,
|
|
171
|
+
series: venueSeriesId,
|
|
172
|
+
});
|
|
173
|
+
return events.map((ev) => ({
|
|
174
|
+
...ev,
|
|
175
|
+
sourceExchange: ev.sourceExchange ?? venueName,
|
|
176
|
+
}));
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
logger_1.logger.warn(`Router.fetchEventsForMappedSeries: fetchEvents failed for venue "${venueName}" ` +
|
|
180
|
+
`series "${venueSeriesId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
181
|
+
return [];
|
|
182
|
+
}
|
|
183
|
+
}));
|
|
184
|
+
return fetchResults.flat();
|
|
185
|
+
}
|
|
186
|
+
// -----------------------------------------------------------------------
|
|
187
|
+
// Cross-venue series (series-map + fan-out)
|
|
188
|
+
// -----------------------------------------------------------------------
|
|
189
|
+
/**
|
|
190
|
+
* Cross-venue series fetch.
|
|
191
|
+
*
|
|
192
|
+
* - If `params.id` matches a normalized id in SERIES_MAP, returns a single
|
|
193
|
+
* synthesized `UnifiedSeries` whose `events` is the concatenation of
|
|
194
|
+
* `fetchEvents({series: venueSeriesId})` results from each mapped venue.
|
|
195
|
+
* - Otherwise fans out `fetchSeries(params)` to all venue instances with
|
|
196
|
+
* `has.fetchSeries !== false`, collects, and returns deduplicated results
|
|
197
|
+
* tagged with their `sourceExchange`.
|
|
198
|
+
*/
|
|
199
|
+
async fetchSeriesImpl(params) {
|
|
200
|
+
const requestedId = params.id;
|
|
201
|
+
if (requestedId !== undefined) {
|
|
202
|
+
const entry = series_map_1.SERIES_MAP.find((e) => e.id === requestedId);
|
|
203
|
+
if (entry !== undefined) {
|
|
204
|
+
return this.fetchSynthesizedSeries(entry, params);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
return this.fanOutFetchSeries(params);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Build a single synthesized `UnifiedSeries` from the SERIES_MAP entry by
|
|
211
|
+
* fetching events from all mapped venues and concatenating them.
|
|
212
|
+
*/
|
|
213
|
+
async fetchSynthesizedSeries(entry, params) {
|
|
214
|
+
const venueEntries = Object.entries(entry.venues);
|
|
215
|
+
const eventsPerVenue = await Promise.all(venueEntries.map(async ([venueName, venueSeriesId]) => {
|
|
216
|
+
const exchange = this.localExchanges[venueName];
|
|
217
|
+
if (!exchange) {
|
|
218
|
+
logger_1.logger.debug(`Router.fetchSynthesizedSeries: no exchange instance for venue "${venueName}", skipping`);
|
|
219
|
+
return [];
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const events = await exchange.fetchEvents({ series: venueSeriesId });
|
|
223
|
+
return events.map((ev) => ({
|
|
224
|
+
...ev,
|
|
225
|
+
sourceExchange: ev.sourceExchange ?? venueName,
|
|
226
|
+
}));
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
logger_1.logger.warn(`Router.fetchSynthesizedSeries: fetchEvents failed for venue "${venueName}" ` +
|
|
230
|
+
`series "${venueSeriesId}": ${error instanceof Error ? error.message : String(error)}`);
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
}));
|
|
234
|
+
const allEvents = eventsPerVenue.flat();
|
|
235
|
+
const synthesized = {
|
|
236
|
+
id: entry.id,
|
|
237
|
+
title: entry.title,
|
|
238
|
+
events: allEvents,
|
|
239
|
+
sourceExchange: 'Router',
|
|
240
|
+
};
|
|
241
|
+
// Apply limit/offset if the caller passed them (BaseExchange.fetchSeries strips
|
|
242
|
+
// them before calling fetchSeriesImpl, but guard defensively).
|
|
243
|
+
const limit = params.limit;
|
|
244
|
+
const offset = params.offset ?? 0;
|
|
245
|
+
if (limit !== undefined) {
|
|
246
|
+
return [{ ...synthesized, events: allEvents.slice(offset, offset + limit) }];
|
|
247
|
+
}
|
|
248
|
+
return [synthesized];
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Fan out `fetchSeries(params)` to all venue instances whose
|
|
252
|
+
* `has.fetchSeries` is not `false`. Tag each result with its originating
|
|
253
|
+
* venue name.
|
|
254
|
+
*/
|
|
255
|
+
async fanOutFetchSeries(params) {
|
|
256
|
+
const venueEntries = Object.entries(this.localExchanges);
|
|
257
|
+
if (venueEntries.length === 0)
|
|
258
|
+
return [];
|
|
259
|
+
const fetchResults = await Promise.all(venueEntries.map(async ([venueName, exchange]) => {
|
|
260
|
+
if (exchange.has.fetchSeries === false)
|
|
261
|
+
return [];
|
|
262
|
+
// When params.id is a venue-native id on this specific venue,
|
|
263
|
+
// pass it through directly so single-venue lookups still work.
|
|
264
|
+
let venueParams = params;
|
|
265
|
+
if (params.id !== undefined) {
|
|
266
|
+
const nativeId = (0, series_map_1.getVenueSeriesId)(params.id, venueName);
|
|
267
|
+
if (nativeId !== undefined) {
|
|
268
|
+
venueParams = { ...params, id: nativeId };
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
try {
|
|
272
|
+
const series = await exchange.fetchSeries(venueParams);
|
|
273
|
+
return series.map((s) => ({
|
|
274
|
+
...s,
|
|
275
|
+
sourceExchange: s.sourceExchange ?? venueName,
|
|
276
|
+
}));
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
logger_1.logger.warn(`Router.fanOutFetchSeries: fetchSeries failed for venue "${venueName}": ` +
|
|
280
|
+
`${error instanceof Error ? error.message : String(error)}`);
|
|
281
|
+
return [];
|
|
282
|
+
}
|
|
283
|
+
}));
|
|
284
|
+
return fetchResults.flat();
|
|
285
|
+
}
|
|
141
286
|
// -----------------------------------------------------------------------
|
|
142
287
|
// Unified orderbook (cross-exchange merge)
|
|
143
288
|
// -----------------------------------------------------------------------
|
package/dist/router/index.d.ts
CHANGED
package/dist/router/index.js
CHANGED
|
@@ -20,3 +20,4 @@ Object.defineProperty(exports, "Router", { enumerable: true, get: function () {
|
|
|
20
20
|
var client_1 = require("./client");
|
|
21
21
|
Object.defineProperty(exports, "PmxtApiClient", { enumerable: true, get: function () { return client_1.PmxtApiClient; } });
|
|
22
22
|
__exportStar(require("./types"), exports);
|
|
23
|
+
__exportStar(require("./series-map"), exports);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Curated mapping from normalized PMXT series ids to per-venue native series ids.
|
|
3
|
+
*
|
|
4
|
+
* The normalized ids use kebab-case following the pattern:
|
|
5
|
+
* <sport-or-domain>-<category-or-format>
|
|
6
|
+
*
|
|
7
|
+
* Venue-native ids are the raw tickers/slugs each platform uses.
|
|
8
|
+
* It is intentional for some entries to have only partial venue coverage --
|
|
9
|
+
* the Router handles missing venue mappings gracefully by skipping that venue.
|
|
10
|
+
*/
|
|
11
|
+
export interface RouterSeriesEntry {
|
|
12
|
+
/** Normalized PMXT series id (kebab-case). */
|
|
13
|
+
id: string;
|
|
14
|
+
/** Human-readable title. */
|
|
15
|
+
title: string;
|
|
16
|
+
/** Map of venueName -> venue-native series id. */
|
|
17
|
+
venues: {
|
|
18
|
+
readonly [venueName: string]: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export declare const SERIES_MAP: readonly RouterSeriesEntry[];
|
|
22
|
+
/**
|
|
23
|
+
* Resolve a normalized PMXT series id to the venue-native series id for a
|
|
24
|
+
* given venue. Returns `undefined` when either the normalized id is not in the
|
|
25
|
+
* map or that venue has no mapping for it.
|
|
26
|
+
*/
|
|
27
|
+
export declare function getVenueSeriesId(normalizedId: string, venue: string): string | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* Reverse-lookup: given a venue name and a venue-native series id, return the
|
|
30
|
+
* normalized PMXT series id. Returns `undefined` when not found.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getNormalizedSeriesId(venue: string, venueSeriesId: string): string | undefined;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Curated mapping from normalized PMXT series ids to per-venue native series ids.
|
|
4
|
+
*
|
|
5
|
+
* The normalized ids use kebab-case following the pattern:
|
|
6
|
+
* <sport-or-domain>-<category-or-format>
|
|
7
|
+
*
|
|
8
|
+
* Venue-native ids are the raw tickers/slugs each platform uses.
|
|
9
|
+
* It is intentional for some entries to have only partial venue coverage --
|
|
10
|
+
* the Router handles missing venue mappings gracefully by skipping that venue.
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SERIES_MAP = void 0;
|
|
14
|
+
exports.getVenueSeriesId = getVenueSeriesId;
|
|
15
|
+
exports.getNormalizedSeriesId = getNormalizedSeriesId;
|
|
16
|
+
exports.SERIES_MAP = [
|
|
17
|
+
{
|
|
18
|
+
id: 'tennis-atp-match',
|
|
19
|
+
title: 'ATP Match Winner',
|
|
20
|
+
venues: {
|
|
21
|
+
kalshi: 'KXATPSETWINNER',
|
|
22
|
+
polymarket: 'atp',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
id: 'tennis-atp-challenger',
|
|
27
|
+
title: 'ATP Challenger Match Winner',
|
|
28
|
+
venues: {
|
|
29
|
+
kalshi: 'KXATPCHALLENGERMATCH',
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
id: 'tennis-wta-match',
|
|
34
|
+
title: 'WTA Match Winner',
|
|
35
|
+
venues: {
|
|
36
|
+
kalshi: 'KXWTASETWINNER',
|
|
37
|
+
polymarket: 'wta',
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
id: 'tennis-itf-match',
|
|
42
|
+
title: 'ITF Match Winner',
|
|
43
|
+
venues: {
|
|
44
|
+
kalshi: 'KXITFMATCH',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
id: 'tennis-itf-women-match',
|
|
49
|
+
title: "ITF Women's Match Winner",
|
|
50
|
+
venues: {
|
|
51
|
+
kalshi: 'KXITFWMATCH',
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'nfl',
|
|
56
|
+
title: 'NFL Game Winner',
|
|
57
|
+
venues: {
|
|
58
|
+
kalshi: 'KXNFLGAME',
|
|
59
|
+
polymarket: 'nfl-game',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'nba',
|
|
64
|
+
title: 'NBA Game Winner',
|
|
65
|
+
venues: {
|
|
66
|
+
kalshi: 'KXNBAGAME',
|
|
67
|
+
polymarket: 'nba',
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: 'ncaa-basketball',
|
|
72
|
+
title: 'NCAA Basketball Game Winner',
|
|
73
|
+
venues: {
|
|
74
|
+
kalshi: 'KXNCAABBGAME',
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
id: 'ufc',
|
|
79
|
+
title: 'UFC Fight Winner',
|
|
80
|
+
venues: {
|
|
81
|
+
polymarket: 'ufc',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
id: 'soccer-fifa-world-cup',
|
|
86
|
+
title: 'FIFA World Cup Match Winner',
|
|
87
|
+
venues: {
|
|
88
|
+
polymarket: 'soccer-fifwc',
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'esports-cs2-map',
|
|
93
|
+
title: 'CS2 Map Winner',
|
|
94
|
+
venues: {
|
|
95
|
+
kalshi: 'KXCS2MAP',
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'esports-lol-map',
|
|
100
|
+
title: 'League of Legends Map Winner',
|
|
101
|
+
venues: {
|
|
102
|
+
kalshi: 'KXLOLMAP',
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'crypto-btc-15m',
|
|
107
|
+
title: 'Bitcoin Price (15-minute)',
|
|
108
|
+
venues: {
|
|
109
|
+
kalshi: 'KXBTC15M',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'crypto-eth-15m',
|
|
114
|
+
title: 'Ethereum Price (15-minute)',
|
|
115
|
+
venues: {
|
|
116
|
+
kalshi: 'KXETH15M',
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: 'crypto-sol-15m',
|
|
121
|
+
title: 'Solana Price (15-minute)',
|
|
122
|
+
venues: {
|
|
123
|
+
kalshi: 'KXSOL15M',
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
];
|
|
127
|
+
// ---------------------------------------------------------------------------
|
|
128
|
+
// Lookup helpers (O(n) over small constant array; acceptable for this table)
|
|
129
|
+
// ---------------------------------------------------------------------------
|
|
130
|
+
/**
|
|
131
|
+
* Resolve a normalized PMXT series id to the venue-native series id for a
|
|
132
|
+
* given venue. Returns `undefined` when either the normalized id is not in the
|
|
133
|
+
* map or that venue has no mapping for it.
|
|
134
|
+
*/
|
|
135
|
+
function getVenueSeriesId(normalizedId, venue) {
|
|
136
|
+
const entry = exports.SERIES_MAP.find((e) => e.id === normalizedId);
|
|
137
|
+
return entry?.venues[venue];
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Reverse-lookup: given a venue name and a venue-native series id, return the
|
|
141
|
+
* normalized PMXT series id. Returns `undefined` when not found.
|
|
142
|
+
*/
|
|
143
|
+
function getNormalizedSeriesId(venue, venueSeriesId) {
|
|
144
|
+
const entry = exports.SERIES_MAP.find((e) => e.venues[venue] === venueSeriesId);
|
|
145
|
+
return entry?.id;
|
|
146
|
+
}
|
package/dist/server/app.js
CHANGED
|
@@ -384,13 +384,30 @@ function createApp(options = {}) {
|
|
|
384
384
|
});
|
|
385
385
|
// POST /api/:exchange/:method
|
|
386
386
|
//
|
|
387
|
-
//
|
|
387
|
+
// Supports two calling conventions:
|
|
388
|
+
// - Envelope: { args: [...], credentials? } — original RPC shape, used by SDKs
|
|
389
|
+
// - Flat body: { slug: "wta", limit: 3, ... } — raw-curl / documentation examples
|
|
390
|
+
//
|
|
391
|
+
// When `args` is a valid array it is used directly (envelope path).
|
|
392
|
+
// When the body is a plain object without an `args` array, the body minus
|
|
393
|
+
// the reserved envelope keys (`args`, `credentials`) becomes args[0].
|
|
388
394
|
// Accepts every method, including reads — so pre-existing clients
|
|
389
395
|
// that POST reads keep working forever.
|
|
390
396
|
app.post("/api/:exchange/:method", async (req, res, next) => {
|
|
391
397
|
const methodName = req.params.method;
|
|
392
|
-
const
|
|
393
|
-
const credentials =
|
|
398
|
+
const body = req.body;
|
|
399
|
+
const credentials = body.credentials;
|
|
400
|
+
let args;
|
|
401
|
+
if (Array.isArray(body.args)) {
|
|
402
|
+
args = body.args;
|
|
403
|
+
}
|
|
404
|
+
else if (body && typeof body === 'object' && !Array.isArray(body)) {
|
|
405
|
+
const { args: _ignored, credentials: _creds, ...rest } = body;
|
|
406
|
+
args = Object.keys(rest).length > 0 ? [rest] : [];
|
|
407
|
+
}
|
|
408
|
+
else {
|
|
409
|
+
args = [];
|
|
410
|
+
}
|
|
394
411
|
await dispatchMethod(req, res, next, methodName, args, credentials);
|
|
395
412
|
});
|
|
396
413
|
// Error handler
|
package/dist/server/openapi.yaml
CHANGED
|
@@ -326,6 +326,15 @@ paths:
|
|
|
326
326
|
schema:
|
|
327
327
|
type: string
|
|
328
328
|
description: Lookup by event slug
|
|
329
|
+
- in: query
|
|
330
|
+
name: series
|
|
331
|
+
required: false
|
|
332
|
+
schema:
|
|
333
|
+
type: string
|
|
334
|
+
description: >-
|
|
335
|
+
Filter events by their parent series. Accepts the venue-native series id / ticker / slug (e.g. Kalshi
|
|
336
|
+
`"KXATPMATCH"`, Polymarket `"wta"`). Passed through to the vendor where supported, otherwise applied to
|
|
337
|
+
`sourceMetadata` after fetch.
|
|
329
338
|
- in: query
|
|
330
339
|
name: filter
|
|
331
340
|
required: false
|
|
@@ -369,6 +378,31 @@ paths:
|
|
|
369
378
|
description: >-
|
|
370
379
|
Fetch events with optional keyword search. Events group related markets together (e.g., "Who will be Fed Chair?"
|
|
371
380
|
contains multiple candidate markets).
|
|
381
|
+
'/api/{exchange}/fetchSeries':
|
|
382
|
+
get:
|
|
383
|
+
summary: Fetch Series
|
|
384
|
+
operationId: fetchSeries
|
|
385
|
+
parameters:
|
|
386
|
+
- $ref: '#/components/parameters/ExchangeParam'
|
|
387
|
+
responses:
|
|
388
|
+
'200':
|
|
389
|
+
description: Fetch Series response
|
|
390
|
+
content:
|
|
391
|
+
application/json:
|
|
392
|
+
schema:
|
|
393
|
+
allOf:
|
|
394
|
+
- $ref: '#/components/schemas/BaseResponse'
|
|
395
|
+
- type: object
|
|
396
|
+
properties:
|
|
397
|
+
data:
|
|
398
|
+
type: array
|
|
399
|
+
items:
|
|
400
|
+
$ref: '#/components/schemas/UnifiedSeries'
|
|
401
|
+
description: >-
|
|
402
|
+
Fetch the recurring series (fourth tier above Event -> Market -> Outcome) that this venue exposes. Returns an
|
|
403
|
+
empty array on venues without a series concept (Limitless, Smarkets, Probable, Metaculus, Baozi, Hyperliquid,
|
|
404
|
+
SuiBets, Polymarket US). - `params.id` -> a single matching series with its events populated where supported. -
|
|
405
|
+
no params -> the full list, typically without nested events for payload size.
|
|
372
406
|
'/api/{exchange}/fetchMarket':
|
|
373
407
|
get:
|
|
374
408
|
summary: Fetch Market
|
|
@@ -548,6 +582,15 @@ paths:
|
|
|
548
582
|
schema:
|
|
549
583
|
type: string
|
|
550
584
|
description: Lookup by event slug
|
|
585
|
+
- in: query
|
|
586
|
+
name: series
|
|
587
|
+
required: false
|
|
588
|
+
schema:
|
|
589
|
+
type: string
|
|
590
|
+
description: >-
|
|
591
|
+
Filter events by their parent series. Accepts the venue-native series id / ticker / slug (e.g. Kalshi
|
|
592
|
+
`"KXATPMATCH"`, Polymarket `"wta"`). Passed through to the vendor where supported, otherwise applied to
|
|
593
|
+
`sourceMetadata` after fetch.
|
|
551
594
|
- in: query
|
|
552
595
|
name: filter
|
|
553
596
|
required: false
|
|
@@ -2780,6 +2823,63 @@ components:
|
|
|
2780
2823
|
- markets
|
|
2781
2824
|
- volume24h
|
|
2782
2825
|
- url
|
|
2826
|
+
UnifiedSeries:
|
|
2827
|
+
type: object
|
|
2828
|
+
description: >-
|
|
2829
|
+
A recurring grouping of events on a venue — the fourth tier above Event -> Market -> Outcome. Examples: Kalshi
|
|
2830
|
+
`KXATPMATCH` (every ATP tennis match), Polymarket `wta` (every WTA match), Opinion's daily `collection`. Series
|
|
2831
|
+
only exists where the venue exposes a recurring-event concept; venues without one return an empty array from
|
|
2832
|
+
`fetchSeries`.
|
|
2833
|
+
properties:
|
|
2834
|
+
id:
|
|
2835
|
+
type: string
|
|
2836
|
+
description: >-
|
|
2837
|
+
Stable venue-native series identifier (e.g. "KXATPMATCH" on Kalshi, "atp" on Polymarket Gamma, numeric Gamma
|
|
2838
|
+
id).
|
|
2839
|
+
ticker:
|
|
2840
|
+
type: string
|
|
2841
|
+
description: 'Venue-native ticker, when distinct from `id`.'
|
|
2842
|
+
slug:
|
|
2843
|
+
type: string
|
|
2844
|
+
description: Venue-native slug.
|
|
2845
|
+
title:
|
|
2846
|
+
type: string
|
|
2847
|
+
description: 'Human-readable series title (e.g. "ATP Match Winner", "WTA").'
|
|
2848
|
+
description:
|
|
2849
|
+
oneOf:
|
|
2850
|
+
- type: string
|
|
2851
|
+
- {}
|
|
2852
|
+
description: Long-form series description.
|
|
2853
|
+
recurrence:
|
|
2854
|
+
oneOf:
|
|
2855
|
+
- type: string
|
|
2856
|
+
- {}
|
|
2857
|
+
description: 'Recurrence cadence the venue reports (''daily'', ''weekly'', ''annual'', ...).'
|
|
2858
|
+
events:
|
|
2859
|
+
type: array
|
|
2860
|
+
items:
|
|
2861
|
+
$ref: '#/components/schemas/UnifiedEvent'
|
|
2862
|
+
description: Child events. Populated when fetched by id; the list form usually omits this to keep payloads small.
|
|
2863
|
+
url:
|
|
2864
|
+
oneOf:
|
|
2865
|
+
- type: string
|
|
2866
|
+
- {}
|
|
2867
|
+
description: Canonical venue URL for the series.
|
|
2868
|
+
image:
|
|
2869
|
+
oneOf:
|
|
2870
|
+
- type: string
|
|
2871
|
+
- {}
|
|
2872
|
+
description: Venue-hosted image.
|
|
2873
|
+
sourceExchange:
|
|
2874
|
+
type: string
|
|
2875
|
+
description: The exchange this series originates from. Populated by the Router.
|
|
2876
|
+
sourceMetadata:
|
|
2877
|
+
type: object
|
|
2878
|
+
additionalProperties: {}
|
|
2879
|
+
description: Raw venue-specific fields not promoted to first-class columns.
|
|
2880
|
+
required:
|
|
2881
|
+
- id
|
|
2882
|
+
- title
|
|
2783
2883
|
PriceCandle:
|
|
2784
2884
|
type: object
|
|
2785
2885
|
properties:
|
|
@@ -3173,6 +3273,12 @@ components:
|
|
|
3173
3273
|
slug:
|
|
3174
3274
|
type: string
|
|
3175
3275
|
description: Lookup by event slug
|
|
3276
|
+
series:
|
|
3277
|
+
type: string
|
|
3278
|
+
description: >-
|
|
3279
|
+
Filter events by their parent series. Accepts the venue-native series id / ticker / slug (e.g. Kalshi
|
|
3280
|
+
`"KXATPMATCH"`, Polymarket `"wta"`). Passed through to the vendor where supported, otherwise applied to
|
|
3281
|
+
`sourceMetadata` after fetch.
|
|
3176
3282
|
filter:
|
|
3177
3283
|
allOf:
|
|
3178
3284
|
- $ref: '#/components/schemas/EventFilterCriteria'
|