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
@@ -5,6 +5,7 @@ exports.MyriadWebSocket = void 0;
5
5
  // We implement a poll-based fallback that resolves promises
6
6
  // on each polling interval, matching the CCXT Pro async pattern.
7
7
  const DEFAULT_POLL_INTERVAL = 5000; // 5 seconds
8
+ const MAX_CONSECUTIVE_FAILURES = 5;
8
9
  class MyriadWebSocket {
9
10
  callApi;
10
11
  fetchOrderBook;
@@ -12,8 +13,12 @@ class MyriadWebSocket {
12
13
  orderBookTimers = new Map();
13
14
  tradeTimers = new Map();
14
15
  orderBookResolvers = new Map();
16
+ orderBookRejecters = new Map();
15
17
  tradeResolvers = new Map();
18
+ tradeRejecters = new Map();
16
19
  lastTradeTimestamp = new Map();
20
+ orderBookFailureCount = new Map();
21
+ tradeFailureCount = new Map();
17
22
  closed = false;
18
23
  constructor(callApi, fetchOrderBook, pollInterval) {
19
24
  this.callApi = callApi;
@@ -23,11 +28,13 @@ class MyriadWebSocket {
23
28
  async watchOrderBook(outcomeId) {
24
29
  if (this.closed)
25
30
  throw new Error('WebSocket connection is closed');
26
- return new Promise((resolve) => {
31
+ return new Promise((resolve, reject) => {
27
32
  if (!this.orderBookResolvers.has(outcomeId)) {
28
33
  this.orderBookResolvers.set(outcomeId, []);
34
+ this.orderBookRejecters.set(outcomeId, []);
29
35
  }
30
36
  this.orderBookResolvers.get(outcomeId).push(resolve);
37
+ this.orderBookRejecters.get(outcomeId).push(reject);
31
38
  if (!this.orderBookTimers.has(outcomeId)) {
32
39
  this.startOrderBookPolling(outcomeId);
33
40
  }
@@ -36,11 +43,13 @@ class MyriadWebSocket {
36
43
  async watchTrades(outcomeId) {
37
44
  if (this.closed)
38
45
  throw new Error('WebSocket connection is closed');
39
- return new Promise((resolve) => {
46
+ return new Promise((resolve, reject) => {
40
47
  if (!this.tradeResolvers.has(outcomeId)) {
41
48
  this.tradeResolvers.set(outcomeId, []);
49
+ this.tradeRejecters.set(outcomeId, []);
42
50
  }
43
51
  this.tradeResolvers.get(outcomeId).push(resolve);
52
+ this.tradeRejecters.get(outcomeId).push(reject);
44
53
  if (!this.tradeTimers.has(outcomeId)) {
45
54
  this.startTradePolling(outcomeId);
46
55
  }
@@ -57,20 +66,39 @@ class MyriadWebSocket {
57
66
  this.orderBookTimers.clear();
58
67
  this.tradeTimers.clear();
59
68
  this.orderBookResolvers.clear();
69
+ this.orderBookRejecters.clear();
60
70
  this.tradeResolvers.clear();
71
+ this.tradeRejecters.clear();
61
72
  }
62
73
  startOrderBookPolling(id) {
63
74
  const poll = async () => {
64
75
  try {
65
76
  const book = await this.fetchOrderBook(id);
77
+ this.orderBookFailureCount.set(id, 0);
66
78
  const resolvers = this.orderBookResolvers.get(id) || [];
67
79
  this.orderBookResolvers.set(id, []);
80
+ this.orderBookRejecters.set(id, []);
68
81
  for (const resolve of resolvers) {
69
82
  resolve(book);
70
83
  }
71
84
  }
72
- catch {
73
- // Silently retry on next interval
85
+ catch (error) {
86
+ const failures = (this.orderBookFailureCount.get(id) || 0) + 1;
87
+ this.orderBookFailureCount.set(id, failures);
88
+ console.warn(`[Myriad] watchOrderBook poll failed for outcomeId=${id} (consecutive failures: ${failures}):`, error);
89
+ if (failures >= MAX_CONSECUTIVE_FAILURES) {
90
+ const timer = this.orderBookTimers.get(id);
91
+ if (timer)
92
+ clearInterval(timer);
93
+ this.orderBookTimers.delete(id);
94
+ this.orderBookFailureCount.delete(id);
95
+ const rejecters = this.orderBookRejecters.get(id) || [];
96
+ this.orderBookResolvers.set(id, []);
97
+ this.orderBookRejecters.set(id, []);
98
+ for (const reject of rejecters) {
99
+ reject(error);
100
+ }
101
+ }
74
102
  }
75
103
  };
76
104
  // Immediate first poll
@@ -110,14 +138,31 @@ class MyriadWebSocket {
110
138
  const maxTs = Math.max(...trades.map(t => t.timestamp));
111
139
  this.lastTradeTimestamp.set(id, maxTs + 1);
112
140
  }
141
+ this.tradeFailureCount.set(id, 0);
113
142
  const resolvers = this.tradeResolvers.get(id) || [];
114
143
  this.tradeResolvers.set(id, []);
144
+ this.tradeRejecters.set(id, []);
115
145
  for (const resolve of resolvers) {
116
146
  resolve(trades);
117
147
  }
118
148
  }
119
- catch {
120
- // Silently retry on next interval
149
+ catch (error) {
150
+ const failures = (this.tradeFailureCount.get(id) || 0) + 1;
151
+ this.tradeFailureCount.set(id, failures);
152
+ console.warn(`[Myriad] watchTrades poll failed for outcomeId=${id} (consecutive failures: ${failures}):`, error);
153
+ if (failures >= MAX_CONSECUTIVE_FAILURES) {
154
+ const timer = this.tradeTimers.get(id);
155
+ if (timer)
156
+ clearInterval(timer);
157
+ this.tradeTimers.delete(id);
158
+ this.tradeFailureCount.delete(id);
159
+ const rejecters = this.tradeRejecters.get(id) || [];
160
+ this.tradeResolvers.set(id, []);
161
+ this.tradeRejecters.set(id, []);
162
+ for (const reject of rejecters) {
163
+ reject(error);
164
+ }
165
+ }
121
166
  }
122
167
  };
123
168
  poll();
@@ -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-05-08T20:51:55.548Z
3
+ * Generated at: 2026-05-08T23:06:00.151Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const opinionApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.opinionApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/opinion/opinion-openapi.yaml
6
- * Generated at: 2026-05-08T20:51:55.548Z
6
+ * Generated at: 2026-05-08T23:06:00.151Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
3
- * Generated at: 2026-05-08T20:51:55.496Z
3
+ * Generated at: 2026-05-08T23:06:00.102Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketClobSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketClobSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketClobAPI.yaml
6
- * Generated at: 2026-05-08T20:51:55.496Z
6
+ * Generated at: 2026-05-08T23:06:00.102Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketClobSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
3
- * Generated at: 2026-05-08T20:51:55.511Z
3
+ * Generated at: 2026-05-08T23:06:00.118Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketDataSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketDataSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/Polymarket_Data_API.yaml
6
- * Generated at: 2026-05-08T20:51:55.511Z
6
+ * Generated at: 2026-05-08T23:06:00.118Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketDataSpec = {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
3
- * Generated at: 2026-05-08T20:51:55.508Z
3
+ * Generated at: 2026-05-08T23:06:00.115Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const polymarketGammaSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.polymarketGammaSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/polymarket/PolymarketGammaAPI.yaml
6
- * Generated at: 2026-05-08T20:51:55.508Z
6
+ * Generated at: 2026-05-08T23:06:00.115Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -214,8 +214,11 @@ class PolymarketAuth {
214
214
  signatureType = discovered.signatureType;
215
215
  }
216
216
  }
217
- catch {
218
- // Discovery failure — fall through to default below.
217
+ catch (err) {
218
+ // Discovery failure — fall through to default (Gnosis Safe) below.
219
+ // A network/HTTP error here does not block trading; we just lose
220
+ // the ability to auto-detect signatureType.
221
+ console.warn('[polymarket] signature-type discovery failed; defaulting to Gnosis Safe', { error: err instanceof Error ? err.message : String(err) });
219
222
  }
220
223
  }
221
224
  if (signatureType === undefined) {
@@ -358,7 +358,8 @@ class PolymarketExchange extends BaseExchange_1.PredictionMarketExchange {
358
358
  total = onChain;
359
359
  }
360
360
  }
361
- catch {
361
+ catch (err) {
362
+ console.warn('[polymarket] on-chain balance lookup failed; using CLOB balance only', { error: err instanceof Error ? err.message : String(err) });
362
363
  }
363
364
  }
364
365
  // 2. Fetch open orders to calculate locked funds.
@@ -136,7 +136,7 @@ function intentToSide(intent) {
136
136
  case 'ORDER_INTENT_SELL_SHORT':
137
137
  return 'sell';
138
138
  default:
139
- return 'buy';
139
+ throw new Error(`[polymarket_us] unknown order intent: ${String(intent)}`);
140
140
  }
141
141
  }
142
142
  function intentToOutcomeId(intent, slug) {
@@ -378,6 +378,10 @@ class PolymarketUSNormalizer {
378
378
  timestamp: parseTimeToMs(trade.createTime),
379
379
  price: (0, price_1.fromAmount)(trade.price),
380
380
  amount: parseFloat(trade.qty || '0'),
381
+ // The Polymarket US activity trade object (Trade$1) does not carry
382
+ // an intent/side field — only price, qty, and state are provided.
383
+ // Side cannot be derived from activity data; consumers must cross-reference
384
+ // with their own order history if side is required.
381
385
  side: 'unknown',
382
386
  outcomeId: undefined,
383
387
  };
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
3
- * Generated at: 2026-05-08T20:51:55.535Z
3
+ * Generated at: 2026-05-08T23:06:00.139Z
4
4
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
5
5
  */
6
6
  export declare const probableApiSpec: {
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.probableApiSpec = void 0;
4
4
  /**
5
5
  * Auto-generated from /home/runner/work/pmxt/pmxt/core/specs/probable/probable.yaml
6
- * Generated at: 2026-05-08T20:51:55.535Z
6
+ * Generated at: 2026-05-08T23:06:00.139Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -272,13 +272,14 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
272
272
  }
273
273
  }
274
274
  async fetchOpenOrders(marketId) {
275
+ if (!marketId) {
276
+ throw new Error('[Probable] fetchOpenOrders requires a marketId: this exchange does not support fetching all orders across all markets');
277
+ }
275
278
  try {
276
279
  const auth = this.ensureAuth();
277
280
  const client = auth.getClobClient();
278
281
  const params = {};
279
- if (marketId) {
280
- params.eventId = marketId;
281
- }
282
+ params.eventId = marketId;
282
283
  const orders = await client.getOpenOrders(params);
283
284
  const orderList = Array.isArray(orders) ? orders : orders?.data || [];
284
285
  return orderList.map((o) => ({
@@ -330,7 +331,8 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
330
331
  total = parseFloat(formatUnits(balance, 18));
331
332
  }
332
333
  catch (chainError) {
333
- // On-chain check failed, return 0
334
+ console.warn('[Probable] fetchBalance: on-chain USDT balance fetch failed:', chainError);
335
+ throw chainError;
334
336
  }
335
337
  // Calculate locked from open BUY orders
336
338
  let locked = 0;
@@ -342,8 +344,9 @@ class ProbableExchange extends BaseExchange_1.PredictionMarketExchange {
342
344
  }
343
345
  }
344
346
  }
345
- catch {
346
- // If we can't fetch orders, locked stays 0
347
+ catch (ordersError) {
348
+ console.warn('[Probable] fetchBalance: failed to fetch open orders for locked balance calculation:', ordersError);
349
+ throw ordersError;
347
350
  }
348
351
  return [{
349
352
  currency: 'USDT',
@@ -313,8 +313,12 @@ class SmarketsFetcher {
313
313
  });
314
314
  return (data.volumes || []);
315
315
  }
316
- catch {
317
- // Volumes are non-critical; return empty on failure
316
+ catch (err) {
317
+ // Volumes are non-critical; return empty on failure but log it.
318
+ console.warn('[smarkets] volume fetch failed for batch', {
319
+ marketIds: batch,
320
+ error: err instanceof Error ? err.message : String(err),
321
+ });
318
322
  return [];
319
323
  }
320
324
  }));
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './types';
3
3
  export * from './utils/math';
4
4
  export { parseOpenApiSpec } from './utils/openapi';
5
5
  export * from './errors';
6
+ export * from './exchanges/mock';
6
7
  export * from './exchanges/polymarket';
7
8
  export * from './exchanges/limitless';
8
9
  export * from './exchanges/kalshi';
@@ -15,10 +16,12 @@ export * from './exchanges/metaculus';
15
16
  export * from './exchanges/smarkets';
16
17
  export * from './exchanges/polymarket_us';
17
18
  export * from './exchanges/hyperliquid';
19
+ export * from './exchanges/gemini-titan';
18
20
  export * from './router';
19
21
  export * from './server/app';
20
22
  export * from './server/utils/port-manager';
21
23
  export * from './server/utils/lock-file';
24
+ import { MockExchange } from './exchanges/mock';
22
25
  import { PolymarketExchange } from './exchanges/polymarket';
23
26
  import { LimitlessExchange } from './exchanges/limitless';
24
27
  import { KalshiExchange } from './exchanges/kalshi';
@@ -31,8 +34,10 @@ import { MetaculusExchange } from './exchanges/metaculus';
31
34
  import { SmarketsExchange } from './exchanges/smarkets';
32
35
  import { PolymarketUSExchange } from './exchanges/polymarket_us';
33
36
  import { HyperliquidExchange } from './exchanges/hyperliquid';
37
+ import { GeminiTitanExchange } from './exchanges/gemini-titan';
34
38
  import { Router } from './router';
35
39
  declare const pmxt: {
40
+ Mock: typeof MockExchange;
36
41
  Polymarket: typeof PolymarketExchange;
37
42
  Limitless: typeof LimitlessExchange;
38
43
  Kalshi: typeof KalshiExchange;
@@ -45,8 +50,10 @@ declare const pmxt: {
45
50
  Smarkets: typeof SmarketsExchange;
46
51
  PolymarketUS: typeof PolymarketUSExchange;
47
52
  Hyperliquid: typeof HyperliquidExchange;
53
+ GeminiTitan: typeof GeminiTitanExchange;
48
54
  Router: typeof Router;
49
55
  };
56
+ export declare const Mock: typeof MockExchange;
50
57
  export declare const Polymarket: typeof PolymarketExchange;
51
58
  export declare const Limitless: typeof LimitlessExchange;
52
59
  export declare const Kalshi: typeof KalshiExchange;
@@ -59,4 +66,5 @@ export declare const Metaculus: typeof MetaculusExchange;
59
66
  export declare const Smarkets: typeof SmarketsExchange;
60
67
  export declare const PolymarketUS: typeof PolymarketUSExchange;
61
68
  export declare const Hyperliquid: typeof HyperliquidExchange;
69
+ export declare const GeminiTitan: typeof GeminiTitanExchange;
62
70
  export default pmxt;
package/dist/index.js CHANGED
@@ -14,13 +14,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.parseOpenApiSpec = void 0;
17
+ exports.GeminiTitan = exports.Hyperliquid = exports.PolymarketUS = exports.Smarkets = exports.Metaculus = exports.Opinion = exports.Myriad = exports.Baozi = exports.Probable = exports.KalshiDemo = exports.Kalshi = exports.Limitless = exports.Polymarket = exports.Mock = exports.parseOpenApiSpec = void 0;
18
18
  __exportStar(require("./BaseExchange"), exports);
19
19
  __exportStar(require("./types"), exports);
20
20
  __exportStar(require("./utils/math"), exports);
21
21
  var openapi_1 = require("./utils/openapi");
22
22
  Object.defineProperty(exports, "parseOpenApiSpec", { enumerable: true, get: function () { return openapi_1.parseOpenApiSpec; } });
23
23
  __exportStar(require("./errors"), exports);
24
+ __exportStar(require("./exchanges/mock"), exports);
24
25
  __exportStar(require("./exchanges/polymarket"), exports);
25
26
  __exportStar(require("./exchanges/limitless"), exports);
26
27
  __exportStar(require("./exchanges/kalshi"), exports);
@@ -33,10 +34,12 @@ __exportStar(require("./exchanges/metaculus"), exports);
33
34
  __exportStar(require("./exchanges/smarkets"), exports);
34
35
  __exportStar(require("./exchanges/polymarket_us"), exports);
35
36
  __exportStar(require("./exchanges/hyperliquid"), exports);
37
+ __exportStar(require("./exchanges/gemini-titan"), exports);
36
38
  __exportStar(require("./router"), exports);
37
39
  __exportStar(require("./server/app"), exports);
38
40
  __exportStar(require("./server/utils/port-manager"), exports);
39
41
  __exportStar(require("./server/utils/lock-file"), exports);
42
+ const mock_1 = require("./exchanges/mock");
40
43
  const polymarket_1 = require("./exchanges/polymarket");
41
44
  const limitless_1 = require("./exchanges/limitless");
42
45
  const kalshi_1 = require("./exchanges/kalshi");
@@ -49,8 +52,10 @@ const metaculus_1 = require("./exchanges/metaculus");
49
52
  const smarkets_1 = require("./exchanges/smarkets");
50
53
  const polymarket_us_1 = require("./exchanges/polymarket_us");
51
54
  const hyperliquid_1 = require("./exchanges/hyperliquid");
55
+ const gemini_titan_1 = require("./exchanges/gemini-titan");
52
56
  const router_1 = require("./router");
53
57
  const pmxt = {
58
+ Mock: mock_1.MockExchange,
54
59
  Polymarket: polymarket_1.PolymarketExchange,
55
60
  Limitless: limitless_1.LimitlessExchange,
56
61
  Kalshi: kalshi_1.KalshiExchange,
@@ -63,8 +68,10 @@ const pmxt = {
63
68
  Smarkets: smarkets_1.SmarketsExchange,
64
69
  PolymarketUS: polymarket_us_1.PolymarketUSExchange,
65
70
  Hyperliquid: hyperliquid_1.HyperliquidExchange,
71
+ GeminiTitan: gemini_titan_1.GeminiTitanExchange,
66
72
  Router: router_1.Router,
67
73
  };
74
+ exports.Mock = mock_1.MockExchange;
68
75
  exports.Polymarket = polymarket_1.PolymarketExchange;
69
76
  exports.Limitless = limitless_1.LimitlessExchange;
70
77
  exports.Kalshi = kalshi_1.KalshiExchange;
@@ -77,4 +84,5 @@ exports.Metaculus = metaculus_1.MetaculusExchange;
77
84
  exports.Smarkets = smarkets_1.SmarketsExchange;
78
85
  exports.PolymarketUS = polymarket_us_1.PolymarketUSExchange;
79
86
  exports.Hyperliquid = hyperliquid_1.HyperliquidExchange;
87
+ exports.GeminiTitan = gemini_titan_1.GeminiTitanExchange;
80
88
  exports.default = pmxt;
@@ -50,7 +50,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
50
50
  offset: params?.offset,
51
51
  closed: params?.status === 'closed' || params?.status === 'inactive',
52
52
  });
53
- return response ?? [];
53
+ if (!Array.isArray(response)) {
54
+ throw new Error(`fetchMarketsImpl: expected array from searchMarkets but received ${typeof response}`);
55
+ }
56
+ return response;
54
57
  }
55
58
  async fetchEventsImpl(params) {
56
59
  const response = await this.client.searchEvents({
@@ -59,7 +62,10 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
59
62
  limit: params?.limit,
60
63
  offset: params?.offset,
61
64
  });
62
- return response ?? [];
65
+ if (!Array.isArray(response)) {
66
+ throw new Error(`fetchEventsImpl: expected array from searchEvents but received ${typeof response}`);
67
+ }
68
+ return response;
63
69
  }
64
70
  // -----------------------------------------------------------------------
65
71
  // Unified orderbook (cross-exchange merge)
@@ -86,19 +92,33 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
86
92
  const outcome = findOutcomeForSide(match.market, resolvedSide);
87
93
  if (!outcome)
88
94
  continue;
89
- fetchPromises.push(exchange.fetchOrderBook(outcome.outcomeId, resolvedSide).catch(() => null));
95
+ fetchPromises.push(exchange
96
+ .fetchOrderBook(outcome.outcomeId, resolvedSide)
97
+ .then((book) => ({ book, venue: venueName, error: null }))
98
+ .catch((error) => ({ book: null, venue: venueName, error })));
90
99
  }
91
100
  // Fetch the source market's orderbook (try remaining exchanges with the raw ID)
92
101
  for (const [name, exchange] of Object.entries(this.exchanges)) {
93
102
  if (matchedVenues.has(name))
94
103
  continue;
95
- fetchPromises.push(exchange.fetchOrderBook(outcomeId, resolvedSide).catch(() => null));
104
+ fetchPromises.push(exchange
105
+ .fetchOrderBook(outcomeId, resolvedSide)
106
+ .then((book) => ({ book, venue: name, error: null }))
107
+ .catch((error) => ({ book: null, venue: name, error })));
108
+ }
109
+ const results = await Promise.all(fetchPromises);
110
+ const books = results.filter((r) => r.book !== null);
111
+ const failures = results.filter((r) => r.book === null && r.error !== null);
112
+ if (books.length === 0 && failures.length > 0) {
113
+ const reasons = failures
114
+ .map((f) => `${f.venue}: ${f.error instanceof Error ? f.error.message : String(f.error)}`)
115
+ .join('; ');
116
+ throw new Error(`fetchOrderBook failed on all exchanges for outcomeId "${outcomeId}": ${reasons}`);
96
117
  }
97
- const books = (await Promise.all(fetchPromises)).filter((b) => b !== null);
98
118
  if (books.length === 0) {
99
- return { bids: [], asks: [], timestamp: Date.now() };
119
+ throw new Error(`fetchOrderBook: no exchange returned an orderbook for outcomeId "${outcomeId}"`);
100
120
  }
101
- return mergeOrderBooks(books);
121
+ return mergeOrderBooks(books.map((r) => r.book));
102
122
  }
103
123
  // -----------------------------------------------------------------------
104
124
  // Cross-exchange market matches
@@ -259,9 +279,16 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
259
279
  try {
260
280
  return await this.fetchArbitrageBulk(params);
261
281
  }
262
- catch {
263
- // Dedicated endpoint not available fall back to N+1 approach.
264
- return this.fetchArbitrageFallback(params);
282
+ catch (error) {
283
+ // Only fall back when the bulk endpoint is genuinely not available (404/501).
284
+ // All other errors (network failures, 5xx, parsing errors) propagate so
285
+ // callers are not silently given stale N+1 data.
286
+ const status = error?.status ?? error?.response?.status;
287
+ if (status === 404 || status === 501) {
288
+ console.warn('[pmxt] Router: bulk arbitrage endpoint unavailable, falling back to N+1 approach');
289
+ return this.fetchArbitrageFallback(params);
290
+ }
291
+ throw error;
265
292
  }
266
293
  }
267
294
  /**
@@ -280,17 +307,24 @@ class Router extends BaseExchange_1.PredictionMarketExchange {
280
307
  const res = await this.client.getArbitrage(query);
281
308
  // getArbitrage already unwraps .data — res is the opportunities array.
282
309
  const items = Array.isArray(res) ? res : (res?.data ?? []);
283
- return items.map((r) => ({
284
- marketA: r.marketA,
285
- marketB: r.marketB,
286
- spread: r.spread ?? 0,
287
- buyVenue: r.buyVenue ?? '',
288
- sellVenue: r.sellVenue ?? '',
289
- buyPrice: r.buyPrice ?? 0,
290
- sellPrice: r.sellPrice ?? 0,
291
- relation: r.relation,
292
- confidence: r.confidence,
293
- }));
310
+ return items.map((r) => {
311
+ if (r.spread == null || r.buyPrice == null || r.sellPrice == null) {
312
+ throw new Error(`fetchArbitrageBulk: arbitrage record is missing required price fields ` +
313
+ `(spread=${r.spread}, buyPrice=${r.buyPrice}, sellPrice=${r.sellPrice}) ` +
314
+ `for markets ${r.marketA?.marketId ?? '?'} / ${r.marketB?.marketId ?? '?'}`);
315
+ }
316
+ return {
317
+ marketA: r.marketA,
318
+ marketB: r.marketB,
319
+ spread: r.spread,
320
+ buyVenue: r.buyVenue ?? '',
321
+ sellVenue: r.sellVenue ?? '',
322
+ buyPrice: r.buyPrice,
323
+ sellPrice: r.sellPrice,
324
+ relation: r.relation,
325
+ confidence: r.confidence,
326
+ };
327
+ });
294
328
  }
295
329
  /**
296
330
  * Legacy N+1 fallback: fetch markets, then fetch matches per-market.
@@ -13,6 +13,8 @@ const metaculus_1 = require("../exchanges/metaculus");
13
13
  const smarkets_1 = require("../exchanges/smarkets");
14
14
  const polymarket_us_1 = require("../exchanges/polymarket_us");
15
15
  const hyperliquid_1 = require("../exchanges/hyperliquid");
16
+ const gemini_titan_1 = require("../exchanges/gemini-titan");
17
+ const mock_1 = require("../exchanges/mock");
16
18
  const router_1 = require("../router");
17
19
  function createExchange(name, credentials, bearerToken) {
18
20
  switch (name) {
@@ -96,6 +98,13 @@ function createExchange(name, credentials, bearerToken) {
96
98
  apiKey: credentials?.apiKey || process.env.HYPERLIQUID_WALLET_ADDRESS,
97
99
  privateKey: credentials?.privateKey || process.env.HYPERLIQUID_PRIVATE_KEY,
98
100
  });
101
+ case "gemini-titan":
102
+ return new gemini_titan_1.GeminiTitanExchange({
103
+ apiKey: credentials?.apiKey || process.env.GEMINI_API_KEY,
104
+ apiSecret: credentials?.apiSecret || process.env.GEMINI_API_SECRET,
105
+ });
106
+ case "mock":
107
+ return new mock_1.MockExchange();
99
108
  case "router":
100
109
  return new router_1.Router({
101
110
  apiKey: bearerToken,
@@ -3684,6 +3684,28 @@ x-sdk-constructors:
3684
3684
  tsName: privateKey
3685
3685
  type: string
3686
3686
  description: Private key for authentication
3687
+ gemini-titan:
3688
+ className: GeminiTitan
3689
+ params:
3690
+ - name: pmxt_api_key
3691
+ tsName: pmxtApiKey
3692
+ type: string
3693
+ description: PMXT API key for hosted access
3694
+ - name: api_key
3695
+ tsName: apiKey
3696
+ type: string
3697
+ description: API key for authentication
3698
+ - name: api_secret
3699
+ tsName: apiSecret
3700
+ type: string
3701
+ description: API secret for authentication
3702
+ mock:
3703
+ className: Mock
3704
+ params:
3705
+ - name: pmxt_api_key
3706
+ tsName: pmxtApiKey
3707
+ type: string
3708
+ description: PMXT API key for hosted access
3687
3709
  router:
3688
3710
  className: Router
3689
3711
  params:
@@ -196,7 +196,9 @@ function createWebSocketHandler(options = {}) {
196
196
  // Close all exchange instances
197
197
  for (const [, exchange] of state.exchanges) {
198
198
  if (typeof exchange.close === "function") {
199
- exchange.close().catch(() => { });
199
+ exchange.close().catch((err) => {
200
+ console.warn('[ws-handler] exchange close() failed', { error: err instanceof Error ? err.message : String(err) });
201
+ });
200
202
  }
201
203
  }
202
204
  state.exchanges.clear();
@@ -254,8 +256,16 @@ function handleSubscribe(ws, state, msg, exchangeName) {
254
256
  state.subscriptions.set(key, { abortController });
255
257
  const streamFn = method === "watchOrderBooks" ? streamBatch : streamSingle;
256
258
  // Fire and forget -- the loop runs until aborted or WS closes
257
- streamFn(exchange, method, args || [], id, ws, abortController.signal).catch(() => {
258
- // Stream ended -- clean up
259
+ streamFn(exchange, method, args || [], id, ws, abortController.signal).catch((err) => {
260
+ // Unexpected stream rejection (programming error — exchange errors are
261
+ // caught and reported to the client inside streamSingle/streamBatch).
262
+ console.warn('[ws-handler] stream ended with unexpected error', {
263
+ exchange: exchangeName,
264
+ method,
265
+ id,
266
+ error: err instanceof Error ? err.message : String(err),
267
+ });
268
+ sendError(ws, id, err instanceof Error ? err.message : 'Streaming error');
259
269
  state.subscriptions.delete(key);
260
270
  });
261
271
  }