pmxt-core 2.9.2 → 2.9.3

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