pmxt-core 2.49.6 → 2.49.8

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 (47) hide show
  1. package/dist/exchanges/baozi/fetcher.js +2 -1
  2. package/dist/exchanges/gemini-titan/fetcher.d.ts +2 -4
  3. package/dist/exchanges/gemini-titan/fetcher.js +40 -13
  4. package/dist/exchanges/gemini-titan/index.js +2 -13
  5. package/dist/exchanges/gemini-titan/normalizer.js +3 -1
  6. package/dist/exchanges/gemini-titan/types.d.ts +4 -0
  7. package/dist/exchanges/gemini-titan/websocket.d.ts +2 -1
  8. package/dist/exchanges/gemini-titan/websocket.js +13 -5
  9. package/dist/exchanges/hyperliquid/fetcher.d.ts +8 -1
  10. package/dist/exchanges/kalshi/api.d.ts +1 -1
  11. package/dist/exchanges/kalshi/api.js +1 -1
  12. package/dist/exchanges/kalshi/fetcher.d.ts +10 -0
  13. package/dist/exchanges/kalshi/normalizer.js +26 -7
  14. package/dist/exchanges/limitless/api.d.ts +1 -1
  15. package/dist/exchanges/limitless/api.js +1 -1
  16. package/dist/exchanges/limitless/fetcher.d.ts +2 -1
  17. package/dist/exchanges/limitless/utils.js +19 -2
  18. package/dist/exchanges/myriad/api.d.ts +1 -1
  19. package/dist/exchanges/myriad/api.js +1 -1
  20. package/dist/exchanges/myriad/fetcher.d.ts +11 -0
  21. package/dist/exchanges/myriad/fetcher.js +3 -1
  22. package/dist/exchanges/myriad/normalizer.js +2 -2
  23. package/dist/exchanges/opinion/api.d.ts +1 -1
  24. package/dist/exchanges/opinion/api.js +1 -1
  25. package/dist/exchanges/opinion/fetcher.js +3 -2
  26. package/dist/exchanges/opinion/index.js +8 -4
  27. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  28. package/dist/exchanges/polymarket/api-clob.js +1 -1
  29. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  30. package/dist/exchanges/polymarket/api-data.js +1 -1
  31. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  32. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  33. package/dist/exchanges/polymarket/auth.js +0 -5
  34. package/dist/exchanges/polymarket/fetcher.d.ts +2 -0
  35. package/dist/exchanges/polymarket/fetcher.js +12 -7
  36. package/dist/exchanges/polymarket/normalizer.js +3 -0
  37. package/dist/exchanges/probable/api.d.ts +1 -1
  38. package/dist/exchanges/probable/api.js +1 -1
  39. package/dist/exchanges/probable/auth.js +1 -1
  40. package/dist/exchanges/probable/websocket.d.ts +1 -1
  41. package/dist/exchanges/probable/websocket.js +1 -1
  42. package/dist/router/e2e-orderbook.js +17 -16
  43. package/dist/server/index.js +12 -3
  44. package/dist/server/openapi.yaml +10 -0
  45. package/dist/server/utils/lock-file.js +5 -2
  46. package/dist/types.d.ts +6 -0
  47. package/package.json +3 -3
@@ -100,7 +100,8 @@ class BaoziFetcher {
100
100
  }
101
101
  return null;
102
102
  }
103
- catch {
103
+ catch (err) {
104
+ logger_1.logger.warn('BaoziFetcher: fetchRawSingleMarket failed', { pubkey, error: String(err) });
104
105
  return null;
105
106
  }
106
107
  }
@@ -14,12 +14,10 @@ export declare class GeminiFetcher implements IExchangeFetcher<GeminiRawEvent, G
14
14
  fetchRawOrderBook(instrumentSymbol: string): Promise<GeminiRawOrderBook | undefined>;
15
15
  getEventTickerForSymbol(instrumentSymbol: string): string | undefined;
16
16
  submitRawOrder(payload: Record<string, unknown>): Promise<GeminiRawOrder>;
17
- cancelRawOrder(orderId: number): Promise<{
18
- result: string;
19
- message: string;
20
- }>;
17
+ cancelRawOrder(orderId: number): Promise<GeminiRawOrder>;
21
18
  fetchRawActiveOrders(symbol?: string): Promise<GeminiRawOrder[]>;
22
19
  fetchRawOrderHistory(): Promise<GeminiRawOrder[]>;
20
+ private fetchPaginatedOrders;
23
21
  fetchRawPositions(): Promise<GeminiRawPosition[]>;
24
22
  private get;
25
23
  private postAuthenticated;
@@ -30,14 +30,21 @@ class GeminiFetcher {
30
30
  limit: String(Math.min(pageSize, maxResults - allEvents.length)),
31
31
  offset: String(offset),
32
32
  };
