pmxt-core 2.13.2 → 2.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/dist/exchanges/kalshi/api.d.ts +7 -1
  2. package/dist/exchanges/kalshi/api.js +11 -2
  3. package/dist/exchanges/kalshi/config.d.ts +103 -0
  4. package/dist/exchanges/kalshi/config.js +144 -0
  5. package/dist/exchanges/kalshi/fetchEvents.d.ts +2 -2
  6. package/dist/exchanges/kalshi/fetchEvents.js +19 -16
  7. package/dist/exchanges/kalshi/fetchMarkets.d.ts +2 -2
  8. package/dist/exchanges/kalshi/fetchMarkets.js +36 -25
  9. package/dist/exchanges/kalshi/fetchOHLCV.d.ts +2 -2
  10. package/dist/exchanges/kalshi/fetchOHLCV.js +20 -17
  11. package/dist/exchanges/kalshi/fetchOrderBook.d.ts +2 -0
  12. package/dist/exchanges/kalshi/fetchOrderBook.js +60 -0
  13. package/dist/exchanges/kalshi/fetchTrades.d.ts +3 -0
  14. package/dist/exchanges/kalshi/fetchTrades.js +32 -0
  15. package/dist/exchanges/kalshi/index.d.ts +8 -3
  16. package/dist/exchanges/kalshi/index.js +82 -70
  17. package/dist/exchanges/kalshi/kalshi.test.js +294 -292
  18. package/dist/exchanges/kalshi/utils.d.ts +1 -3
  19. package/dist/exchanges/kalshi/utils.js +14 -16
  20. package/dist/exchanges/kalshi/websocket.d.ts +4 -3
  21. package/dist/exchanges/kalshi/websocket.js +87 -61
  22. package/dist/exchanges/kalshi-demo/index.d.ts +10 -0
  23. package/dist/exchanges/kalshi-demo/index.js +23 -0
  24. package/dist/exchanges/limitless/api.d.ts +1 -1
  25. package/dist/exchanges/limitless/api.js +1 -1
  26. package/dist/exchanges/myriad/api.d.ts +1 -1
  27. package/dist/exchanges/myriad/api.js +1 -1
  28. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  29. package/dist/exchanges/polymarket/api-clob.js +1 -1
  30. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  31. package/dist/exchanges/polymarket/api-data.js +1 -1
  32. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  33. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  34. package/dist/exchanges/probable/api.d.ts +1 -1
  35. package/dist/exchanges/probable/api.js +1 -1
  36. package/dist/index.d.ts +4 -0
  37. package/dist/index.js +5 -1
  38. package/dist/server/app.js +56 -48
  39. package/package.json +3 -3
