pmxt-core 2.48.6 → 2.49.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/exchanges/gemini-titan/normalizer.js +9 -3
- package/dist/exchanges/gemini-titan/websocket.js +17 -5
- package/dist/exchanges/hyperliquid/normalizer.js +2 -0
- package/dist/exchanges/interfaces.d.ts +2 -2
- package/dist/exchanges/kalshi/api.d.ts +1 -1
- package/dist/exchanges/kalshi/api.js +2 -2
- package/dist/exchanges/kalshi/config.d.ts +6 -6
- package/dist/exchanges/kalshi/config.js +8 -8
- package/dist/exchanges/kalshi/fetcher.d.ts +2 -0
- package/dist/exchanges/kalshi/fetcher.js +6 -7
- package/dist/exchanges/kalshi/normalizer.js +6 -1
- package/dist/exchanges/kalshi/websocket.js +21 -9
- package/dist/exchanges/limitless/api.d.ts +1 -1
- package/dist/exchanges/limitless/api.js +1 -1
- package/dist/exchanges/limitless/client.js +2 -2
- package/dist/exchanges/limitless/config.d.ts +2 -0
- package/dist/exchanges/limitless/config.js +3 -1
- package/dist/exchanges/limitless/fetcher.d.ts +10 -0
- package/dist/exchanges/limitless/fetcher.js +2 -1
- package/dist/exchanges/limitless/index.js +1 -1
- package/dist/exchanges/limitless/utils.js +3 -3
- package/dist/exchanges/limitless/websocket.js +8 -11
- package/dist/exchanges/metaculus/utils.js +0 -1
- package/dist/exchanges/myriad/api.d.ts +1 -1
- package/dist/exchanges/myriad/api.js +17 -2
- package/dist/exchanges/myriad/normalizer.js +10 -5
- package/dist/exchanges/myriad/utils.js +2 -0
- package/dist/exchanges/opinion/api.d.ts +16 -1
- package/dist/exchanges/opinion/api.js +20 -1
- package/dist/exchanges/opinion/websocket.js +21 -14
- package/dist/exchanges/polymarket/api-clob.d.ts +13 -1
- package/dist/exchanges/polymarket/api-clob.js +26 -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/auth.js +3 -3
- package/dist/exchanges/polymarket/config.d.ts +1 -0
- package/dist/exchanges/polymarket/config.js +4 -0
- package/dist/exchanges/polymarket/index.js +2 -1
- package/dist/exchanges/polymarket_us/websocket.js +3 -2
- package/dist/exchanges/probable/api.d.ts +1 -1
- package/dist/exchanges/probable/api.js +1 -1
- package/dist/exchanges/probable/auth.js +5 -4
- package/dist/exchanges/probable/config.d.ts +2 -0
- package/dist/exchanges/probable/config.js +5 -0
- package/dist/exchanges/probable/websocket.js +2 -1
- package/dist/exchanges/suibets/api.d.ts +1 -1
- package/dist/exchanges/suibets/api.js +1 -1
- package/dist/exchanges/suibets/config.d.ts +1 -1
- package/dist/exchanges/suibets/config.js +2 -2
- package/dist/exchanges/suibets/errors.js +9 -0
- package/dist/exchanges/suibets/fetcher.d.ts +22 -2
- package/dist/exchanges/suibets/fetcher.js +23 -5
- package/dist/exchanges/suibets/index.d.ts +1 -1
- package/dist/exchanges/suibets/index.js +1 -1
- package/dist/exchanges/suibets/normalizer.js +2 -22
- package/dist/feeds/binance/binance-feed.js +6 -3
- package/dist/feeds/chainlink/chainlink-feed.js +5 -2
- package/dist/server/openapi.yaml +2 -0
- package/dist/types.d.ts +1 -0
- package/dist/utils/throttler.js +3 -5
- package/package.json +4 -4
|
@@ -181,11 +181,17 @@ class GeminiNormalizer {
|
|
|
181
181
|
// Extract prices
|
|
182
182
|
const bestBid = contract.prices?.bestBid ? parseFloat(contract.prices.bestBid) : 0.5;
|
|
183
183
|
const bestAsk = contract.prices?.bestAsk ? parseFloat(contract.prices.bestAsk) : 0.5;
|
|
184
|
+
const buyYes = contract.prices?.buy?.yes ? parseFloat(contract.prices.buy.yes) : undefined;
|
|
185
|
+
const sellYes = contract.prices?.sell?.yes ? parseFloat(contract.prices.sell.yes) : undefined;
|
|
186
|
+
const buyNo = contract.prices?.buy?.no ? parseFloat(contract.prices.buy.no) : undefined;
|
|
187
|
+
const sellNo = contract.prices?.sell?.no ? parseFloat(contract.prices.sell.no) : undefined;
|
|
184
188
|
const lastPrice = contract.prices?.lastTradePrice
|
|
185
189
|
? parseFloat(contract.prices.lastTradePrice)
|
|
186
190
|
: (bestBid + bestAsk) / 2;
|
|
187
|
-
const
|
|
188
|
-
const
|
|
191
|
+
const yesPriceSource = buyYes ?? sellYes ?? lastPrice;
|
|
192
|
+
const noPriceSource = buyNo ?? sellNo ?? (1 - yesPriceSource);
|
|
193
|
+
const yesPrice = roundPrice(Math.max(0, Math.min(1, yesPriceSource)));
|
|
194
|
+
const noPrice = roundPrice(Math.max(0, Math.min(1, noPriceSource)));
|
|
189
195
|
const outcomes = [
|
|
190
196
|
{
|
|
191
197
|
outcomeId: (0, utils_1.toOutcomeId)(instrumentSymbol, 'yes'),
|
|
@@ -220,7 +226,7 @@ class GeminiNormalizer {
|
|
|
220
226
|
outcomes,
|
|
221
227
|
resolutionDate,
|
|
222
228
|
volume24h: 0,
|
|
223
|
-
liquidity: event.liquidity ? parseFloat(event.liquidity) : 0,
|
|
229
|
+
liquidity: event.liquidity ? parseFloat(event.liquidity) : (event.volume24h ? parseFloat(event.volume24h) : (event.volume ? parseFloat(event.volume) : 0)),
|
|
224
230
|
url: buildExchangeUrl(event.ticker),
|
|
225
231
|
category: event.category,
|
|
226
232
|
tags,
|
|
@@ -76,8 +76,10 @@ class GeminiWebSocket {
|
|
|
76
76
|
const message = JSON.parse(data.toString());
|
|
77
77
|
this.handleMessage(message);
|
|
78
78
|
}
|
|
79
|
-
catch {
|
|
80
|
-
|
|
79
|
+
catch (error) {
|
|
80
|
+
logger_1.logger.warn('[gemini-titan] failed to parse or handle message', {
|
|
81
|
+
error: error instanceof Error ? error.message : String(error),
|
|
82
|
+
});
|
|
81
83
|
}
|
|
82
84
|
});
|
|
83
85
|
this.ws.on('error', (error) => {
|
|
@@ -179,7 +181,7 @@ class GeminiWebSocket {
|
|
|
179
181
|
this.resolveOrderBook(symbol, existing);
|
|
180
182
|
}
|
|
181
183
|
applyDelta(levels, price, size, sortOrder) {
|
|
182
|
-
const idx = levels.findIndex(l =>
|
|
184
|
+
const idx = levels.findIndex(l => l.price === price);
|
|
183
185
|
if (size === 0) {
|
|
184
186
|
if (idx !== -1)
|
|
185
187
|
levels.splice(idx, 1);
|
|
@@ -229,7 +231,12 @@ class GeminiWebSocket {
|
|
|
229
231
|
this.subscribedDepthSymbols.add(symbol);
|
|
230
232
|
if (!this.isConnected) {
|
|
231
233
|
this.connect().catch((err) => {
|
|
232
|
-
logger_1.logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}')
|
|
234
|
+
logger_1.logger.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}')`, {
|
|
235
|
+
error: err instanceof Error ? err.message : String(err),
|
|
236
|
+
});
|
|
237
|
+
if (!this.isTerminated) {
|
|
238
|
+
this.scheduleReconnect();
|
|
239
|
+
}
|
|
233
240
|
});
|
|
234
241
|
}
|
|
235
242
|
else {
|
|
@@ -255,7 +262,12 @@ class GeminiWebSocket {
|
|
|
255
262
|
this.subscribedTradeSymbols.add(symbol);
|
|
256
263
|
if (!this.isConnected) {
|
|
257
264
|
this.connect().catch((err) => {
|
|
258
|
-
logger_1.logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}')
|
|
265
|
+
logger_1.logger.warn(`[gemini-titan] connect failed during watchTrades('${symbol}')`, {
|
|
266
|
+
error: err instanceof Error ? err.message : String(err),
|
|
267
|
+
});
|
|
268
|
+
if (!this.isTerminated) {
|
|
269
|
+
this.scheduleReconnect();
|
|
270
|
+
}
|
|
259
271
|
});
|
|
260
272
|
}
|
|
261
273
|
else {
|
|
@@ -251,10 +251,12 @@ class HyperliquidNormalizer {
|
|
|
251
251
|
const bids = rawBids.map(level => ({
|
|
252
252
|
price: parseFloat(level.px),
|
|
253
253
|
size: parseFloat(level.sz),
|
|
254
|
+
orderCount: level.n,
|
|
254
255
|
}));
|
|
255
256
|
const asks = rawAsks.map(level => ({
|
|
256
257
|
price: parseFloat(level.px),
|
|
257
258
|
size: parseFloat(level.sz),
|
|
259
|
+
orderCount: level.n,
|
|
258
260
|
}));
|
|
259
261
|
return {
|
|
260
262
|
bids: [...bids].sort((a, b) => b.price - a.price),
|
|
@@ -6,14 +6,14 @@ export interface FetcherContext {
|
|
|
6
6
|
callApi(operationId: string, params?: Record<string, any>): Promise<any>;
|
|
7
7
|
getHeaders(): Record<string, string>;
|
|
8
8
|
}
|
|
9
|
-
export interface IExchangeFetcher<TRawMarket = unknown, TRawEvent = unknown> {
|
|
9
|
+
export interface IExchangeFetcher<TRawMarket = unknown, TRawEvent = unknown, TRawPositions = unknown[]> {
|
|
10
10
|
fetchRawMarkets(params?: MarketFilterParams): Promise<TRawMarket[]>;
|
|
11
11
|
fetchRawEvents(params: EventFetchParams): Promise<TRawEvent[]>;
|
|
12
12
|
fetchRawOHLCV?(id: string, params: OHLCVParams): Promise<unknown>;
|
|
13
13
|
fetchRawOrderBook?(id: string): Promise<unknown>;
|
|
14
14
|
fetchRawTrades?(id: string, params: TradesParams): Promise<unknown[]>;
|
|
15
15
|
fetchRawMyTrades?(params: MyTradesParams, walletAddress: string): Promise<unknown[]>;
|
|
16
|
-
fetchRawPositions?(walletAddress: string): Promise<
|
|
16
|
+
fetchRawPositions?(walletAddress: string): Promise<TRawPositions>;
|
|
17
17
|
fetchRawBalance?(walletAddress: string): Promise<unknown>;
|
|
18
18
|
}
|
|
19
19
|
export interface IExchangeNormalizer<TRawMarket = unknown, TRawEvent = unknown> {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
3
|
-
* Generated at: 2026-06-
|
|
3
|
+
* Generated at: 2026-06-08T19:06:11.820Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const kalshiApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.kalshiApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/kalshi/Kalshi.yaml
|
|
6
|
-
* Generated at: 2026-06-
|
|
6
|
+
* Generated at: 2026-06-08T19:06:11.820Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.kalshiApiSpec = {
|
|
@@ -14,7 +14,7 @@ exports.kalshiApiSpec = {
|
|
|
14
14
|
},
|
|
15
15
|
"servers": [
|
|
16
16
|
{
|
|
17
|
-
"url": "https://{env}.
|
|
17
|
+
"url": "https://{env}.external-api.kalshi.com/trade-api/v2",
|
|
18
18
|
"variables": {
|
|
19
19
|
"env": {
|
|
20
20
|
"default": "api",
|
|
@@ -11,13 +11,13 @@
|
|
|
11
11
|
* Do NOT put runtime config into api.ts — it will be overwritten.
|
|
12
12
|
*
|
|
13
13
|
* Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
|
|
14
|
-
* env = "api" → production: https://api.
|
|
15
|
-
* env = "demo-api" → demo/paper: https://demo-api.
|
|
14
|
+
* env = "api" → production: https://api.external-api.kalshi.com
|
|
15
|
+
* env = "demo-api" → demo/paper: https://demo-api.external-api.kalshi.com
|
|
16
16
|
*/
|
|
17
17
|
export declare const KALSHI_PROD_API_URL: string;
|
|
18
18
|
export declare const KALSHI_DEMO_API_URL: string;
|
|
19
|
-
export declare const KALSHI_PROD_WS_URL = "wss://api.
|
|
20
|
-
export declare const KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.
|
|
19
|
+
export declare const KALSHI_PROD_WS_URL = "wss://api.external-api.kalshi.com/trade-api/ws/v2";
|
|
20
|
+
export declare const KALSHI_DEMO_WS_URL = "wss://demo-api.external-api.kalshi.com/trade-api/ws/v2";
|
|
21
21
|
export declare const KALSHI_PATHS: {
|
|
22
22
|
TRADE_API: string;
|
|
23
23
|
EVENTS: string;
|
|
@@ -39,12 +39,12 @@ export interface KalshiApiConfig {
|
|
|
39
39
|
/**
|
|
40
40
|
* Return a typed config object for the requested environment.
|
|
41
41
|
*
|
|
42
|
-
* @param demoMode - Pass `true` to target demo-api.
|
|
42
|
+
* @param demoMode - Pass `true` to target demo-api.external-api.kalshi.com.
|
|
43
43
|
*
|
|
44
44
|
* @example
|
|
45
45
|
* ```typescript
|
|
46
46
|
* const config = getKalshiConfig(true);
|
|
47
|
-
* // config.apiUrl === "https://demo-api.
|
|
47
|
+
* // config.apiUrl === "https://demo-api.external-api.kalshi.com"
|
|
48
48
|
* ```
|
|
49
49
|
*/
|
|
50
50
|
export declare function getKalshiConfig(demoMode?: boolean, baseUrlOverride?: string): KalshiApiConfig;
|
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
* Do NOT put runtime config into api.ts — it will be overwritten.
|
|
13
13
|
*
|
|
14
14
|
* Environment mapping (aligns with the `{env}` server variable in Kalshi.yaml):
|
|
15
|
-
* env = "api" → production: https://api.
|
|
16
|
-
* env = "demo-api" → demo/paper: https://demo-api.
|
|
15
|
+
* env = "api" → production: https://api.external-api.kalshi.com
|
|
16
|
+
* env = "demo-api" → demo/paper: https://demo-api.external-api.kalshi.com
|
|
17
17
|
*/
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
19
|
exports.KALSHI_PATHS = exports.KALSHI_DEMO_WS_URL = exports.KALSHI_PROD_WS_URL = exports.KALSHI_DEMO_API_URL = exports.KALSHI_PROD_API_URL = void 0;
|
|
@@ -24,10 +24,10 @@ exports.getSeriesUrl = getSeriesUrl;
|
|
|
24
24
|
exports.getPortfolioUrl = getPortfolioUrl;
|
|
25
25
|
exports.getMarketsUrl = getMarketsUrl;
|
|
26
26
|
// ── Base URL constants ────────────────────────────────────────────────────────
|
|
27
|
-
exports.KALSHI_PROD_API_URL = process.env.KALSHI_BASE_URL || "https://api.
|
|
28
|
-
exports.KALSHI_DEMO_API_URL = process.env.KALSHI_DEMO_BASE_URL || "https://demo-api.kalshi.
|
|
29
|
-
exports.KALSHI_PROD_WS_URL = "wss://api.
|
|
30
|
-
exports.KALSHI_DEMO_WS_URL = "wss://demo-api.kalshi.
|
|
27
|
+
exports.KALSHI_PROD_API_URL = process.env.KALSHI_BASE_URL || "https://api.external-api.kalshi.com";
|
|
28
|
+
exports.KALSHI_DEMO_API_URL = process.env.KALSHI_DEMO_BASE_URL || "https://demo-api.external-api.kalshi.com";
|
|
29
|
+
exports.KALSHI_PROD_WS_URL = "wss://api.external-api.kalshi.com/trade-api/ws/v2";
|
|
30
|
+
exports.KALSHI_DEMO_WS_URL = "wss://demo-api.external-api.kalshi.com/trade-api/ws/v2";
|
|
31
31
|
// ── Path constants ────────────────────────────────────────────────────────────
|
|
32
32
|
exports.KALSHI_PATHS = {
|
|
33
33
|
TRADE_API: "/trade-api/v2",
|
|
@@ -42,12 +42,12 @@ exports.KALSHI_PATHS = {
|
|
|
42
42
|
/**
|
|
43
43
|
* Return a typed config object for the requested environment.
|
|
44
44
|
*
|
|
45
|
-
* @param demoMode - Pass `true` to target demo-api.
|
|
45
|
+
* @param demoMode - Pass `true` to target demo-api.external-api.kalshi.com.
|
|
46
46
|
*
|
|
47
47
|
* @example
|
|
48
48
|
* ```typescript
|
|
49
49
|
* const config = getKalshiConfig(true);
|
|
50
|
-
* // config.apiUrl === "https://demo-api.
|
|
50
|
+
* // config.apiUrl === "https://demo-api.external-api.kalshi.com"
|
|
51
51
|
* ```
|
|
52
52
|
*/
|
|
53
53
|
function getKalshiConfig(demoMode = false, baseUrlOverride) {
|
|
@@ -13,6 +13,8 @@ export interface KalshiRawMarket {
|
|
|
13
13
|
last_price_dollars?: string;
|
|
14
14
|
yes_ask_dollars?: string;
|
|
15
15
|
yes_bid_dollars?: string;
|
|
16
|
+
yes_ask_size_fp?: string;
|
|
17
|
+
yes_bid_size_fp?: string;
|
|
16
18
|
rules_primary?: string;
|
|
17
19
|
rules_secondary?: string;
|
|
18
20
|
expiration_time: string;
|
|
@@ -65,11 +65,9 @@ class KalshiFetcher {
|
|
|
65
65
|
}
|
|
66
66
|
const status = params?.status || 'active';
|
|
67
67
|
if (status === 'all') {
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.fetchAllWithStatus('settled'),
|
|
72
|
-
]);
|
|
68
|
+
const openEvents = await this.fetchAllWithStatus('open');
|
|
69
|
+
const closedEvents = await this.fetchAllWithStatus('closed');
|
|
70
|
+
const settledEvents = await this.fetchAllWithStatus('settled');
|
|
73
71
|
return this.enrichEventsWithSeriesList([...openEvents, ...closedEvents, ...settledEvents]);
|
|
74
72
|
}
|
|
75
73
|
else if (status === 'closed' || status === 'inactive') {
|
|
@@ -293,7 +291,8 @@ class KalshiFetcher {
|
|
|
293
291
|
return this.cachedEvents;
|
|
294
292
|
}
|
|
295
293
|
const isSorted = params?.sort && (params.sort === 'volume' || params.sort === 'liquidity');
|
|
296
|
-
const
|
|
294
|
+
const safetyCap = BATCH_SIZE * 10;
|
|
295
|
+
const fetchLimit = isSorted ? Math.min(1000, safetyCap) : Math.min(limit, safetyCap);
|
|
297
296
|
const [allEvents, fetchedSeriesMap] = await Promise.all([
|
|
298
297
|
this.fetchActiveEvents(fetchLimit, apiStatus),
|
|
299
298
|
this.fetchRawSeriesMap(),
|
|
@@ -364,7 +363,7 @@ class KalshiFetcher {
|
|
|
364
363
|
}
|
|
365
364
|
cursor = data.cursor;
|
|
366
365
|
page++;
|
|
367
|
-
if (
|
|
366
|
+
if (page >= 10)
|
|
368
367
|
break;
|
|
369
368
|
}
|
|
370
369
|
catch (e) {
|
|
@@ -112,7 +112,12 @@ class KalshiNormalizer {
|
|
|
112
112
|
// market, and aren't promoted to a column — attach them here so
|
|
113
113
|
// markets are queryable by series. event_ticker is omitted (already
|
|
114
114
|
// promoted to eventId).
|
|
115
|
-
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, {
|
|
115
|
+
sourceMetadata: (0, metadata_1.buildSourceMetadata)(market, KALSHI_PROMOTED_MARKET_KEYS, {
|
|
116
|
+
yes_ask_size_fp: market.yes_ask_size_fp,
|
|
117
|
+
yes_bid_size_fp: market.yes_bid_size_fp,
|
|
118
|
+
series_ticker: event.series_ticker,
|
|
119
|
+
series_title: event.series_title,
|
|
120
|
+
}),
|
|
116
121
|
};
|
|
117
122
|
(0, market_utils_1.addBinaryOutcomes)(um);
|
|
118
123
|
return um;
|
|
@@ -401,14 +401,16 @@ class KalshiWebSocket {
|
|
|
401
401
|
// The resolver will be fulfilled once the connection is (re)established
|
|
402
402
|
// and data arrives.
|
|
403
403
|
if (!this.isConnected) {
|
|
404
|
-
this.connect().catch(() => {
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
404
|
+
this.connect().catch((err) => {
|
|
405
|
+
logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbook", {
|
|
406
|
+
error: String(err),
|
|
407
|
+
});
|
|
408
|
+
if (!this.isTerminated) {
|
|
409
|
+
this.scheduleReconnect();
|
|
410
|
+
}
|
|
408
411
|
});
|
|
409
412
|
}
|
|
410
413
|
else {
|
|
411
|
-
// Already connected — ensure subscription message is sent
|
|
412
414
|
this.subscribeToOrderbook([ticker]);
|
|
413
415
|
}
|
|
414
416
|
// Return a promise that resolves on the next orderbook update
|
|
@@ -430,8 +432,13 @@ class KalshiWebSocket {
|
|
|
430
432
|
}
|
|
431
433
|
// Attempt connection — if it fails, scheduleReconnect handles recovery.
|
|
432
434
|
if (!this.isConnected) {
|
|
433
|
-
this.connect().catch(() => {
|
|
434
|
-
|
|
435
|
+
this.connect().catch((err) => {
|
|
436
|
+
logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToOrderbooks", {
|
|
437
|
+
error: String(err),
|
|
438
|
+
});
|
|
439
|
+
if (!this.isTerminated) {
|
|
440
|
+
this.scheduleReconnect();
|
|
441
|
+
}
|
|
435
442
|
});
|
|
436
443
|
}
|
|
437
444
|
else if (newTickers.length > 0) {
|
|
@@ -463,8 +470,13 @@ class KalshiWebSocket {
|
|
|
463
470
|
this.subscribedTradeTickers.add(ticker);
|
|
464
471
|
}
|
|
465
472
|
if (!this.isConnected) {
|
|
466
|
-
this.connect().catch(() => {
|
|
467
|
-
|
|
473
|
+
this.connect().catch((err) => {
|
|
474
|
+
logger_1.logger.warn("Kalshi WebSocket connect failed during subscribeToTrades", {
|
|
475
|
+
error: String(err),
|
|
476
|
+
});
|
|
477
|
+
if (!this.isTerminated) {
|
|
478
|
+
this.scheduleReconnect();
|
|
479
|
+
}
|
|
468
480
|
});
|
|
469
481
|
}
|
|
470
482
|
else {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
3
|
-
* Generated at: 2026-06-
|
|
3
|
+
* Generated at: 2026-06-08T19:06:11.859Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const limitlessApiSpec: {
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.limitlessApiSpec = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/limitless/Limitless.yaml
|
|
6
|
-
* Generated at: 2026-06-
|
|
6
|
+
* Generated at: 2026-06-08T19:06:11.859Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.limitlessApiSpec = {
|
|
@@ -153,7 +153,7 @@ class LimitlessClient {
|
|
|
153
153
|
// Sign with the EOA private key.
|
|
154
154
|
const orderSigner = new sdk_1.OrderSigner(wallet);
|
|
155
155
|
const signature = await orderSigner.signOrder(unsignedOrder, {
|
|
156
|
-
chainId:
|
|
156
|
+
chainId: config_1.LIMITLESS_CHAIN_ID,
|
|
157
157
|
contractAddress: market.venue.exchange,
|
|
158
158
|
});
|
|
159
159
|
// Submit with ownerId (required by Limitless API).
|
|
@@ -307,7 +307,7 @@ class LimitlessClient {
|
|
|
307
307
|
const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
308
308
|
const ABI = ["function balanceOf(address) view returns (uint256)", "function decimals() view returns (uint8)"];
|
|
309
309
|
const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
|
|
310
|
-
chainId:
|
|
310
|
+
chainId: config_1.LIMITLESS_CHAIN_ID,
|
|
311
311
|
name: 'base',
|
|
312
312
|
});
|
|
313
313
|
const contract = new ethers_1.Contract(USDC_ADDRESS, ABI, provider);
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LIMITLESS_RPC_URL = void 0;
|
|
3
|
+
exports.LIMITLESS_CHAIN_ID = exports.LIMITLESS_RPC_URL = exports.LIMITLESS_BASE_URL = void 0;
|
|
4
|
+
exports.LIMITLESS_BASE_URL = process.env.LIMITLESS_BASE_URL || 'https://api.limitless.exchange';
|
|
4
5
|
exports.LIMITLESS_RPC_URL = process.env.LIMITLESS_RPC_URL || 'https://mainnet.base.org';
|
|
6
|
+
exports.LIMITLESS_CHAIN_ID = 8453; // Base mainnet
|
|
@@ -8,6 +8,16 @@ export interface LimitlessRawMarket {
|
|
|
8
8
|
description?: string;
|
|
9
9
|
tokens?: Record<string, string>;
|
|
10
10
|
prices?: number[];
|
|
11
|
+
tradePrices?: {
|
|
12
|
+
buy?: {
|
|
13
|
+
market?: number[];
|
|
14
|
+
limit?: number[];
|
|
15
|
+
};
|
|
16
|
+
sell?: {
|
|
17
|
+
market?: number[];
|
|
18
|
+
limit?: number[];
|
|
19
|
+
};
|
|
20
|
+
};
|
|
11
21
|
expirationTimestamp?: string;
|
|
12
22
|
volumeFormatted?: number;
|
|
13
23
|
volume?: number;
|
|
@@ -60,6 +60,7 @@ class LimitlessFetcher {
|
|
|
60
60
|
const httpClient = new HttpClient({
|
|
61
61
|
baseURL: this.apiUrl,
|
|
62
62
|
apiKey: this.apiKey,
|
|
63
|
+
timeout: 30000,
|
|
63
64
|
});
|
|
64
65
|
const marketFetcher = new MarketFetcher(httpClient);
|
|
65
66
|
if (params?.marketId) {
|
|
@@ -196,7 +197,7 @@ class LimitlessFetcher {
|
|
|
196
197
|
}
|
|
197
198
|
async fetchRawEventBySlug(slug) {
|
|
198
199
|
const { HttpClient, MarketFetcher } = await Promise.resolve().then(() => __importStar(require('@limitless-exchange/sdk')));
|
|
199
|
-
const httpClient = new HttpClient({ baseURL: this.apiUrl });
|
|
200
|
+
const httpClient = new HttpClient({ baseURL: this.apiUrl, timeout: 30000 });
|
|
200
201
|
const marketFetcher = new MarketFetcher(httpClient);
|
|
201
202
|
const market = await marketFetcher.getMarket(slug);
|
|
202
203
|
return market ? [market] : [];
|
|
@@ -501,7 +501,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
501
501
|
// Static network avoids ethers v5 auto-detect (eth_chainId), which can throw
|
|
502
502
|
// noNetwork / NETWORK_ERROR on flaky public RPCs (#92).
|
|
503
503
|
const provider = new ethers_1.providers.StaticJsonRpcProvider(config_1.LIMITLESS_RPC_URL, {
|
|
504
|
-
chainId:
|
|
504
|
+
chainId: config_1.LIMITLESS_CHAIN_ID,
|
|
505
505
|
name: 'base',
|
|
506
506
|
});
|
|
507
507
|
// Get USDC contract address for Base
|
|
@@ -63,9 +63,9 @@ function mapMarketToUnified(market, context = {}) {
|
|
|
63
63
|
if (!market.tokens.yes || !market.tokens.no) {
|
|
64
64
|
throw new Error(`[limitless] Market "${market.slug}" is missing token addresses`);
|
|
65
65
|
}
|
|
66
|
-
const
|
|
67
|
-
const yesPrice =
|
|
68
|
-
const noPrice =
|
|
66
|
+
const legacyPrices = Array.isArray(market.prices) ? market.prices : [];
|
|
67
|
+
const yesPrice = market.tradePrices?.buy?.market?.[0] ?? legacyPrices[0] ?? 0.5;
|
|
68
|
+
const noPrice = market.tradePrices?.sell?.market?.[0] ?? legacyPrices[1] ?? Math.max(0, Math.min(1, 1 - yesPrice));
|
|
69
69
|
const yesLabel = hasParentContext ? rawTitle : 'Yes';
|
|
70
70
|
const noLabel = hasParentContext ? `Not ${rawTitle}` : 'No';
|
|
71
71
|
outcomes.push({
|
|
@@ -73,8 +73,8 @@ class LimitlessWebSocket {
|
|
|
73
73
|
if (!this.client.isConnected()) {
|
|
74
74
|
await this.client.connect();
|
|
75
75
|
}
|
|
76
|
-
// Subscribe to market
|
|
77
|
-
await this.client.subscribe('
|
|
76
|
+
// Subscribe to market orderbook updates through the SDK market-price channel.
|
|
77
|
+
await this.client.subscribe('subscribe_market_prices', { marketSlugs: [marketSlug] });
|
|
78
78
|
if (callback) {
|
|
79
79
|
this.orderbookCallbacks.set(marketSlug, callback);
|
|
80
80
|
}
|
|
@@ -170,7 +170,7 @@ class LimitlessWebSocket {
|
|
|
170
170
|
await this.client.connect();
|
|
171
171
|
}
|
|
172
172
|
// Subscribe to market prices
|
|
173
|
-
await this.client.subscribe('
|
|
173
|
+
await this.client.subscribe('subscribe_market_prices', { marketAddresses: [marketAddress] });
|
|
174
174
|
this.priceCallbacks.set(marketAddress, callback);
|
|
175
175
|
}
|
|
176
176
|
/**
|
|
@@ -184,11 +184,7 @@ class LimitlessWebSocket {
|
|
|
184
184
|
if (!this.client.isConnected()) {
|
|
185
185
|
await this.client.connect();
|
|
186
186
|
}
|
|
187
|
-
await this.client.subscribe('
|
|
188
|
-
// Actually, the channel type has 'subscribe_positions'. Let's check.
|
|
189
|
-
// Wait, I saw 'orders' in SubscriptionChannel.
|
|
190
|
-
// Let's use 'orders' as it's common for user data.
|
|
191
|
-
// Wait, I saw 'subscribe_positions' in the type.
|
|
187
|
+
await this.client.subscribe('subscribe_order_events');
|
|
192
188
|
await this.client.subscribe('subscribe_positions');
|
|
193
189
|
this.client.on('positions', callback);
|
|
194
190
|
}
|
|
@@ -220,11 +216,12 @@ class LimitlessWebSocket {
|
|
|
220
216
|
async unsubscribe(marketSlugOrAddress) {
|
|
221
217
|
this.orderbookCallbacks.delete(marketSlugOrAddress);
|
|
222
218
|
this.priceCallbacks.delete(marketSlugOrAddress);
|
|
223
|
-
//
|
|
224
|
-
|
|
219
|
+
// The current SDK only exposes explicit unsubscribe lifecycle channels;
|
|
220
|
+
// cast market-price unsubscribe channels until the SDK publishes them.
|
|
221
|
+
await this.client.unsubscribe('subscribe_market_prices', {
|
|
225
222
|
marketSlugs: [marketSlugOrAddress],
|
|
226
223
|
});
|
|
227
|
-
await this.client.unsubscribe('
|
|
224
|
+
await this.client.unsubscribe('subscribe_market_prices', {
|
|
228
225
|
marketAddresses: [marketSlugOrAddress],
|
|
229
226
|
});
|
|
230
227
|
}
|
|
@@ -135,7 +135,6 @@ function buildOutcomes(question, postId, medianProb) {
|
|
|
135
135
|
aggregations: latest,
|
|
136
136
|
resolution: question?.resolution ?? null,
|
|
137
137
|
scaling: question?.scaling ?? null,
|
|
138
|
-
possibilities: question?.possibilities ?? null,
|
|
139
138
|
};
|
|
140
139
|
// Multiple choice: one outcome per option, each independently forecastable
|
|
141
140
|
if (type === "multiple_choice") {
|
|
@@ -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-08T19:06:11.872Z
|
|
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-08T19:06:11.872Z
|
|
7
7
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
8
8
|
*/
|
|
9
9
|
exports.myriadApiSpec = {
|
|
@@ -166,10 +166,18 @@ exports.myriadApiSpec = {
|
|
|
166
166
|
"enum": [
|
|
167
167
|
"open",
|
|
168
168
|
"closed",
|
|
169
|
-
"resolved"
|
|
169
|
+
"resolved",
|
|
170
|
+
"voided"
|
|
170
171
|
]
|
|
171
172
|
}
|
|
172
173
|
},
|
|
174
|
+
{
|
|
175
|
+
"name": "trading_model",
|
|
176
|
+
"in": "query",
|
|
177
|
+
"schema": {
|
|
178
|
+
"type": "string"
|
|
179
|
+
}
|
|
180
|
+
},
|
|
173
181
|
{
|
|
174
182
|
"name": "token_address",
|
|
175
183
|
"in": "query",
|
|
@@ -655,6 +663,13 @@ exports.myriadApiSpec = {
|
|
|
655
663
|
"type": "string"
|
|
656
664
|
}
|
|
657
665
|
},
|
|
666
|
+
{
|
|
667
|
+
"name": "trading_model",
|
|
668
|
+
"in": "query",
|
|
669
|
+
"schema": {
|
|
670
|
+
"type": "string"
|
|
671
|
+
}
|
|
672
|
+
},
|
|
658
673
|
{
|
|
659
674
|
"name": "token_address",
|
|
660
675
|
"in": "query",
|
|
@@ -15,6 +15,12 @@ const MYRIAD_PROMOTED_MARKET_KEYS = [
|
|
|
15
15
|
const MYRIAD_PROMOTED_EVENT_KEYS = [
|
|
16
16
|
'id', 'title', 'markets',
|
|
17
17
|
];
|
|
18
|
+
const WEI_SCALE = BigInt('1000000000000000000');
|
|
19
|
+
const PRECISION_SCALE = BigInt('1000000000000');
|
|
20
|
+
function weiStringToNumber(value) {
|
|
21
|
+
const scaled = (BigInt(value) * PRECISION_SCALE) / WEI_SCALE;
|
|
22
|
+
return Number(scaled) / Number(PRECISION_SCALE);
|
|
23
|
+
}
|
|
18
24
|
function selectTimeframe(interval) {
|
|
19
25
|
switch (interval) {
|
|
20
26
|
case '1m':
|
|
@@ -183,15 +189,14 @@ class MyriadNormalizer {
|
|
|
183
189
|
};
|
|
184
190
|
}
|
|
185
191
|
normalizeClobOrderBook(raw) {
|
|
186
|
-
const WEI = 1e18;
|
|
187
192
|
return {
|
|
188
193
|
bids: raw.bids.map(([priceWei, sizeWei]) => ({
|
|
189
|
-
price:
|
|
190
|
-
size:
|
|
194
|
+
price: weiStringToNumber(priceWei),
|
|
195
|
+
size: weiStringToNumber(sizeWei),
|
|
191
196
|
})),
|
|
192
197
|
asks: raw.asks.map(([priceWei, sizeWei]) => ({
|
|
193
|
-
price:
|
|
194
|
-
size:
|
|
198
|
+
price: weiStringToNumber(priceWei),
|
|
199
|
+
size: weiStringToNumber(sizeWei),
|
|
195
200
|
})),
|
|
196
201
|
timestamp: Date.now(),
|
|
197
202
|
};
|
|
@@ -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-08T19:06:11.875Z
|
|
4
4
|
* Do not edit manually -- run "npm run fetch:openapi" to regenerate.
|
|
5
5
|
*/
|
|
6
6
|
export declare const opinionApiSpec: {
|
|
@@ -113,6 +113,21 @@ export declare const opinionApiSpec: {
|
|
|
113
113
|
}[];
|
|
114
114
|
};
|
|
115
115
|
};
|
|
116
|
+
"/market/slug/{slug}": {
|
|
117
|
+
get: {
|
|
118
|
+
tags: string[];
|
|
119
|
+
summary: string;
|
|
120
|
+
operationId: string;
|
|
121
|
+
parameters: {
|
|
122
|
+
name: string;
|
|
123
|
+
in: string;
|
|
124
|
+
required: boolean;
|
|
125
|
+
schema: {
|
|
126
|
+
type: string;
|
|
127
|
+
};
|
|
128
|
+
}[];
|
|
129
|
+
};
|
|
130
|
+
};
|
|
116
131
|
"/market/categorical/{marketId}": {
|
|
117
132
|
get: {
|
|
118
133
|
tags: string[];
|