pmxt-core 2.39.1 → 2.40.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.
Files changed (82) hide show
  1. package/dist/exchanges/baozi/fetcher.js +28 -8
  2. package/dist/exchanges/baozi/index.js +6 -4
  3. package/dist/exchanges/gemini-titan/auth.d.ts +34 -0
  4. package/dist/exchanges/gemini-titan/auth.js +80 -0
  5. package/dist/exchanges/gemini-titan/config.d.ts +15 -0
  6. package/dist/exchanges/gemini-titan/config.js +24 -0
  7. package/dist/exchanges/gemini-titan/errors.d.ts +20 -0
  8. package/dist/exchanges/gemini-titan/errors.js +75 -0
  9. package/dist/exchanges/gemini-titan/fetcher.d.ts +26 -0
  10. package/dist/exchanges/gemini-titan/fetcher.js +148 -0
  11. package/dist/exchanges/gemini-titan/index.d.ts +31 -0
  12. package/dist/exchanges/gemini-titan/index.js +188 -0
  13. package/dist/exchanges/gemini-titan/normalizer.d.ts +13 -0
  14. package/dist/exchanges/gemini-titan/normalizer.js +229 -0
  15. package/dist/exchanges/gemini-titan/types.d.ts +220 -0
  16. package/dist/exchanges/gemini-titan/types.js +6 -0
  17. package/dist/exchanges/gemini-titan/utils.d.ts +30 -0
  18. package/dist/exchanges/gemini-titan/utils.js +57 -0
  19. package/dist/exchanges/gemini-titan/websocket.d.ts +46 -0
  20. package/dist/exchanges/gemini-titan/websocket.js +295 -0
  21. package/dist/exchanges/kalshi/api.d.ts +1 -1
  22. package/dist/exchanges/kalshi/api.js +1 -1
  23. package/dist/exchanges/kalshi/fetcher.js +6 -2
  24. package/dist/exchanges/limitless/api.d.ts +1 -1
  25. package/dist/exchanges/limitless/api.js +1 -1
  26. package/dist/exchanges/limitless/index.js +3 -6
  27. package/dist/exchanges/limitless/utils.js +9 -1
  28. package/dist/exchanges/metaculus/fetchEvents.js +7 -2
  29. package/dist/exchanges/mock/index.d.ts +55 -0
  30. package/dist/exchanges/mock/index.js +603 -0
  31. package/dist/exchanges/mock/seededRng.d.ts +10 -0
  32. package/dist/exchanges/mock/seededRng.js +48 -0
  33. package/dist/exchanges/myriad/api.d.ts +1 -1
  34. package/dist/exchanges/myriad/api.js +1 -1
  35. package/dist/exchanges/myriad/websocket.d.ts +4 -0
  36. package/dist/exchanges/myriad/websocket.js +51 -6
  37. package/dist/exchanges/opinion/api.d.ts +1 -1
  38. package/dist/exchanges/opinion/api.js +1 -1
  39. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  40. package/dist/exchanges/polymarket/api-clob.js +1 -1
  41. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  42. package/dist/exchanges/polymarket/api-data.js +1 -1
  43. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  44. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  45. package/dist/exchanges/polymarket/auth.js +5 -2
  46. package/dist/exchanges/polymarket/index.js +2 -1
  47. package/dist/exchanges/polymarket_us/normalizer.js +5 -1
  48. package/dist/exchanges/probable/api.d.ts +1 -1
  49. package/dist/exchanges/probable/api.js +1 -1
  50. package/dist/exchanges/probable/index.js +9 -6
  51. package/dist/exchanges/smarkets/fetcher.js +6 -2
  52. package/dist/index.d.ts +8 -0
  53. package/dist/index.js +9 -1
  54. package/dist/router/Router.js +55 -21
  55. package/dist/server/exchange-factory.js +9 -0
  56. package/dist/server/openapi.yaml +22 -0
  57. package/dist/server/ws-handler.js +13 -3
  58. package/package.json +3 -3
  59. package/dist/exchanges/baozi/price.test.d.ts +0 -1
  60. package/dist/exchanges/baozi/price.test.js +0 -33
  61. package/dist/exchanges/kalshi/kalshi.test.d.ts +0 -1
  62. package/dist/exchanges/kalshi/kalshi.test.js +0 -641
  63. package/dist/exchanges/kalshi/price.test.d.ts +0 -1
  64. package/dist/exchanges/kalshi/price.test.js +0 -24
  65. package/dist/exchanges/myriad/price.test.d.ts +0 -1
  66. package/dist/exchanges/myriad/price.test.js +0 -17
  67. package/dist/exchanges/polymarket_us/errors.test.d.ts +0 -1
  68. package/dist/exchanges/polymarket_us/errors.test.js +0 -54
  69. package/dist/exchanges/polymarket_us/index.test.d.ts +0 -8
  70. package/dist/exchanges/polymarket_us/index.test.js +0 -237
  71. package/dist/exchanges/polymarket_us/normalizer.test.d.ts +0 -1
  72. package/dist/exchanges/polymarket_us/normalizer.test.js +0 -224
  73. package/dist/exchanges/polymarket_us/price.test.d.ts +0 -1
  74. package/dist/exchanges/polymarket_us/price.test.js +0 -131
  75. package/dist/exchanges/polymarket_us/websocket.test.d.ts +0 -8
  76. package/dist/exchanges/polymarket_us/websocket.test.js +0 -162
  77. package/dist/exchanges/smarkets/price.test.d.ts +0 -1
  78. package/dist/exchanges/smarkets/price.test.js +0 -50
  79. package/dist/router/Router.test.d.ts +0 -1
  80. package/dist/router/Router.test.js +0 -328
  81. package/dist/router/client.test.d.ts +0 -1
  82. package/dist/router/client.test.js +0 -177
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toMarketId = toMarketId;
4
+ exports.fromMarketId = fromMarketId;
5
+ exports.toOutcomeId = toOutcomeId;
6
+ exports.fromOutcomeId = fromOutcomeId;
7
+ exports.isGeminiInstrument = isGeminiInstrument;
8
+ const config_1 = require("./config");
9
+ /**
10
+ * Build a unique market ID from a Gemini instrumentSymbol.
11
+ * Format: "gemi-{instrumentSymbol}"
12
+ *
13
+ * A market encompasses both YES and NO sides of a single contract.
14
+ */
15
+ function toMarketId(instrumentSymbol) {
16
+ return `gemi-${instrumentSymbol}`;
17
+ }
18
+ /**
19
+ * Extract the instrumentSymbol from our market ID format.
20
+ */
21
+ function fromMarketId(marketId) {
22
+ const prefix = 'gemi-';
23
+ if (!marketId.startsWith(prefix)) {
24
+ throw new Error(`Invalid Gemini Titan market ID: ${marketId}`);
25
+ }
26
+ return marketId.slice(prefix.length);
27
+ }
28
+ /**
29
+ * Build an outcome ID that encodes both the instrumentSymbol and side.
30
+ * Format: "GEMI:{instrumentSymbol}:{side}"
31
+ *
32
+ * The side is needed because Gemini orders require an explicit
33
+ * "outcome" field ("yes" or "no").
34
+ */
35
+ function toOutcomeId(instrumentSymbol, side) {
36
+ return `GEMI:${instrumentSymbol}:${side}`;
37
+ }
38
+ /**
39
+ * Decode an outcome ID back into instrumentSymbol and side.
40
+ */
41
+ function fromOutcomeId(outcomeId) {
42
+ const parts = outcomeId.split(':');
43
+ if (parts.length !== 3 || parts[0] !== 'GEMI') {
44
+ throw new Error(`Invalid Gemini Titan outcome ID: ${outcomeId}`);
45
+ }
46
+ const side = parts[2];
47
+ if (side !== 'yes' && side !== 'no') {
48
+ throw new Error(`Invalid side in outcome ID: ${outcomeId}`);
49
+ }
50
+ return { instrumentSymbol: parts[1], side };
51
+ }
52
+ /**
53
+ * Check if a string looks like a Gemini prediction market instrument symbol.
54
+ */
55
+ function isGeminiInstrument(symbol) {
56
+ return symbol.startsWith(config_1.INSTRUMENT_PREFIX);
57
+ }
@@ -0,0 +1,46 @@
1
+ import { OrderBook, Trade } from '../../types';
2
+ import { GeminiAuth } from './auth';
3
+ export interface GeminiWebSocketConfig {
4
+ wsUrl: string;
5
+ reconnectIntervalMs?: number;
6
+ watchTimeoutMs?: number;
7
+ }
8
+ /**
9
+ * Gemini Titan WebSocket for real-time order book and trade streaming.
10
+ *
11
+ * Subscribes to:
12
+ * - {symbol}@depth20 (L2 partial depth snapshots at 1s intervals)
13
+ * - {symbol}@trade (executed trades)
14
+ *
15
+ * Auth headers are sent during the handshake if credentials are provided
16
+ * (needed for account-scoped streams, optional for public data).
17
+ */
18
+ export declare class GeminiWebSocket {
19
+ private ws?;
20
+ private readonly auth;
21
+ private readonly config;
22
+ private readonly orderBookResolvers;
23
+ private readonly tradeResolvers;
24
+ private readonly orderBooks;
25
+ private readonly subscribedDepthSymbols;
26
+ private readonly subscribedTradeSymbols;
27
+ private messageIdCounter;
28
+ private isConnecting;
29
+ private isConnected;
30
+ private reconnectTimer?;
31
+ private connectionPromise?;
32
+ private isTerminated;
33
+ constructor(auth: GeminiAuth | undefined, config: GeminiWebSocketConfig);
34
+ private connect;
35
+ private scheduleReconnect;
36
+ private sendSubscribe;
37
+ private handleMessage;
38
+ private handleDepthSnapshot;
39
+ private handleDepthUpdate;
40
+ private applyDelta;
41
+ private handleTrade;
42
+ private resolveOrderBook;
43
+ watchOrderBook(symbol: string): Promise<OrderBook>;
44
+ watchTrades(symbol: string): Promise<Trade[]>;
45
+ close(): Promise<void>;
46
+ }
@@ -0,0 +1,295 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GeminiWebSocket = void 0;
7
+ const ws_1 = __importDefault(require("ws"));
8
+ const watch_timeout_1 = require("../../utils/watch-timeout");
9
+ /**
10
+ * Gemini Titan WebSocket for real-time order book and trade streaming.
11
+ *
12
+ * Subscribes to:
13
+ * - {symbol}@depth20 (L2 partial depth snapshots at 1s intervals)
14
+ * - {symbol}@trade (executed trades)
15
+ *
16
+ * Auth headers are sent during the handshake if credentials are provided
17
+ * (needed for account-scoped streams, optional for public data).
18
+ */
19
+ class GeminiWebSocket {
20
+ ws;
21
+ auth;
22
+ config;
23
+ orderBookResolvers = new Map();
24
+ tradeResolvers = new Map();
25
+ orderBooks = new Map();
26
+ subscribedDepthSymbols = new Set();
27
+ subscribedTradeSymbols = new Set();
28
+ messageIdCounter = 1;
29
+ isConnecting = false;
30
+ isConnected = false;
31
+ reconnectTimer;
32
+ connectionPromise;
33
+ isTerminated = false;
34
+ constructor(auth, config) {
35
+ this.auth = auth;
36
+ this.config = config;
37
+ }
38
+ // -------------------------------------------------------------------------
39
+ // Connection
40
+ // -------------------------------------------------------------------------
41
+ async connect() {
42
+ if (this.isConnected || this.isTerminated)
43
+ return;
44
+ if (this.connectionPromise)
45
+ return this.connectionPromise;
46
+ this.isConnecting = true;
47
+ this.connectionPromise = new Promise((resolve, reject) => {
48
+ try {
49
+ const headers = this.auth
50
+ ? this.auth.buildWsHeaders()
51
+ : {};
52
+ this.ws = new ws_1.default(this.config.wsUrl, { headers });
53
+ this.ws.on('open', () => {
54
+ this.isConnected = true;
55
+ this.isConnecting = false;
56
+ this.connectionPromise = undefined;
57
+ // Resubscribe on reconnect
58
+ const allStreams = [];
59
+ for (const sym of this.subscribedDepthSymbols) {
60
+ allStreams.push(`${sym}@depth20`);
61
+ }
62
+ for (const sym of this.subscribedTradeSymbols) {
63
+ allStreams.push(`${sym}@trade`);
64
+ }
65
+ if (allStreams.length > 0) {
66
+ this.sendSubscribe(allStreams);
67
+ }
68
+ resolve();
69
+ });
70
+ this.ws.on('message', (data) => {
71
+ try {
72
+ const message = JSON.parse(data.toString());
73
+ this.handleMessage(message);
74
+ }
75
+ catch {
76
+ // Ignore unparseable messages
77
+ }
78
+ });
79
+ this.ws.on('error', (error) => {
80
+ this.isConnecting = false;
81
+ this.connectionPromise = undefined;
82
+ reject(error);
83
+ });
84
+ this.ws.on('close', () => {
85
+ this.isConnected = false;
86
+ this.isConnecting = false;
87
+ this.connectionPromise = undefined;
88
+ if (!this.isTerminated) {
89
+ this.scheduleReconnect();
90
+ }
91
+ });
92
+ }
93
+ catch (error) {
94
+ this.isConnecting = false;
95
+ this.connectionPromise = undefined;
96
+ reject(error);
97
+ }
98
+ });
99
+ return this.connectionPromise;
100
+ }
101
+ scheduleReconnect() {
102
+ if (this.isTerminated)
103
+ return;
104
+ if (this.reconnectTimer)
105
+ clearTimeout(this.reconnectTimer);
106
+ this.reconnectTimer = setTimeout(() => {
107
+ this.connect().catch((err) => {
108
+ console.warn(`[gemini-titan] reconnect failed: ${err instanceof Error ? err.message : String(err)}`);
109
+ });
110
+ }, this.config.reconnectIntervalMs ?? 5000);
111
+ }
112
+ // -------------------------------------------------------------------------
113
+ // Subscription
114
+ // -------------------------------------------------------------------------
115
+ sendSubscribe(streams) {
116
+ if (!this.ws || !this.isConnected)
117
+ return;
118
+ this.ws.send(JSON.stringify({
119
+ id: String(this.messageIdCounter++),
120
+ method: 'subscribe',
121
+ params: streams,
122
+ }));
123
+ }
124
+ // -------------------------------------------------------------------------
125
+ // Message handling
126
+ // -------------------------------------------------------------------------
127
+ handleMessage(message) {
128
+ // Gemini sends flat objects, NOT wrapped in { stream, data }.
129
+ // Depth snapshots: { lastUpdateId, symbol, bids, asks }
130
+ // Depth deltas: { e, E, s, U, u, b, a }
131
+ // Trades: { E, s, t, p, q, m }
132
+ // Confirmations: { id, status: 200 }
133
+ if (message.lastUpdateId !== undefined && message.bids) {
134
+ // Depth snapshot — symbol comes back lowercase
135
+ this.handleDepthSnapshot(message);
136
+ }
137
+ else if (message.e === 'depthUpdate' || (message.U !== undefined && message.b)) {
138
+ this.handleDepthUpdate(message);
139
+ }
140
+ else if (message.t !== undefined && message.p !== undefined && message.q !== undefined) {
141
+ this.handleTrade(message);
142
+ }
143
+ // Subscription confirmations ({ id, status }) are ignored
144
+ }
145
+ handleDepthSnapshot(data) {
146
+ // symbol comes back lowercase from the API, but we subscribed with
147
+ // uppercase. Normalize to uppercase for resolver lookup.
148
+ const symbol = data.symbol.toUpperCase();
149
+ const bids = (data.bids ?? []).map((level) => ({
150
+ price: parseFloat(level[0]),
151
+ size: parseFloat(level[1]),
152
+ }));
153
+ const asks = (data.asks ?? []).map((level) => ({
154
+ price: parseFloat(level[0]),
155
+ size: parseFloat(level[1]),
156
+ }));
157
+ bids.sort((a, b) => b.price - a.price);
158
+ asks.sort((a, b) => a.price - b.price);
159
+ const orderBook = { bids, asks, timestamp: Date.now() };
160
+ this.orderBooks.set(symbol, orderBook);
161
+ this.resolveOrderBook(symbol, orderBook);
162
+ }
163
+ handleDepthUpdate(data) {
164
+ const symbol = data.s.toUpperCase();
165
+ const existing = this.orderBooks.get(symbol);
166
+ if (!existing)
167
+ return; // No snapshot yet, discard delta
168
+ for (const [priceStr, sizeStr] of (data.b ?? [])) {
169
+ this.applyDelta(existing.bids, parseFloat(priceStr), parseFloat(sizeStr), 'desc');
170
+ }
171
+ for (const [priceStr, sizeStr] of (data.a ?? [])) {
172
+ this.applyDelta(existing.asks, parseFloat(priceStr), parseFloat(sizeStr), 'asc');
173
+ }
174
+ existing.timestamp = Date.now();
175
+ this.resolveOrderBook(symbol, existing);
176
+ }
177
+ applyDelta(levels, price, size, sortOrder) {
178
+ const idx = levels.findIndex(l => Math.abs(l.price - price) < 0.0001);
179
+ if (size === 0) {
180
+ if (idx !== -1)
181
+ levels.splice(idx, 1);
182
+ }
183
+ else if (idx !== -1) {
184
+ levels[idx] = { price, size };
185
+ }
186
+ else {
187
+ levels.push({ price, size });
188
+ if (sortOrder === 'desc') {
189
+ levels.sort((a, b) => b.price - a.price);
190
+ }
191
+ else {
192
+ levels.sort((a, b) => a.price - b.price);
193
+ }
194
+ }
195
+ }
196
+ handleTrade(data) {
197
+ const symbol = data.s.toUpperCase();
198
+ const trade = {
199
+ id: String(data.t ?? Date.now()),
200
+ timestamp: data.E ? Math.floor(data.E / 1_000_000) : Date.now(), // E is nanoseconds
201
+ price: parseFloat(data.p),
202
+ amount: parseFloat(data.q),
203
+ side: data.m ? 'sell' : 'buy', // m = true means buyer is maker (taker sold)
204
+ };
205
+ const resolvers = this.tradeResolvers.get(symbol);
206
+ if (resolvers && resolvers.length > 0) {
207
+ resolvers.forEach(r => r.resolve([trade]));
208
+ this.tradeResolvers.set(symbol, []);
209
+ }
210
+ }
211
+ resolveOrderBook(symbol, orderBook) {
212
+ const resolvers = this.orderBookResolvers.get(symbol);
213
+ if (resolvers && resolvers.length > 0) {
214
+ resolvers.forEach(r => r.resolve(orderBook));
215
+ this.orderBookResolvers.set(symbol, []);
216
+ }
217
+ }
218
+ // -------------------------------------------------------------------------
219
+ // Public API
220
+ // -------------------------------------------------------------------------
221
+ async watchOrderBook(symbol) {
222
+ if (this.isTerminated) {
223
+ throw new Error(`WebSocket terminated, cannot watch ${symbol}`);
224
+ }
225
+ this.subscribedDepthSymbols.add(symbol);
226
+ if (!this.isConnected) {
227
+ this.connect().catch((err) => {
228
+ console.warn(`[gemini-titan] connect failed during watchOrderBook('${symbol}'): ${err instanceof Error ? err.message : String(err)}`);
229
+ });
230
+ }
231
+ else {
232
+ this.sendSubscribe([`${symbol}@depth20`]);
233
+ }
234
+ const dataPromise = new Promise((resolve, reject) => {
235
+ if (!this.orderBookResolvers.has(symbol)) {
236
+ this.orderBookResolvers.set(symbol, []);
237
+ }
238
+ this.orderBookResolvers.get(symbol).push({ resolve, reject });
239
+ });
240
+ return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchOrderBook('${symbol}')`);
241
+ }
242
+ async watchTrades(symbol) {
243
+ if (this.isTerminated) {
244
+ throw new Error(`WebSocket terminated, cannot watch trades for ${symbol}`);
245
+ }
246
+ this.subscribedTradeSymbols.add(symbol);
247
+ if (!this.isConnected) {
248
+ this.connect().catch((err) => {
249
+ console.warn(`[gemini-titan] connect failed during watchTrades('${symbol}'): ${err instanceof Error ? err.message : String(err)}`);
250
+ });
251
+ }
252
+ else {
253
+ this.sendSubscribe([`${symbol}@trade`]);
254
+ }
255
+ const dataPromise = new Promise((resolve, reject) => {
256
+ if (!this.tradeResolvers.has(symbol)) {
257
+ this.tradeResolvers.set(symbol, []);
258
+ }
259
+ this.tradeResolvers.get(symbol).push({ resolve, reject });
260
+ });
261
+ return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${symbol}')`);
262
+ }
263
+ async close() {
264
+ this.isTerminated = true;
265
+ if (this.reconnectTimer) {
266
+ clearTimeout(this.reconnectTimer);
267
+ this.reconnectTimer = undefined;
268
+ }
269
+ for (const [symbol, resolvers] of this.orderBookResolvers) {
270
+ resolvers.forEach(r => r.reject(new Error(`WebSocket closed for ${symbol}`)));
271
+ }
272
+ this.orderBookResolvers.clear();
273
+ for (const [symbol, resolvers] of this.tradeResolvers) {
274
+ resolvers.forEach(r => r.reject(new Error(`WebSocket closed for ${symbol}`)));
275
+ }
276
+ this.tradeResolvers.clear();
277
+ if (this.ws) {
278
+ const ws = this.ws;
279
+ this.ws = undefined;
280
+ if (ws.readyState !== ws_1.default.CLOSED && ws.readyState !== ws_1.default.CLOSING) {
281
+ return new Promise((resolve) => {
282
+ ws.once('close', () => {
283
+ this.isConnected = false;
284
+ this.isConnecting = false;
285
+ resolve();
286
+ });
287
+ ws.close();
288
+ });
289
+ }
290
+ }
291
+ this.isConnected = false;
292
+ this.isConnecting = false;
293
+ }
294
+ }
295
+ exports.GeminiWebSocket = GeminiWebSocket;
@@ -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-05-08T20:51:55.489Z
3
+ * Generated at: 2026-05-08T23:06:00.095Z
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-05-08T20:51:55.489Z
6
+ * Generated at: 2026-05-08T23:06:00.095Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -217,8 +217,12 @@ class KalshiFetcher {
217
217
  event.tags = series.tags;
218
218
  }