@@ -0,0 +1,60 @@
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.fetchOrderBook = fetchOrderBook;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const validation_1 = require("../../utils/validation");
9
+ const errors_1 = require("./errors");
10
+ const config_1 = require("./config");
11
+ async function fetchOrderBook(baseUrl, id) {
12
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
13
+ try {
14
+ // Check if this is a NO outcome request
15
+ const isNoOutcome = id.endsWith("-NO");
16
+ const ticker = id.replace(/-NO$/, "");
17
+ const url = (0, config_1.getMarketsUrl)(baseUrl, ticker, ["orderbook"]);
18
+ const response = await axios_1.default.get(url);
19
+ const data = response.data.orderbook;
20
+ // Structure: { yes: [[price, qty], ...], no: [[price, qty], ...] }
21
+ // Kalshi returns bids at their actual prices (not inverted)
22
+ // - yes: bids for buying YES at price X
23
+ // - no: bids for buying NO at price X
24
+ let bids;
25
+ let asks;
26
+ if (isNoOutcome) {
27
+ // NO outcome order book:
28
+ // - Bids: people buying NO (use data.no directly)
29
+ // - Asks: people selling NO = people buying YES (invert data.yes)
30
+ bids = (data.no || []).map((level) => ({
31
+ price: level[0] / 100,
32
+ size: level[1],
33
+ }));
34
+ asks = (data.yes || []).map((level) => ({
35
+ price: 1 - level[0] / 100, // Invert YES price to get NO ask price
36
+ size: level[1],
37
+ }));
38
+ }
39
+ else {
40
+ // YES outcome order book:
41
+ // - Bids: people buying YES (use data.yes directly)
42
+ // - Asks: people selling YES = people buying NO (invert data.no)
43
+ bids = (data.yes || []).map((level) => ({
44
+ price: level[0] / 100,
45
+ size: level[1],
46
+ }));
47
+ asks = (data.no || []).map((level) => ({
48
+ price: 1 - level[0] / 100, // Invert NO price to get YES ask price
49
+ size: level[1],
50
+ }));
51
+ }
52
+ // Sort bids desc, asks asc
53
+ bids.sort((a, b) => b.price - a.price);
54
+ asks.sort((a, b) => a.price - b.price);
55
+ return { bids, asks, timestamp: Date.now() };
56
+ }
57
+ catch (error) {
58
+ throw errors_1.kalshiErrorMapper.mapError(error);
59
+ }
60
+ }
@@ -0,0 +1,3 @@
1
+ import { HistoryFilterParams, TradesParams } from "../../BaseExchange";
2
+ import { Trade } from "../../types";
3
+ export declare function fetchTrades(baseUrl: string, id: string, params: TradesParams | HistoryFilterParams): Promise<Trade[]>;
@@ -0,0 +1,32 @@
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.fetchTrades = fetchTrades;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ const errors_1 = require("./errors");
9
+ const config_1 = require("./config");
10
+ async function fetchTrades(baseUrl, id, params) {
11
+ try {
12
+ const ticker = id.replace(/-NO$/, "");
13
+ const url = (0, config_1.getMarketsUrl)(baseUrl, undefined, ["trades"]);
14
+ const response = await axios_1.default.get(url, {
15
+ params: {
16
+ ticker: ticker,
17
+ limit: params.limit || 100,
18
+ },
19
+ });
20
+ const trades = response.data.trades || [];
21
+ return trades.map((t) => ({
22
+ id: t.trade_id,
23
+ timestamp: new Date(t.created_time).getTime(),
24
+ price: t.yes_price / 100,
25
+ amount: t.count,
26
+ side: t.taker_side === "yes" ? "buy" : "sell",
27
+ }));
28
+ }
29
+ catch (error) {
30
+ throw errors_1.kalshiErrorMapper.mapError(error);
31
+ }
32
+ }
@@ -1,11 +1,15 @@
1
- import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from '../../BaseExchange';
2
- import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Balance, Order, Position, CreateOrderParams } from '../../types';
3
- import { KalshiWebSocketConfig } from './websocket';
1
+ import { PredictionMarketExchange, MarketFilterParams, HistoryFilterParams, OHLCVParams, TradesParams, ExchangeCredentials, EventFetchParams, MyTradesParams, OrderHistoryParams } from "../../BaseExchange";
2
+ import { UnifiedMarket, UnifiedEvent, PriceCandle, OrderBook, Trade, UserTrade, Balance, Order, Position, CreateOrderParams } from "../../types";
3
+ import { KalshiWebSocketConfig } from "./websocket";
4
4
  export type { KalshiWebSocketConfig };
5
5
  export interface KalshiExchangeOptions {
6
6
  credentials?: ExchangeCredentials;
7
7
  websocket?: KalshiWebSocketConfig;
8
8
  }
