pmxt-core 2.46.14 → 2.48.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/baozi/normalizer.js +2 -0
- package/dist/exchanges/baozi/utils.js +28 -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 +54 -0
- package/dist/exchanges/hyperliquid/index.d.ts +3 -0
- package/dist/exchanges/hyperliquid/index.js +7 -0
- package/dist/exchanges/hyperliquid/normalizer.js +20 -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 +35 -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/limitless/normalizer.js +10 -0
- package/dist/exchanges/limitless/utils.js +17 -0
- package/dist/exchanges/metaculus/fetchEvents.js +6 -0
- package/dist/exchanges/metaculus/index.d.ts +3 -0
- package/dist/exchanges/metaculus/index.js +3 -0
- package/dist/exchanges/metaculus/utils.d.ts +2 -1
- package/dist/exchanges/metaculus/utils.js +32 -3
- 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/myriad/normalizer.js +14 -0
- package/dist/exchanges/myriad/utils.js +14 -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 +42 -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 +48 -0
- package/dist/exchanges/polymarket/utils.js +28 -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 +42 -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/probable/utils.js +17 -0
- package/dist/exchanges/smarkets/index.d.ts +1 -0
- package/dist/exchanges/smarkets/index.js +5 -0
- package/dist/exchanges/smarkets/normalizer.js +20 -0
- package/dist/exchanges/suibets/index.d.ts +1 -0
- package/dist/exchanges/suibets/index.js +5 -0
- package/dist/exchanges/suibets/normalizer.js +20 -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/method-verbs.json +10 -0
- package/dist/server/openapi.yaml +132 -0
- package/dist/types.d.ts +35 -0
- package/dist/utils/metadata.d.ts +14 -0
- package/dist/utils/metadata.js +33 -0
- package/package.json +3 -3
|
@@ -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();
|
|
@@ -2,7 +2,20 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SmarketsNormalizer = void 0;
|
|
4
4
|
const market_utils_1 = require("../../utils/market-utils");
|
|
5
|
+
const metadata_1 = require("../../utils/metadata");
|
|
5
6
|
const price_1 = require("./price");
|
|
7
|
+
// Raw Smarkets event fields already promoted to first-class Unified columns —
|
|
8
|
+
// excluded from sourceMetadata so we capture only vendor data not in the
|
|
9
|
+
// unified shape.
|
|
10
|
+
const SMARKETS_PROMOTED_EVENT_KEYS = [
|
|
11
|
+
'id', 'name', 'description', 'slug', 'full_slug',
|
|
12
|
+
'start_datetime', 'end_date',
|
|
13
|
+
];
|
|
14
|
+
// Raw Smarkets market fields already promoted to first-class Unified columns.
|
|
15
|
+
const SMARKETS_PROMOTED_MARKET_KEYS = [
|
|
16
|
+
'id', 'event_id', 'name', 'slug', 'description',
|
|
17
|
+
'category', 'categories',
|
|
18
|
+
];
|
|
6
19
|
// ----------------------------------------------------------------------------
|
|
7
20
|
// Helpers
|
|
8
21
|
// ----------------------------------------------------------------------------
|
|
@@ -94,6 +107,9 @@ class SmarketsNormalizer {
|
|
|
94
107
|
url: buildEventUrl(event),
|
|
95
108
|
category,
|
|
96
109
|
tags,
|
|
110
|
+
// event_id is promoted to eventId; parent_id lives on the raw event
|
|
111
|
+
// (not a recurring/series market field), so no extra is needed.
|
|
112
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, SMARKETS_PROMOTED_MARKET_KEYS),
|
|
97
113
|
};
|
|
98
114
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
99
115
|
return um;
|
|
@@ -116,6 +132,10 @@ class SmarketsNormalizer {
|
|
|
116
132
|
url: buildEventUrl(raw.event),
|
|
117
133
|
category,
|
|
118
134
|
tags: category ? [category] : [],
|
|
135
|
+
// Captures non-promoted event fields: state, type, parent_id,
|
|
136
|
+
// start_date, created, modified, bettable, hidden, inplay_enabled,
|
|
137
|
+
// short_name, seo_description, special_rules, chart_time_period, etc.
|
|
138
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw.event, SMARKETS_PROMOTED_EVENT_KEYS),
|
|
119
139
|
};
|
|
120
140
|
}
|
|
121
141
|
normalizeOrderBook(raw, _id) {
|
|
@@ -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))
|
|
@@ -1,7 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SuibetsNormalizer = void 0;
|
|
4
|
+
const metadata_1 = require("../../utils/metadata");
|
|
4
5
|
const utils_1 = require("./utils");
|
|
6
|
+
// Raw SuiBets offer fields already promoted to first-class UnifiedMarket columns.
|
|
7
|
+
// Omit these from sourceMetadata to capture only vendor-specific data not
|
|
8
|
+
// represented by the unified shape.
|
|
9
|
+
const SUIBETS_PROMOTED_OFFER_KEYS = [
|
|
10
|
+
'id', 'matchId', 'matchName', 'homeTeam', 'awayTeam',
|
|
11
|
+
'creatorOdds', 'creatorStake', 'remainingStake', 'totalMatched',
|
|
12
|
+
'matchDate', 'expiresAt', 'status', 'onchainOfferId',
|
|
13
|
+
'leagueName', 'sport', 'isOnchain',
|
|
14
|
+
];
|
|
15
|
+
// Raw SuiBets event fields already promoted to first-class UnifiedEvent columns.
|
|
16
|
+
const SUIBETS_PROMOTED_EVENT_KEYS = [
|
|
17
|
+
'id', 'name', 'homeTeam', 'awayTeam', 'sport', 'leagueName', 'offers',
|
|
18
|
+
];
|
|
5
19
|
function liquidity(offer) {
|
|
6
20
|
const remaining = offer.remainingStake ?? offer.creatorStake;
|
|
7
21
|
return (0, utils_1.mistToSui)(remaining);
|
|
@@ -57,6 +71,9 @@ class SuibetsNormalizer {
|
|
|
57
71
|
contractAddress: raw.onchainOfferId,
|
|
58
72
|
yes: creatorOutcome,
|
|
59
73
|
no: takerOutcome,
|
|
74
|
+
// Retains creatorWallet, creatorTeam, takerStake, currency \u2014 fields
|
|
75
|
+
// that are vendor-specific and not promoted to any unified column.
|
|
76
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_OFFER_KEYS),
|
|
60
77
|
};
|
|
61
78
|
return market;
|
|
62
79
|
}
|
|
@@ -84,6 +101,9 @@ class SuibetsNormalizer {
|
|
|
84
101
|
url: 'https://suibets.replit.app/p2p',
|
|
85
102
|
category: 'Sports',
|
|
86
103
|
tags: ['Sports', 'P2P', 'Sui', raw.sport, raw.leagueName].filter((t) => Boolean(t)),
|
|
104
|
+
// Retains matchDate and status \u2014 event-level fields not promoted to
|
|
105
|
+
// any first-class UnifiedEvent column.
|
|
106
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, SUIBETS_PROMOTED_EVENT_KEYS),
|
|
87
107
|
};
|
|
88
108
|
}
|
|
89
109
|
normalizePosition(raw) {
|
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
|
+
}
|