219
219
  }
220
- catch {
221
- // Non-critical
220
+ catch (err) {
221
+ // Non-critical — tags are enrichment only.
222
+ console.warn('[kalshi] series tag fetch failed', {
223
+ series_ticker: event.series_ticker,
224
+ error: err instanceof Error ? err.message : String(err),
225
+ });
222
226
  }
223
227
  }
224
228
  return [event];
@@ -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-05-08T20:51:55.531Z
3
+ * Generated at: 2026-05-08T23:06:00.134Z
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-05-08T20:51:55.531Z
6
+ * Generated at: 2026-05-08T23:06:00.134Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -273,8 +273,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
273
273
  const client = this.ensureClient();
274
274
  try {
275
275
  if (!marketId) {
276
- console.warn('Limitless: fetchOpenOrders requires marketId (slug) to be efficient. Returning [].');
277
- return [];
276
+ throw new Error('Limitless: fetchOpenOrders requires marketId (slug).');
278
277
  }
279
278
  const orders = await client.getOrders(marketId, ['LIVE']);
280
279
  return orders.map((o) => ({
@@ -298,8 +297,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
298
297
  async fetchClosedOrders(params) {
299
298
  const client = this.ensureClient();
300
299
  if (!params?.marketId) {
301
- console.warn('Limitless: fetchClosedOrders requires marketId (slug). Returning [].');
302
- return [];
300
+ throw new Error('Limitless: fetchClosedOrders requires marketId (slug).');
303
301
  }
304
302
  const orders = await client.getOrders(params.marketId, ['MATCHED']);
305
303
  return orders.map((o) => ({
@@ -319,8 +317,7 @@ class LimitlessExchange extends BaseExchange_1.PredictionMarketExchange {
319
317
  async fetchAllOrders(params) {
320
318
  const client = this.ensureClient();
321
319
  if (!params?.marketId) {
322
- console.warn('Limitless: fetchAllOrders requires marketId (slug). Returning [].');
323
- return [];
320
+ throw new Error('Limitless: fetchAllOrders requires marketId (slug).');
324
321
  }
325
322
  const orders = await client.getOrders(params.marketId, ['LIVE', 'MATCHED']);
326
323
  return orders.map((o) => ({
@@ -36,6 +36,13 @@ function mapMarketToUnified(market) {
36
36
  metadata: { clobTokenId: market.tokens.no },
37
37
  });
38
38
  }
39
+ // Limitless returns status='FUNDED' for active markets and expired=true
40
+ // when the market has ended. Map to the same canonical values Polymarket uses.
41
+ let status;
42
+ if (market.expired === true)
43
+ status = 'closed';
44
+ else if (market.status === 'FUNDED')
45
+ status = 'active';
39
46
  const um = {
40
47
  id: market.slug,
41
48
  marketId: market.slug,
@@ -52,7 +59,8 @@ function mapMarketToUnified(market) {
52
59
  url: `https://limitless.exchange/markets/${market.slug}`,
53
60
  image: market.logo || `https://limitless.exchange/api/og?slug=${market.slug}`,
54
61
  category: market.categories?.[0],
55
- tags: market.tags || []
62
+ tags: market.tags || [],
63
+ status,
56
64
  };
57
65
  (0, market_utils_1.addBinaryOutcomes)(um);
58
66
  return um;
@@ -120,8 +120,13 @@ async function fetchEventBySlug(slug, callApi) {
120
120
  ];
121
121
  }
122
122
  }
123
- catch {
124
- // fall through
123
+ catch (err) {
124
+ // A 404 means this slug is not a known tournament — fall through to
125
+ // the next lookup strategy. Any other error (network, auth, etc.)
126
+ // is a real failure and must propagate.
127
+ if (!(err instanceof Error) || !('status' in err) || err.status !== 404) {
128
+ throw err;
129
+ }
125
130
  }
126
131
  // Finally try slug match against post.slug / post.url_title
127
132
  const posts = await fetchPostPages(callApi, { with_cp: true, order_by: "-forecasts_count" }, 500);
@@ -0,0 +1,55 @@
1
+ import { EventFetchParams, MarketFetchParams, OHLCVParams, PredictionMarketExchange, TradesParams } from '../../BaseExchange';
2
+ import { Balance, BuiltOrder, CreateOrderParams, Order, OrderBook, Position, PriceCandle, Trade, UnifiedEvent, UnifiedMarket, UserTrade } from '../../types';
3
+ export interface MockExchangeOptions {
4
+ marketCount?: number;
5
+ balance?: number;
6
+ orderLatencyMs?: number;
7
+ limitOrderMode?: 'immediate' | 'resting';
8
+ }
9
+ export declare class MockExchange extends PredictionMarketExchange {
10
+ private readonly _marketCount;
11
+ private readonly _initialBalance;
12
+ private readonly _orderLatencyMs;
13
+ private readonly _limitOrderMode;
14
+ private _generatedMarkets?;
15
+ private _generatedEvents?;
16
+ private _freeCash;
17
+ private _ordSeq;
18
+ private _orders;
19
+ private _lockedByBuy;
20
+ private _positions;
21
+ private _myTrades;
22
+ constructor(options?: MockExchangeOptions);
23
+ get name(): string;
24
+ private _locked;
25
+ private _bookMidPrice;
26
+ private _generateMarket;
27
+ private _buildMarkets;
28
+ private _buildEvents;
29
+ protected fetchMarketsImpl(params?: MarketFetchParams): Promise<UnifiedMarket[]>;
30
+ protected fetchEventsImpl(params: EventFetchParams): Promise<UnifiedEvent[]>;
31
+ fetchOrderBook(id: string): Promise<OrderBook>;
32
+ fetchOHLCV(id: string, params: OHLCVParams): Promise<PriceCandle[]>;
33
+ fetchTrades(id: string, _params: TradesParams): Promise<Trade[]>;
34
+ fetchBalance(_address?: string): Promise<Balance[]>;
35
+ fetchPositions(_address?: string): Promise<Position[]>;
36
+ private _nextOrderId;
37
+ private _setPosition;
38
+ private _pushTrade;
39
+ private _applyInstantFill;
40
+ private _placeRestingLimit;
41
+ createOrder(params: CreateOrderParams): Promise<Order>;
42
+ fillOrder(orderId: string, amount?: number): Promise<Order>;
43
+ cancelOrder(orderId: string): Promise<Order>;
44
+ fetchOrder(orderId: string): Promise<Order>;
45
+ fetchOpenOrders(_marketId?: string): Promise<Order[]>;
46
+ fetchMyTrades(_params?: {
47
+ outcomeId?: string;
48
+ marketId?: string;
49
+ }): Promise<UserTrade[]>;
50
+ fetchClosedOrders(): Promise<Order[]>;
51
+ fetchAllOrders(): Promise<Order[]>;
52
+ buildOrder(params: CreateOrderParams): Promise<BuiltOrder>;
53
+ submitOrder(built: BuiltOrder): Promise<Order>;
54
+ reset(): void;
55
+ }