9
+ /** @internal */
10
+ export interface KalshiInternalOptions extends KalshiExchangeOptions {
11
+ demoMode?: boolean;
12
+ }
9
13
  export declare class KalshiExchange extends PredictionMarketExchange {
10
14
  readonly has: {
11
15
  fetchMarkets: true;
@@ -27,6 +31,7 @@ export declare class KalshiExchange extends PredictionMarketExchange {
27
31
  };
28
32
  private auth?;
29
33
  private wsConfig?;
34
+ private config;
30
35
  constructor(options?: ExchangeCredentials | KalshiExchangeOptions);
31
36
  get name(): string;
32
37
  protected sign(method: string, path: string, _params: Record<string, any>): Record<string, string>;
@@ -12,6 +12,7 @@ const errors_1 = require("./errors");
12
12
  const errors_2 = require("../../errors");
13
13
  const openapi_1 = require("../../utils/openapi");
14
14
  const api_1 = require("./api");
15
+ const config_1 = require("./config");
15
16
  class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
16
17
  has = {
17
18
  fetchMarkets: true,
@@ -33,14 +34,17 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
33
34
  };
34
35
  auth;
35
36
  wsConfig;
37
+ config;
36
38
  constructor(options) {
37
39
  // Support both old signature (credentials only) and new signature (options object)
38
40
  let credentials;
39
41
  let wsConfig;
40
- if (options && 'credentials' in options) {
41
- // New signature: KalshiExchangeOptions
42
+ let demoMode = false;
43
+ if (options && "credentials" in options) {
44
+ // New signature: KalshiExchangeOptions / KalshiInternalOptions
42
45
  credentials = options.credentials;
43
46
  wsConfig = options.websocket;
47
+ demoMode = options.demoMode || false;
44
48
  }
45
49
  else {
46
50
  // Old signature: ExchangeCredentials directly
@@ -49,10 +53,11 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
49
53
  super(credentials);
50
54
  this.rateLimit = 100;
51
55
  this.wsConfig = wsConfig;
56
+ this.config = (0, config_1.getKalshiConfig)(demoMode);
52
57
  if (credentials?.apiKey && credentials?.privateKey) {
53
58
  this.auth = new auth_1.KalshiAuth(credentials);
54
59
  }
55
- const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.kalshiApiSpec);
60
+ const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.kalshiApiSpec, this.config.apiUrl + config_1.KALSHI_PATHS.TRADE_API);
56
61
  this.defineImplicitApi(descriptor);
57
62
  }
58
63
  get name() {
@@ -65,7 +70,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
65
70
  const auth = this.ensureAuth();
66
71
  // The implicit API passes just the spec path (e.g. /portfolio/balance),
67
72
  // but Kalshi's signature requires the full path including /trade-api/v2.
68
- return auth.getHeaders(method, '/trade-api/v2' + path);
73
+ return auth.getHeaders(method, "/trade-api/v2" + path);
69
74
  }
70
75
  mapImplicitApiError(error) {
71
76
  throw errors_1.kalshiErrorMapper.mapError(error);
@@ -75,8 +80,8 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
75
80
  // ----------------------------------------------------------------------------
76
81
  ensureAuth() {
77
82
  if (!this.auth) {
78
- throw new errors_2.AuthenticationError('Trading operations require authentication. ' +
79
- 'Initialize KalshiExchange with credentials (apiKey and privateKey).', 'Kalshi');
83
+ throw new errors_2.AuthenticationError("Trading operations require authentication. " +
84
+ "Initialize KalshiExchange with credentials (apiKey and privateKey).", "Kalshi");
80
85
  }
81
86
  return this.auth;
82
87
  }
@@ -93,10 +98,11 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
93
98
  return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.callApi.bind(this));
94
99
  }
95
100
  async fetchOrderBook(id) {
96
- (0, validation_1.validateIdFormat)(id, 'OrderBook');
97
- const isNoOutcome = id.endsWith('-NO');
98
- const ticker = id.replace(/-NO$/, '');
99
- const data = (await this.callApi('GetMarketOrderbook', { ticker })).orderbook;
101
+ (0, validation_1.validateIdFormat)(id, "OrderBook");
102
+ const isNoOutcome = id.endsWith("-NO");
103
+ const ticker = id.replace(/-NO$/, "");
104
+ const data = (await this.callApi("GetMarketOrderbook", { ticker }))
105
+ .orderbook;
100
106
  let bids;
101
107
  let asks;
102
108
  if (isNoOutcome) {
@@ -105,7 +111,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
105
111
  size: level[1],
106
112
  }));