33
- if (params.status && params.status !== 'all') {
34
- queryParams.status = params.status === 'active' ? 'active' : params.status;
33
+ const status = params.status;
34
+ if (Array.isArray(status)) {
35
+ const statuses = status.filter(s => s !== 'all');
36
+ if (statuses.length > 0)
37
+ queryParams.status = statuses;
35
38
  }
36
- else if (!params.status) {
39
+ else if (status && status !== 'all') {
40
+ queryParams.status = status === 'active' ? 'active' : status;
41
+ }
42
+ else if (!status) {
37
43
  queryParams.status = 'active';
38
44
  }
39
- if (params.category) {
40
- queryParams.category = params.category;
45
+ const category = params.category;
46
+ if (category) {
47
+ queryParams.category = category;
41
48
  }
42
49
  if (params.query) {
43
50
  queryParams.search = params.query;
@@ -96,15 +103,28 @@ class GeminiFetcher {
96
103
  return this.postAuthenticated('/v1/prediction-markets/order/cancel', { orderId });
97
104
  }
98
105
  async fetchRawActiveOrders(symbol) {
99
- const extra = {};
100
- if (symbol)
101
- extra.symbol = symbol;
102
- const response = await this.postAuthenticated('/v1/prediction-markets/orders/active', extra);
103
- return response.orders;
106
+ return this.fetchPaginatedOrders('/v1/prediction-markets/orders/active', symbol ? { symbol } : {});
104
107
  }
105
108
  async fetchRawOrderHistory() {
106
- const response = await this.postAuthenticated('/v1/prediction-markets/orders/history', {});
107
- return Array.isArray(response) ? response : [];
109
+ return this.fetchPaginatedOrders('/v1/prediction-markets/orders/history', {});
110
+ }
111
+ async fetchPaginatedOrders(path, extra) {
112
+ const allOrders = [];
113
+ const limit = 100;
114
+ let offset = 0;
115
+ while (true) {
116
+ const response = await this.postAuthenticated(path, { ...extra, limit, offset });
117
+ const orders = response.orders ?? [];
118
+ allOrders.push(...orders);
119
+ const pagination = response.pagination;
120
+ const count = pagination?.count ?? orders.length;
121
+ const pageOffset = pagination?.offset ?? offset;
122
+ if (orders.length === 0 || pageOffset + orders.length >= count) {
123
+ break;
124
+ }
125
+ offset = pageOffset + orders.length;
126
+ }
127
+ return allOrders;
108
128
  }
109
129
  async fetchRawPositions() {
110
130
  const response = await this.postAuthenticated('/v1/prediction-markets/positions', {});
@@ -116,7 +136,14 @@ class GeminiFetcher {
116
136
  const url = new URL(path, this.baseUrl);
117
137
  if (params) {
118
138
  for (const [key, value] of Object.entries(params)) {
119
- url.searchParams.set(key, value);
139
+ if (Array.isArray(value)) {
140
+ for (const item of value) {
141
+ url.searchParams.append(key, item);
142
+ }
143
+ }
144
+ else {
145
+ url.searchParams.set(key, value);
146
+ }
120
147
  }
121
148
  }
122
149
  const response = await this.ctx.http.get(url.toString());
@@ -197,19 +197,8 @@ class GeminiTitanExchange extends BaseExchange_1.PredictionMarketExchange {
197
197
  async cancelOrder(orderId) {
198
198
  this.requireAuth();
199
199
  try {
200
- await this.fetcher.cancelRawOrder(parseInt(orderId, 10));
201
- return {
202
- id: orderId,
203
- marketId: '',
204
- outcomeId: '',
205
- side: 'buy',
206
- type: 'limit',
207
- amount: 0,
208
- status: 'canceled',
209
- filled: 0,
210
- remaining: 0,
211
- timestamp: Date.now(),
212
- };
200
+ const rawOrder = await this.fetcher.cancelRawOrder(parseInt(orderId, 10));
201
+ return this.normalizer.normalizeOrder(rawOrder);
213
202
  }
214
203
  catch (error) {
215
204
  throw errors_2.geminiErrorMapper.mapError(error);
@@ -282,7 +282,9 @@ class GeminiNormalizer {
282
282
  size,
283
283
  entryPrice,
284
284
  currentPrice,
285
- unrealizedPnL: (currentPrice - entryPrice) * size,
285
+ unrealizedPnL: raw.unrealizedPnl != null
286
+ ? parseFloat(raw.unrealizedPnl)
287
+ : (currentPrice - entryPrice) * size,
286
288
  };
287
289
  }
288
290
  }
@@ -141,6 +141,9 @@ export interface GeminiRawPosition {
141
141
  isAboveAutoStartThreshold?: boolean;
142
142
  isLive?: boolean;
143
143
  realizedPl?: string;
144
+ marketValue?: string;
145
+ unrealizedPnl?: string;
146
+ unrealizedPct?: number;
144
147
  }
145
148
  export interface GeminiRawActiveOrdersResponse {
146
149
  orders: GeminiRawOrder[];
@@ -150,6 +153,7 @@ export interface GeminiRawActiveOrdersResponse {
150
153
  count: number;
151
154
  };
152
155
  }
156
+ export type GeminiRawOrderHistoryResponse = GeminiRawActiveOrdersResponse;
153
157
  export interface GeminiRawPositionsResponse {
154
158
  positions: GeminiRawPosition[];
155
159
  total?: number;
@@ -9,7 +9,7 @@ export interface GeminiWebSocketConfig {
9
9
  * Gemini Titan WebSocket for real-time order book and trade streaming.
10
10
  *
11
11
  * Subscribes to:
12
- * - {symbol}@depth20 (L2 partial depth snapshots at 1s intervals)
12
+ * - {symbol}@depth@100ms (full depth snapshots/deltas at 100ms intervals)
13
13
  * - {symbol}@trade (executed trades)
14
14
  *
15
15
  * Auth headers are sent during the handshake if credentials are provided
@@ -42,5 +42,6 @@ export declare class GeminiWebSocket {
42
42
  private resolveOrderBook;
43
43
  watchOrderBook(symbol: string): Promise<OrderBook>;
44
44
  watchTrades(symbol: string): Promise<Trade[]>;
45
+ private depthStream;
45
46
  close(): Promise<void>;
46
47
  }
@@ -11,7 +11,7 @@ const watch_timeout_1 = require("../../utils/watch-timeout");
11
11
  * Gemini Titan WebSocket for real-time order book and trade streaming.
12
12
  *
13
13
  * Subscribes to:
14
- * - {symbol}@depth20 (L2 partial depth snapshots at 1s intervals)
14
+ * - {symbol}@depth@100ms (full depth snapshots/deltas at 100ms intervals)
15
15
  * - {symbol}@trade (executed trades)
16
16
  *
17
17
  * Auth headers are sent during the handshake if credentials are provided
@@ -61,7 +61,7 @@ class GeminiWebSocket {
61
61
  // Resubscribe on reconnect
62
62
  const allStreams = [];
63
63
  for (const sym of this.subscribedDepthSymbols) {
64
- allStreams.push(`${sym}@depth20`);
64
+ allStreams.push(this.depthStream(sym));
65
65
  }
66
66
  for (const sym of this.subscribedTradeSymbols) {
67
67
  allStreams.push(`${sym}@trade`);
@@ -132,7 +132,7 @@ class GeminiWebSocket {
132
132
  // -------------------------------------------------------------------------
133
133
  handleMessage(message) {
134
134
  // Gemini sends flat objects, NOT wrapped in { stream, data }.
135
- // Depth snapshots: { lastUpdateId, symbol, bids, asks }
135
+ // Depth snapshots: { lastUpdateId, s, bids, asks }
136
136
  // Depth deltas: { e, E, s, U, u, b, a }
137
137
  // Trades: { E, s, t, p, q, m }
138
138
  // Confirmations: { id, status: 200 }
@@ -151,7 +151,12 @@ class GeminiWebSocket {
151
151
  handleDepthSnapshot(data) {
152
152
  // symbol comes back lowercase from the API, but we subscribed with
153
153
  // uppercase. Normalize to uppercase for resolver lookup.
154
- const symbol = data.symbol.toUpperCase();
154
+ const rawSymbol = data.s ?? data.symbol;
155
+ if (typeof rawSymbol !== 'string' || rawSymbol.length === 0) {
156
+ logger_1.logger.warn('[gemini-titan] depth snapshot missing symbol field');
157
+ return;
158
+ }
159
+ const symbol = rawSymbol.toUpperCase();
155
160
  const bids = (data.bids ?? []).map((level) => ({
156
161
  price: parseFloat(level[0]),
157
162
  size: parseFloat(level[1]),
@@ -240,7 +245,7 @@ class GeminiWebSocket {
240
245
  });
241
246
  }
242
247
  else {
243
- this.sendSubscribe([`${symbol}@depth20`]);
248
+ this.sendSubscribe([this.depthStream(symbol)]);
244
249
  }
245
250
  const dataPromise = new Promise((resolve, reject) => {
246
251
  if (!this.orderBookResolvers.has(symbol)) {
@@ -286,6 +291,9 @@ class GeminiWebSocket {
286
291
  });
287
292
  return (0, watch_timeout_1.withWatchTimeout)(dataPromise, this.config.watchTimeoutMs ?? watch_timeout_1.DEFAULT_WATCH_TIMEOUT_MS, `watchTrades('${symbol}')`);
288
293
  }
294
+ depthStream(symbol) {
295
+ return `${symbol}@depth@100ms`;
296
+ }
289
297
  async close() {
290
298
  this.isTerminated = true;
291
299
  if (this.reconnectTimer) {
@@ -9,7 +9,6 @@ export interface HyperliquidRawOutcome {
9
9
  name: string;
10
10
  description: string;
11
11
  sideSpecs: HyperliquidRawSideSpec[];
12
- quoteToken: string;
13
12
  }
14
13
  export interface HyperliquidRawQuestion {
15
14
  question: number;
@@ -91,6 +90,7 @@ export interface HyperliquidRawPosition {
91
90
  leverage: {
92
91
  type: string;
93
92
  value: number;
93
+ rawUsd?: string;
94
94
  };
95
95
  liquidationPx: string | null;
96
96
  marginUsed: string;
@@ -99,6 +99,11 @@ export interface HyperliquidRawPosition {
99
99
  returnOnEquity: string;
100
100
  szi: string;
101
101
  unrealizedPnl: string;
102
+ cumFunding?: {
103
+ allTime?: string;
104
+ sinceChange?: string;
105
+ sinceOpen?: string;
106
+ };
102
107
  }
103
108
  export interface HyperliquidRawUserState {
104
109
  assetPositions: Array<{
@@ -117,6 +122,8 @@ export interface HyperliquidRawUserState {
117
122
  totalNtlPos: string;
118
123
  totalRawUsd: string;
119
124
  };
125
+ crossMaintenanceMarginUsed?: string;
126
+ time?: number;
120
127
  withdrawable: string;
121
128
  }
122
129
  export interface HyperliquidRawOutcomeWithQuestion {
@@ -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-09T14:50:57.378Z
3
+ * Generated at: 2026-06-10T19:11:09.082Z
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-09T14:50:57.378Z
6
+ * Generated at: 2026-06-10T19:11:09.082Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.kalshiApiSpec = {
@@ -13,6 +13,16 @@ export interface KalshiRawMarket {
13
13
  last_price_dollars?: string;
14
14
  yes_ask_dollars?: string;
15
15
  yes_bid_dollars?: string;
16
+ no_ask_dollars?: string;
17
+ no_bid_dollars?: string;
18
+ response_price_units?: string;
19
+ market_type?: string;
20
+ mve_collection_ticker?: string;
21
+ mve_selected_legs?: Array<{
22
+ event_ticker: string;
23
+ market_ticker: string;
24
+ side: string;
25
+ }>;
16
26
  yes_ask_size_fp?: string;
17
27
  yes_bid_size_fp?: string;
18
28
  rules_primary?: string;
@@ -18,8 +18,18 @@ const KALSHI_PROMOTED_MARKET_KEYS = [
18
18
  'volume_24h_fp', 'volume_24h', 'volume', 'volume_fp',
19
19
  'liquidity_dollars', 'liquidity', 'open_interest_fp', 'open_interest',
20
20
  'status', 'last_price_dollars', 'previous_price_dollars',
21
- 'yes_ask_dollars', 'yes_bid_dollars', 'last_price', 'yes_ask', 'yes_bid',
21
+ 'yes_ask_dollars', 'yes_bid_dollars', 'no_ask_dollars', 'no_bid_dollars',
22
+ 'response_price_units', 'last_price', 'yes_ask', 'yes_bid',
22
23
  ];
24
+ function parseKalshiPrice(value, responseUnits) {
25
+ if (value == null)
26
+ return undefined;
27
+ const parsed = typeof value === 'number' ? value : parseFloat(value);
28
+ if (!Number.isFinite(parsed))
29
+ return undefined;
30
+ const normalizedUnits = responseUnits?.toLowerCase();
31
+ return normalizedUnits === 'cent' || normalizedUnits === 'cents' ? parsed / 100 : parsed;
32
+ }
23
33
  class KalshiNormalizer {
24
34
  normalizeMarket(raw) {
25
35
  // This normalizes a single-market event. For multi-market events, use normalizeMarketsFromEvent.
@@ -42,15 +52,19 @@ class KalshiNormalizer {
42
52
  return null;
43
53
  // Kalshi API v2 migrated from cent integers to FixedPointDollars strings.
44
54
  // Prefer the _dollars fields; fall back to deprecated cent fields.
55
+ const yesBid = parseKalshiPrice(market.yes_bid_dollars, market.response_price_units);
56
+ const yesAsk = parseKalshiPrice(market.yes_ask_dollars, market.response_price_units);
57
+ const noBid = parseKalshiPrice(market.no_bid_dollars, market.response_price_units);
58
+ const noAsk = parseKalshiPrice(market.no_ask_dollars, market.response_price_units);
45
59
  let price = 0;
46
60
  if (market.last_price_dollars != null) {
47
- price = parseFloat(market.last_price_dollars);
61
+ price = parseKalshiPrice(market.last_price_dollars, market.response_price_units) ?? 0;
48
62
  }
49
- else if (market.yes_ask_dollars != null && market.yes_bid_dollars != null) {
50
- price = (parseFloat(market.yes_ask_dollars) + parseFloat(market.yes_bid_dollars)) / 2;
63
+ else if (yesAsk != null && yesBid != null) {
64
+ price = (yesAsk + yesBid) / 2;
51
65
  }
52
- else if (market.yes_ask_dollars != null) {
53
- price = parseFloat(market.yes_ask_dollars);
66
+ else if (yesAsk != null) {
67
+ price = yesAsk;
54
68
  }
55
69
  else if (market.last_price) {
56
70
  price = (0, price_1.fromKalshiCents)(market.last_price);
@@ -66,6 +80,9 @@ class KalshiNormalizer {
66
80
  if (market.previous_price_dollars != null && market.last_price_dollars != null) {
67
81
  priceChange = parseFloat(market.last_price_dollars) - parseFloat(market.previous_price_dollars);
68
82
  }
83
+ const noPrice = noAsk != null && noBid != null
84
+ ? (noAsk + noBid) / 2
85
+ : noAsk ?? noBid ?? (0, price_1.invertKalshiUnified)(price);
69
86
  const outcomes = [
70
87
  {
71
88
  outcomeId: market.ticker,
@@ -73,13 +90,15 @@ class KalshiNormalizer {
73
90
  label: candidateName || 'Yes',
74
91
  price,
75
92
  priceChange24h: priceChange,
93
+ metadata: { bid: yesBid, ask: yesAsk },
76
94
  },
77
95
  {
78
96
  outcomeId: `${market.ticker}-NO`,
79
97
  marketId: market.ticker,
80
98
  label: candidateName ? `Not ${candidateName}` : 'No',
81
- price: (0, price_1.invertKalshiUnified)(price),
99
+ price: noPrice,
82
100
  priceChange24h: -priceChange,
101
+ metadata: { bid: noBid, ask: noAsk },
83
102
  },
84
103
  ];
85
104
  const unifiedTags = [];
@@ -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-09T14:50:57.416Z
3
+ * Generated at: 2026-06-10T19:11:09.124Z
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-09T14:50:57.416Z
6
+ * Generated at: 2026-06-10T19:11:09.124Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.limitlessApiSpec = {
@@ -18,7 +18,8 @@ export interface LimitlessRawMarket {
18
18
  limit?: number[];
19
19
  };
20
20
  };
21
- expirationTimestamp?: string;
21
+ expirationDate?: string;
22
+ expirationTimestamp?: number | string;
22
23
  volumeFormatted?: number;
23
24
  volume?: number;
24
25
  logo?: string | null;
@@ -14,7 +14,7 @@ exports.DEFAULT_LIMITLESS_API_URL = 'https://api.limitless.exchange';
14
14
  const LIMITLESS_PROMOTED_MARKET_KEYS = [
15
15
  'slug', 'title', 'question', 'description',
16
16
  'tokens', 'prices',
17
- 'expirationTimestamp',
17
+ 'expirationDate', 'expirationTimestamp',
18
18
  'volumeFormatted', 'volume',
19
19
  'logo',
20
20
  'categories', 'tags',
@@ -100,7 +100,7 @@ function mapMarketToUnified(market, context = {}) {
100
100
  description: market.description || resolvedContext.eventDescription,
101
101
  slug: market.slug,
102
102
  outcomes: outcomes,
103
- resolutionDate: market.expirationTimestamp ? new Date(market.expirationTimestamp) : new Date(),
103
+ resolutionDate: parseLimitlessExpiration(market),
104
104
  volume24h: Number(market.volumeFormatted || 0),
105
105
  volume: Number(market.volume || 0),
106
106
  liquidity: 0, // Not directly in the flat market list
@@ -115,6 +115,23 @@ function mapMarketToUnified(market, context = {}) {
115
115
  (0, market_utils_1.addBinaryOutcomes)(um);
116
116
  return um;
117
117
  }
118
+ function parseLimitlessExpiration(market) {
119
+ if (typeof market.expirationDate === 'string' && market.expirationDate.trim()) {
120
+ return new Date(market.expirationDate);
121
+ }
122
+ const rawTimestamp = market.expirationTimestamp;
123
+ if (typeof rawTimestamp === 'number' && Number.isFinite(rawTimestamp)) {
124
+ return new Date(rawTimestamp < 1e12 ? rawTimestamp * 1000 : rawTimestamp);
125
+ }
126
+ if (typeof rawTimestamp === 'string' && rawTimestamp.trim()) {
127
+ const numeric = Number(rawTimestamp);
128
+ if (Number.isFinite(numeric)) {
129
+ return new Date(numeric < 1e12 ? numeric * 1000 : numeric);
130
+ }
131
+ return new Date(rawTimestamp);
132
+ }
133
+ return new Date();
134
+ }
118
135
  function getText(value) {
119
136
  return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
120
137
  }
@@ -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-09T14:50:57.427Z
3
+ * Generated at: 2026-06-10T19:11:09.135Z
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-09T14:50:57.427Z
6
+ * Generated at: 2026-06-10T19:11:09.135Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.myriadApiSpec = {
@@ -4,15 +4,25 @@ export interface MyriadRawMarket {
4
4
  id: number;
5
5
  networkId: number;
6
6
  title?: string;
7
+ shortName?: string;
7
8
  description?: string;
8
9
  slug?: string;
9
10
  imageUrl?: string;
10
11
  expiresAt?: string;
12
+ publishedAt?: string;
11
13
  volume24h?: number;
12
14
  volume?: number;
15
+ volumeNotional?: number;
16
+ volumeNotional24h?: number;
13
17
  liquidity?: number;
14
18
  eventId?: number;
19
+ outcomeIndex?: number;
20
+ tradingModel?: string;
21
+ negRisk?: boolean;
22
+ executionMode?: string;
23
+ oracle?: Record<string, unknown>;
15
24
  topics?: string[];
25
+ tags?: string[];
16
26
  outcomes?: MyriadRawOutcome[];
17
27
  [key: string]: unknown;
18
28
  }
@@ -39,6 +49,7 @@ export interface MyriadRawQuestion {
39
49
  export interface MyriadRawTradeEvent {
40
50
  action?: string;
41
51
  blockNumber?: number;
52
+ txId?: string;
42
53
  timestamp?: number;
43
54
  value?: number;
44
55
  shares?: number;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MyriadFetcher = void 0;
4
4
  const utils_1 = require("./utils");
5
5
  const errors_1 = require("./errors");
6
+ const logger_1 = require("../../utils/logger");
6
7
  const MAX_PAGE_SIZE = 100;
7
8
  class MyriadFetcher {
8
9
  ctx;
@@ -129,7 +130,8 @@ class MyriadFetcher {
129
130
  return null;
130
131
  return { bids: data.bids || [], asks: data.asks || [] };
131
132
  }
132
- catch {
133
+ catch (err) {
134
+ logger_1.logger.warn('MyriadFetcher: fetchClobOrderBook failed', { marketId, error: String(err) });
133
135
  return null;
134
136
  }
135
137
  }
@@ -203,7 +203,7 @@ class MyriadNormalizer {
203
203
  }
204
204
  normalizeTrade(raw, index) {
205
205
  return {
206
- id: `${raw.blockNumber || raw.timestamp}-${index}`,
206
+ id: raw.txId ?? `${raw.blockNumber || raw.timestamp}-${index}`,
207
207
  timestamp: (raw.timestamp || 0) * 1000,
208
208
  price: (0, price_1.resolveMyriadPrice)(raw),
209
209
  amount: Number(raw.shares || 0),
@@ -212,7 +212,7 @@ class MyriadNormalizer {
212
212
  }
213
213
  normalizeUserTrade(raw, index) {
214
214
  return {
215
- id: `${raw.blockNumber || raw.timestamp}-${index}`,
215
+ id: raw.txId ?? `${raw.blockNumber || raw.timestamp}-${index}`,
216
216
  timestamp: (raw.timestamp || 0) * 1000,
217
217
  price: (0, price_1.resolveMyriadPrice)(raw),
218
218
  amount: Number(raw.shares || 0),
@@ -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-09T14:50:57.433Z
3
+ * Generated at: 2026-06-10T19:11:09.140Z
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-06-09T14:50:57.433Z
6
+ * Generated at: 2026-06-10T19:11:09.140Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.opinionApiSpec = {
@@ -297,8 +297,9 @@ class OpinionFetcher {
297
297
  * Validates that the API response envelope indicates success (code === 0).
298
298
  */
299
299
  assertSuccess(data) {
300
- if (data.errno !== 0) {
301
- throw new Error(`Opinion API error (errno ${data.errno}): ${data.errmsg || 'Unknown error'}`);
300
+ const code = data.code ?? data.errno;
301
+ if (code !== 0) {
302
+ throw new Error(`Opinion API error (code ${code ?? 'unknown'}): ${data.msg || data.errmsg || 'Unknown error'}`);
302
303
  }
303
304
  }
304
305
  }
@@ -323,8 +323,10 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
323
323
  await auth.ensureTradingEnabled();
324
324
  const client = await auth.getClobClient();
325
325
  const response = await client.placeOrder(built.raw);
326
- if (response.errno !== 0) {
327
- throw new Error(`Order submission failed: ${response.errmsg} (errno: ${response.errno})`);
326
+ const errorCode = response.code ?? response.errno;
327
+ const errorMessage = response.msg || response.errmsg || 'Unknown error';
328
+ if (errorCode !== 0) {
329
+ throw new Error(`Order submission failed: ${errorMessage} (code: ${errorCode ?? 'unknown'})`);
328
330
  }
329
331
  const result = response.result;
330
332
  const orderId = result?.orderId ?? result?.order_id ?? 'unknown';
@@ -356,8 +358,10 @@ class OpinionExchange extends BaseExchange_1.PredictionMarketExchange {
356
358
  const auth = this.ensureTradeAuth();
357
359
  const client = await auth.getClobClient();
358
360
  const response = await client.cancelOrder(orderId);
359
- if (response.errno !== 0) {
360
- throw new Error(`Order cancellation failed: ${response.errmsg} (errno: ${response.errno})`);
361
+ const errorCode = response.code ?? response.errno;
362
+ const errorMessage = response.msg || response.errmsg || 'Unknown error';
363
+ if (errorCode !== 0) {
364
+ throw new Error(`Order cancellation failed: ${errorMessage} (code: ${errorCode ?? 'unknown'})`);
361
365
  }
362
366
  return {
363
367
  id: orderId,
@@ -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-06-09T14:50:57.385Z
3
+ * Generated at: 2026-06-10T19:11:09.088Z
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-06-09T14:50:57.385Z
6
+ * Generated at: 2026-06-10T19:11:09.088Z
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-06-09T14:50:57.397Z
3
+ * Generated at: 2026-06-10T19:11:09.105Z
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-06-09T14:50:57.397Z
6
+ * Generated at: 2026-06-10T19:11:09.105Z
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-06-09T14:50:57.394Z
3
+ * Generated at: 2026-06-10T19:11:09.102Z
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-06-09T14:50:57.394Z
6
+ * Generated at: 2026-06-10T19:11:09.102Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.polymarketGammaSpec = {
@@ -91,7 +91,6 @@ class PolymarketAuth {
91
91
  // 2. If that fails (e.g. 404 or 400), try to CREATE new ones.
92
92
  let creds;
93
93
  try {
94
- // console.log('Trying to derive existing API key...');
95
94
  creds = await l1Client.deriveApiKey();
96
95
  if (!creds || !creds.key || !creds.secret || !creds.passphrase) {
97
96
  // If derived creds are missing, throw to trigger catch -> create
@@ -111,7 +110,6 @@ class PolymarketAuth {
111
110
  logger_1.logger.error('Incomplete credentials after derivation', { hasKey: !!creds?.key, hasSecret: !!creds?.secret, hasPassphrase: !!creds?.passphrase });
112
111
  throw new Error('Authentication failed: Derived credentials are incomplete.');
113
112
  }
114
- // console.log(`[PolymarketAuth] Successfully obtained API credentials for key ${creds.key.substring(0, 8)}...`);
115
113
  this.apiCreds = creds;
116
114
  return creds;
117
115
  }
@@ -138,14 +136,12 @@ class PolymarketAuth {
138
136
  timeout: 10_000,
139
137
  });
140
138
  const profile = response.data;
141
- // console.log(`[PolymarketAuth] Profile for ${address}:`, JSON.stringify(profile));
142
139
  if (profile && profile.proxyAddress) {
143
140
  this.discoveredProxyAddress = profile.proxyAddress;
144
141
  // Determine signature type.
145
142
  // Polymarket usually uses 1 for their own proxy and 2 for Gnosis Safe (which is what their new profiles use).
146
143
  // If it's a proxy address but we don't know the type, 1 is a safe default for Polymarket.
147
144
  this.discoveredSignatureType = profile.isGnosisSafe ? SIG_TYPE_GNOSIS_SAFE : SIG_TYPE_POLY_PROXY;
148
- // console.log(`[PolymarketAuth] Auto-discovered proxy for ${address}: ${this.discoveredProxyAddress} (Type: ${this.discoveredSignatureType})`);
149
145
  if (!this.discoveredProxyAddress || this.discoveredSignatureType === undefined) {
150
146
  throw new Error('[polymarket] Proxy discovery incomplete — missing proxyAddress or signatureType');
151
147
  }
@@ -250,7 +246,6 @@ class PolymarketAuth {
250
246
  const finalProxyAddress = proxyAddress || signerAddress;
251
247
  const finalSignatureType = signatureType;
252
248
  // Create L2-authenticated client
253
- // console.log(`[PolymarketAuth] Initializing ClobClient | Signer: ${signerAddress} | Funder: ${finalProxyAddress} | SigType: ${finalSignatureType}`);
254
249
  this.clobClient = new clob_client_v2_1.ClobClient({
255
250
  host: this.host,
256
251
  chain: config_1.POLYMARKET_CHAIN_ID,
@@ -55,6 +55,8 @@ export interface PolymarketRawOrderBook {
55
55
  bids?: PolymarketRawOrderBookLevel[];
56
56
  asks?: PolymarketRawOrderBookLevel[];
57
57
  timestamp?: string | number;
58
+ neg_risk?: boolean;
59
+ last_trade_price?: string;
58
60
  }
59
61
  export interface PolymarketRawTrade {
60
62
  id?: string;
@@ -58,6 +58,8 @@ function ensureDate(d) {
58
58
  return new Date(d);
59
59
  }
60
60
  const GAMMA_MARKETS_URL = process.env.POLYMARKET_GAMMA_MARKETS_URL || 'https://gamma-api.polymarket.com/markets';
61
+ // Cloudflare blocks large single-shot Gamma lookups (~50 condition_ids / ~3.5KB query).
62
+ const GAMMA_CONDITION_IDS_BATCH_SIZE = 40;
61
63
  class PolymarketFetcher {
62
64
  ctx;
63
65
  http;
@@ -246,14 +248,17 @@ class PolymarketFetcher {
246
248
  }));
247
249
  }
248
250
  async resolveConditionIds(conditionIds) {
249
- const response = await this.http.get(GAMMA_MARKETS_URL, {
250
- params: { condition_ids: conditionIds.join(',') },
251
- });
252
- const markets = Array.isArray(response.data) ? response.data : [];
253
251
  const result = new Map();
254
- for (const market of markets) {
255
- if (market.conditionId && market.id) {
256
- result.set(market.conditionId, String(market.id));
252
+ for (let offset = 0; offset < conditionIds.length; offset += GAMMA_CONDITION_IDS_BATCH_SIZE) {
253
+ const batch = conditionIds.slice(offset, offset + GAMMA_CONDITION_IDS_BATCH_SIZE);
254
+ const response = await this.http.get(GAMMA_MARKETS_URL, {
255
+ params: { condition_ids: batch.join(',') },
256
+ });
257
+ const markets = Array.isArray(response.data) ? response.data : [];
258
+ for (const market of markets) {
259
+ if (market.conditionId && market.id) {
260
+ result.set(market.conditionId, String(market.id));
261
+ }
257
262
  }
258
263
  }
259
264
  return result;
@@ -139,6 +139,9 @@ class PolymarketNormalizer {
139
139
  bids,
140
140
  asks,
141
141
  timestamp: raw.timestamp ? (typeof raw.timestamp === 'string' ? (isFinite(Number(raw.timestamp)) ? Number(raw.timestamp) : new Date(raw.timestamp).getTime()) : Number(raw.timestamp)) : Date.now(),
142
+ isNegRisk: raw.neg_risk ?? false,
143
+ lastTradePrice: raw.last_trade_price != null ? parseFloat(raw.last_trade_price) : undefined,
144
+ sourceMetadata: (0, metadata_1.buildSourceMetadata)(raw, ['asset_id', 'bids', 'asks', 'timestamp', 'neg_risk', 'last_trade_price']),
142
145
  };
143
146
  }
144
147
  normalizeTrade(raw, index) {
@@ -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-06-09T14:50:57.421Z
3
+ * Generated at: 2026-06-10T19:11:09.128Z
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-06-09T14:50:57.421Z
6
+ * Generated at: 2026-06-10T19:11:09.128Z
7
7
  * Do not edit manually -- run "npm run fetch:openapi" to regenerate.
8
8
  */
9
9
  exports.probableApiSpec = {
@@ -54,7 +54,7 @@ class ProbableAuth {
54
54
  });
55
55
  }
56
56
  else {
57
- const baseUrl = process.env.PROBABLE_BASE_URL || 'https://api.probable.markets/public/api/v1';
57
+ const baseUrl = process.env.PROBABLE_BASE_URL || 'https://market-api.probable.markets/public/api/v1';
58
58
  this.clobClient = (0, clob_1.createClobClient)({
59
59
  chainId,
60
60
  baseUrl,
@@ -2,7 +2,7 @@ import { OrderBook } from '../../types';
2
2
  export interface ProbableWebSocketConfig {
3
3
  /** WebSocket URL (default: wss://ws.probable.markets/public/api/v1) */
4
4
  wsUrl?: string;
5
- /** Base URL for the CLOB client (default: https://api.probable.markets/public/api/v1) */
5
+ /** Base URL for the CLOB client (default: https://market-api.probable.markets/public/api/v1) */
6
6
  baseUrl?: string;
7
7
  /** Chain ID (default: 56 for BSC mainnet) */
8
8
  chainId?: number;
@@ -22,7 +22,7 @@ class ProbableWebSocket {
22
22
  return this.client;
23
23
  const chainId = this.config.chainId || parseInt(process.env.PROBABLE_CHAIN_ID || String(config_1.PROBABLE_CHAIN_ID), 10);
24
24
  const wsUrl = this.config.wsUrl || process.env.PROBABLE_WS_URL || 'wss://ws.probable.markets/public/api/v1';
25
- const baseUrl = this.config.baseUrl || process.env.PROBABLE_BASE_URL || 'https://api.probable.markets/public/api/v1';
25
+ const baseUrl = this.config.baseUrl || process.env.PROBABLE_BASE_URL || 'https://market-api.probable.markets/public/api/v1';
26
26
  // Dynamically import @prob/clob using eval to bypass TS compilation to require()
27
27
  // which forces native import() usage, resolving ESM/CJS issues
28
28
  const { createClobClient: createClient } = await eval('import("@prob/clob")');
@@ -9,10 +9,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
9
9
  const Router_1 = require("./Router");
10
10
  const polymarket_1 = require("../exchanges/polymarket");
11
11
  const limitless_1 = require("../exchanges/limitless");
12
+ const logger_1 = require("../utils/logger");
12
13
  async function main() {
13
14
  const apiKey = process.env.PMXT_API_KEY;
14
15
  if (!apiKey) {
15
- console.error('Set PMXT_API_KEY');
16
+ logger_1.logger.error('Set PMXT_API_KEY');
16
17
  process.exit(1);
17
18
  }
18
19
  const polymarket = new polymarket_1.PolymarketExchange({});
@@ -23,36 +24,36 @@ async function main() {
23
24
  });
24
25
  // Morocco FIFA World Cup market on Polymarket
25
26
  const marketId = 'f017596d-4d53-49d5-a7d6-36ed9c37fdc4';
26
- console.log('Fetching unified orderbook for Morocco (Polymarket + Limitless)...');
27
- console.log(`Input market ID: ${marketId}`);
28
- console.log('---');
27
+ logger_1.logger.info('Fetching unified orderbook for Morocco (Polymarket + Limitless)...');
28
+ logger_1.logger.info(`Input market ID: ${marketId}`);
29
+ logger_1.logger.info('---');
29
30
  const book = await router.fetchOrderBook(marketId, undefined, { side: 'yes' });
30
- console.log(`Bids: ${book.bids.length} levels`);
31
- console.log(`Asks: ${book.asks.length} levels`);
32
- console.log('Top 5 bids:', book.bids.slice(0, 5));
33
- console.log('Top 5 asks:', book.asks.slice(0, 5));
31
+ logger_1.logger.info(`Bids: ${book.bids.length} levels`);
32
+ logger_1.logger.info(`Asks: ${book.asks.length} levels`);
33
+ logger_1.logger.info('Top 5 bids:', { bids: book.bids.slice(0, 5) });
34
+ logger_1.logger.info('Top 5 asks:', { asks: book.asks.slice(0, 5) });
34
35
  // Verify we got data from BOTH exchanges
35
36
  // Polymarket top bid was 0.016, Limitless had 0.002
36
37
  // If merged correctly, we should see both
37
38
  const hasPoly = book.bids.some((b) => b.price === 0.016);
38
39
  const hasLimitless = book.bids.some((b) => b.price === 0.002);
39
- console.log('---');
40
- console.log(`Has Polymarket levels: ${hasPoly}`);
41
- console.log(`Has Limitless levels: ${hasLimitless}`);
40
+ logger_1.logger.info('---');
41
+ logger_1.logger.info(`Has Polymarket levels: ${hasPoly}`);
42
+ logger_1.logger.info(`Has Limitless levels: ${hasLimitless}`);
42
43
  if (hasPoly && hasLimitless) {
43
- console.log('SUCCESS: Merged orderbook contains levels from both exchanges');
44
+ logger_1.logger.info('SUCCESS: Merged orderbook contains levels from both exchanges');
44
45
  }
45
46
  else if (!hasPoly && hasLimitless) {
46
- console.log('PARTIAL: Only Limitless book (source market fetch failed)');
47
+ logger_1.logger.info('PARTIAL: Only Limitless book (source market fetch failed)');
47
48
  }
48
49
  else if (hasPoly && !hasLimitless) {
49
- console.log('PARTIAL: Only Polymarket book (matched market fetch failed)');
50
+ logger_1.logger.info('PARTIAL: Only Polymarket book (matched market fetch failed)');
50
51
  }
51
52
  else {
52
- console.log('FAIL: No data from either exchange');
53
+ logger_1.logger.info('FAIL: No data from either exchange');
53
54
  }
54
55
  }
55
56
  main().catch((err) => {
56
- console.error(err);
57
+ logger_1.logger.error('E2E orderbook script failed', { error: String(err) });
57
58
  process.exit(1);
58
59
  });
@@ -49,11 +49,20 @@ async function main() {
49
49
  }
50
50
  logger_1.logger.info(`Lock file created at ${lockFile.lockPath}`);
51
51
  // Graceful shutdown
52
- const shutdown = async () => {
52
+ let shuttingDown = false;
53
+ const shutdown = () => {
54
+ if (shuttingDown)
55
+ return;
56
+ shuttingDown = true;
53
57
  logger_1.logger.info('Shutting down gracefully...');
54
58
  server.close();
55
- await lockFile.remove();
56
- process.exit(0);
59
+ void lockFile.remove()
60
+ .catch((error) => {
61
+ logger_1.logger.warn('Failed to remove lock file during shutdown:', error);
62
+ })
63
+ .finally(() => {
64
+ process.exit(0);
65
+ });
57
66
  };
58
67
  process.on('SIGTERM', shutdown);
59
68
  process.on('SIGINT', shutdown);
@@ -3022,6 +3022,16 @@ components:
3022
3022
  datetime:
3023
3023
  type: string
3024
3024
  description: ISO 8601 datetime string of the snapshot (CCXT-compatible).
3025
+ isNegRisk:
3026
+ type: boolean
3027
+ description: Whether the venue marks this snapshot as a negative-risk market.
3028
+ lastTradePrice:
3029
+ type: number
3030
+ description: Last traded price from venues that include it with the book snapshot.
3031
+ sourceMetadata:
3032
+ type: object
3033
+ additionalProperties: {}
3034
+ description: Venue-specific metadata preserved from the raw order book snapshot.
3025
3035
  required:
3026
3036
  - bids
3027
3037
  - asks
@@ -38,6 +38,7 @@ const fs = __importStar(require("fs/promises"));
38
38
  const path = __importStar(require("path"));
39
39
  const os = __importStar(require("os"));
40
40
  const child_process_1 = require("child_process");
41
+ const logger_1 = require("../../utils/logger");
41
42
  class LockFile {
42
43
  lockPath;
43
44
  constructor() {
@@ -62,8 +63,10 @@ class LockFile {
62
63
  try {
63
64
  await fs.unlink(this.lockPath);
64
65
  }
65
- catch {
66
- // Ignore errors if file doesn't exist
66
+ catch (err) {
67
+ if (err?.code !== 'ENOENT') {
68
+ logger_1.logger.warn('LockFile: failed to remove lock', { path: this.lockPath, error: String(err) });
69
+ }
67
70
  }
68
71
  }
69
72
  isProcessRunning(pid) {
package/dist/types.d.ts CHANGED
@@ -159,6 +159,12 @@ export interface OrderBook {
159
159
  timestamp?: number;
160
160
  /** ISO 8601 datetime string of the snapshot (CCXT-compatible). */
161
161
  datetime?: string;
162
+ /** Whether the venue marks this snapshot as a negative-risk market. */
163
+ isNegRisk?: boolean;
164
+ /** Last traded price from venues that include it with the book snapshot. */
165
+ lastTradePrice?: number;
166
+ /** Venue-specific metadata preserved from the raw order book snapshot. */
167
+ sourceMetadata?: Record<string, unknown>;
162
168
  }
163
169
  export interface Trade {
164
170
  /** The unique identifier for this trade. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmxt-core",
3
- "version": "2.49.6",
3
+ "version": "2.49.8",
4
4
  "description": "pmxt is a unified prediction market data API. The ccxt for prediction markets.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -29,8 +29,8 @@
29
29
  "test": "jest -c jest.config.js",
30
30
  "server": "tsx watch src/server/index.ts",
31
31
  "server:prod": "node dist/server/index.js",
32
- "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.49.6,library=urllib3",
33
- "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.49.6,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
32
+ "generate:sdk:python": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g python -o ../sdks/python/generated --package-name pmxt_internal --additional-properties=projectName=pmxt-internal,packageVersion=2.49.8,library=urllib3",
33
+ "generate:sdk:typescript": "npx @openapitools/openapi-generator-cli generate -i src/server/openapi.yaml -g typescript-fetch -o ../sdks/typescript/generated --additional-properties=npmName=pmxtjs,npmVersion=2.49.8,supportsES6=true,typescriptThreePlus=true && node ../sdks/typescript/scripts/fix-generated.js",
34
34
  "fetch:openapi": "node scripts/fetch-openapi-specs.js",
35
35
  "extract:jsdoc": "node ../scripts/extract-jsdoc.js",
36
36
  "generate:docs": "npm run extract:jsdoc && node ../scripts/generate-api-docs.js",