107
113
  asks = (data.yes || []).map((level) => ({
108
- price: 1 - (level[0] / 100),
114
+ price: 1 - level[0] / 100,
109
115
  size: level[1],
110
116
  }));
111
117
  }
@@ -115,7 +121,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
115
121
  size: level[1],
116
122
  }));
117
123
  asks = (data.no || []).map((level) => ({
118
- price: 1 - (level[0] / 100),
124
+ price: 1 - level[0] / 100,
119
125
  size: level[1],
120
126
  }));
121
127
  }
@@ -124,47 +130,52 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
124
130
  return { bids, asks, timestamp: Date.now() };
125
131
  }
126
132
  async fetchTrades(id, params) {
127
- if ('resolution' in params && params.resolution !== undefined) {
133
+ if ("resolution" in params && params.resolution !== undefined) {
128
134
  console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
129
- 'It will be removed in v3.0.0. Please remove it from your code.');
135
+ "It will be removed in v3.0.0. Please remove it from your code.");
130
136
  }
131
- const ticker = id.replace(/-NO$/, '');
132
- const data = await this.callApi('GetTrades', { ticker, limit: params.limit || 100 });
137
+ const ticker = id.replace(/-NO$/, "");
138
+ const data = await this.callApi("GetTrades", {
139
+ ticker,
140
+ limit: params.limit || 100,
141
+ });
133
142
  const trades = data.trades || [];
134
143
  return trades.map((t) => ({
135
144
  id: t.trade_id,
136
145
  timestamp: new Date(t.created_time).getTime(),
137
146
  price: t.yes_price / 100,
138
147
  amount: t.count,
139
- side: t.taker_side === 'yes' ? 'buy' : 'sell',
148
+ side: t.taker_side === "yes" ? "buy" : "sell",
140
149
  }));
141
150
  }
142
151
  // ----------------------------------------------------------------------------
143
152
  // User Data Methods
144
153
  // ----------------------------------------------------------------------------
145
154
  async fetchBalance() {
146
- const data = await this.callApi('GetBalance');
155
+ const data = await this.callApi("GetBalance");
147
156
  const available = data.balance / 100;
148
157
  const total = data.portfolio_value / 100;
149
- return [{
150
- currency: 'USD',
158
+ return [
159
+ {
160
+ currency: "USD",
151
161
  total,
152
162
  available,
153
163
  locked: total - available,
154
- }];
164
+ },
165
+ ];
155
166
  }
156
167
  // ----------------------------------------------------------------------------
157
168
  // Trading Methods
158
169
  // ----------------------------------------------------------------------------
159
170
  async createOrder(params) {
160
- const isYesSide = params.side === 'buy';
171
+ const isYesSide = params.side === "buy";
161
172
  const kalshiOrder = {
162
173
  ticker: params.marketId,
163
174
  client_order_id: `pmxt-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
164
- side: isYesSide ? 'yes' : 'no',
165
- action: params.side === 'buy' ? 'buy' : 'sell',
175
+ side: isYesSide ? "yes" : "no",
176
+ action: params.side === "buy" ? "buy" : "sell",
166
177
  count: params.amount,
167
- type: params.type === 'limit' ? 'limit' : 'market',
178
+ type: params.type === "limit" ? "limit" : "market",
168
179
  };
169
180
  if (params.price) {
170
181
  const priceInCents = Math.round(params.price * 100);
@@ -175,55 +186,43 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
175
186
  kalshiOrder.no_price = priceInCents;
176
187
  }
177
188
  }
178
- const data = await this.callApi('CreateOrder', kalshiOrder);
189
+ const data = await this.callApi("CreateOrder", kalshiOrder);
179
190
  const order = data.order;
180
- return {
181
- id: order.order_id,
182
- marketId: params.marketId,
183
- outcomeId: params.outcomeId,
184
- side: params.side,
185
- type: params.type,
186
- price: params.price,
187
- amount: params.amount,
188
- status: this.mapKalshiOrderStatus(order.status),
189
- filled: order.queue_position === 0 ? params.amount : 0,
190
- remaining: order.remaining_count || params.amount,
191
- timestamp: new Date(order.created_time).getTime(),
192
- };
191
+ return this.mapKalshiOrder(order);
193
192
  }
194
193
  async cancelOrder(orderId) {
195
- const data = await this.callApi('CancelOrder', { order_id: orderId });
194
+ const data = await this.callApi("CancelOrder", { order_id: orderId });
196
195
  const order = data.order;
197
196
  return {
198
197
  id: order.order_id,
199
198
  marketId: order.ticker,
200
199
  outcomeId: order.ticker,
201
- side: order.side === 'yes' ? 'buy' : 'sell',
202
- type: 'limit',
200
+ side: order.side === "yes" ? "buy" : "sell",
201
+ type: "limit",
203
202
  amount: order.count,
204
- status: 'cancelled',
203
+ status: "cancelled",
205
204
  filled: order.count - (order.remaining_count || 0),
206
205
  remaining: 0,
207
206
  timestamp: new Date(order.created_time).getTime(),
208
207
  };
209
208
  }
210
209
  async fetchOrder(orderId) {
211
- const data = await this.callApi('GetOrder', { order_id: orderId });
210
+ const data = await this.callApi("GetOrder", { order_id: orderId });
212
211
  return this.mapKalshiOrder(data.order);
213
212
  }
214
213
  async fetchOpenOrders(marketId) {
215
- const queryParams = { status: 'resting' };
214
+ const queryParams = { status: "resting" };
216
215
  if (marketId) {
217
216
  queryParams.ticker = marketId;
218
217
  }
219
- const data = await this.callApi('GetOrders', queryParams);
218
+ const data = await this.callApi("GetOrders", queryParams);
220
219
  const orders = data.orders || [];
221
220
  return orders.map((order) => this.mapKalshiOrder(order));
222
221
  }
223
222
  async fetchMyTrades(params) {
224
223
  const queryParams = {};
225
224
  if (params?.outcomeId || params?.marketId) {
226
- queryParams.ticker = (params.outcomeId || params.marketId).replace(/-NO$/, '');
225
+ queryParams.ticker = (params.outcomeId || params.marketId).replace(/-NO$/, "");
227
226
  }
228
227
  if (params?.since)
229
228
  queryParams.min_ts = Math.floor(params.since.getTime() / 1000);
@@ -233,13 +232,13 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
233
232
  queryParams.limit = params.limit;
234
233
  if (params?.cursor)
235
234
  queryParams.cursor = params.cursor;
236
- const data = await this.callApi('GetFills', queryParams);
235
+ const data = await this.callApi("GetFills", queryParams);
237
236
  return (data.fills || []).map((f) => ({
238
237
  id: f.fill_id,
239
238
  timestamp: new Date(f.created_time).getTime(),
240
239
  price: f.yes_price / 100,
241
240
  amount: f.count,
242
- side: f.side === 'yes' ? 'buy' : 'sell',
241
+ side: f.side === "yes" ? "buy" : "sell",
243
242
  orderId: f.order_id,
244
243
  }));
245
244
  }
@@ -253,7 +252,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
253
252
  queryParams.limit = params.limit;
254
253
  if (params?.cursor)
255
254
  queryParams.cursor = params.cursor;
256
- const data = await this.callApi('GetHistoricalOrders', queryParams);
255
+ const data = await this.callApi("GetHistoricalOrders", queryParams);
257
256
  return (data.orders || []).map((o) => this.mapKalshiOrder(o));
258
257
  }
259
258
  async fetchAllOrders(params) {
@@ -269,12 +268,15 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
269
268
  const historicalParams = { ...queryParams };
270
269
  delete historicalParams.min_ts; // GetHistoricalOrders only supports max_ts
271
270
  const [liveData, historicalData] = await Promise.all([
272
- this.callApi('GetOrders', queryParams),
273
- this.callApi('GetHistoricalOrders', historicalParams),
271
+ this.callApi("GetOrders", queryParams),
272
+ this.callApi("GetHistoricalOrders", historicalParams),
274
273
  ]);
275
274
  const seen = new Set();
276
275
  const all = [];
277
- for (const o of [...(liveData.orders || []), ...(historicalData.orders || [])]) {
276
+ for (const o of [
277
+ ...(liveData.orders || []),
278
+ ...(historicalData.orders || []),
279
+ ]) {
278
280
  if (!seen.has(o.order_id)) {
279
281
  seen.add(o.order_id);
280
282
  all.push(this.mapKalshiOrder(o));
@@ -283,7 +285,7 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
283
285
  return all.sort((a, b) => b.timestamp - a.timestamp);
284
286
  }
285
287
  async fetchPositions() {
286
- const data = await this.callApi('GetPositions');
288
+ const data = await this.callApi("GetPositions");
287
289
  const positions = data.market_positions || [];
288
290
  return positions.map((pos) => {
289
291
  const absPosition = Math.abs(pos.position);
@@ -306,8 +308,8 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
306
308
  id: order.order_id,
307
309
  marketId: order.ticker,
308
310
  outcomeId: order.ticker,
309
- side: order.side === 'yes' ? 'buy' : 'sell',
310
- type: order.type === 'limit' ? 'limit' : 'market',
311
+ side: order.side === "yes" ? "buy" : "sell",
312
+ type: order.type === "limit" ? "limit" : "market",
311
313
  price: order.yes_price ? order.yes_price / 100 : undefined,
312
314
  amount: order.count,
313
315
  status: this.mapKalshiOrderStatus(order.status),
@@ -318,17 +320,17 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
318
320
  }
319
321
  // Helper to map Kalshi order status to unified status
320
322
  mapKalshiOrderStatus(status) {
321
- switch ((status ?? '').toLowerCase()) {
322
- case 'resting':
323
- return 'open';
324
- case 'canceled':
325
- case 'cancelled':
326
- return 'cancelled';
327
- case 'executed':
328
- case 'filled':
329
- return 'filled';
323
+ switch ((status ?? "").toLowerCase()) {
324
+ case "resting":
325
+ return "open";
326
+ case "canceled":
327
+ case "cancelled":
328
+ return "cancelled";
329
+ case "executed":
330
+ case "filled":
331
+ return "filled";
330
332
  default:
331
- return 'open';
333
+ return "open";
332
334
  }
333
335
  }
334
336
  // ----------------------------------------------------------------------------
@@ -338,19 +340,29 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
338
340
  async watchOrderBook(id, limit) {
339
341
  const auth = this.ensureAuth();
340
342
  if (!this.ws) {
341
- this.ws = new websocket_1.KalshiWebSocket(auth, this.wsConfig);
343
+ // Merge wsConfig with wsUrl from config
344
+ const wsConfigWithUrl = {
345
+ ...this.wsConfig,
346
+ wsUrl: this.wsConfig?.wsUrl || this.config.wsUrl,
347
+ };
348
+ this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
342
349
  }
343
350
  // Normalize ticker (strip -NO suffix if present)
344
- const marketTicker = id.replace(/-NO$/, '');
351
+ const marketTicker = id.replace(/-NO$/, "");
345
352
  return this.ws.watchOrderBook(marketTicker);
346
353
  }
347
354
  async watchTrades(id, since, limit) {
348
355
  const auth = this.ensureAuth();
349
356
  if (!this.ws) {
350
- this.ws = new websocket_1.KalshiWebSocket(auth, this.wsConfig);
357
+ // Merge wsConfig with wsUrl from config
358
+ const wsConfigWithUrl = {
359
+ ...this.wsConfig,
360
+ wsUrl: this.wsConfig?.wsUrl || this.config.wsUrl,
361
+ };
362
+ this.ws = new websocket_1.KalshiWebSocket(auth, wsConfigWithUrl);
351
363
  }
352
364
  // Normalize ticker (strip -NO suffix if present)
353
- const marketTicker = id.replace(/-NO$/, '');
365
+ const marketTicker = id.replace(/-NO$/, "");
354
366
  return this.ws.watchTrades(marketTicker);
355
367
  }
356
368
  async close() {