pmxt-core 2.39.1 → 2.40.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 (81) hide show
  1. package/dist/exchanges/baozi/fetcher.js +28 -8
  2. package/dist/exchanges/baozi/index.js +6 -4
  3. package/dist/exchanges/gemini-titan/auth.d.ts +34 -0
  4. package/dist/exchanges/gemini-titan/auth.js +80 -0
  5. package/dist/exchanges/gemini-titan/config.d.ts +15 -0
  6. package/dist/exchanges/gemini-titan/config.js +24 -0
  7. package/dist/exchanges/gemini-titan/errors.d.ts +20 -0
  8. package/dist/exchanges/gemini-titan/errors.js +75 -0
  9. package/dist/exchanges/gemini-titan/fetcher.d.ts +26 -0
  10. package/dist/exchanges/gemini-titan/fetcher.js +148 -0
  11. package/dist/exchanges/gemini-titan/index.d.ts +31 -0
  12. package/dist/exchanges/gemini-titan/index.js +188 -0
  13. package/dist/exchanges/gemini-titan/normalizer.d.ts +13 -0
  14. package/dist/exchanges/gemini-titan/normalizer.js +229 -0
  15. package/dist/exchanges/gemini-titan/types.d.ts +220 -0
  16. package/dist/exchanges/gemini-titan/types.js +6 -0
  17. package/dist/exchanges/gemini-titan/utils.d.ts +30 -0
  18. package/dist/exchanges/gemini-titan/utils.js +57 -0
  19. package/dist/exchanges/gemini-titan/websocket.d.ts +46 -0
  20. package/dist/exchanges/gemini-titan/websocket.js +295 -0
  21. package/dist/exchanges/kalshi/api.d.ts +1 -1
  22. package/dist/exchanges/kalshi/api.js +1 -1
  23. package/dist/exchanges/kalshi/fetcher.js +6 -2
  24. package/dist/exchanges/limitless/api.d.ts +1 -1
  25. package/dist/exchanges/limitless/api.js +1 -1
  26. package/dist/exchanges/limitless/index.js +3 -6
  27. package/dist/exchanges/metaculus/fetchEvents.js +7 -2
  28. package/dist/exchanges/mock/index.d.ts +55 -0
  29. package/dist/exchanges/mock/index.js +603 -0
  30. package/dist/exchanges/mock/seededRng.d.ts +10 -0
  31. package/dist/exchanges/mock/seededRng.js +48 -0
  32. package/dist/exchanges/myriad/api.d.ts +1 -1
  33. package/dist/exchanges/myriad/api.js +1 -1
  34. package/dist/exchanges/myriad/websocket.d.ts +4 -0
  35. package/dist/exchanges/myriad/websocket.js +51 -6
  36. package/dist/exchanges/opinion/api.d.ts +1 -1
  37. package/dist/exchanges/opinion/api.js +1 -1
  38. package/dist/exchanges/polymarket/api-clob.d.ts +1 -1
  39. package/dist/exchanges/polymarket/api-clob.js +1 -1
  40. package/dist/exchanges/polymarket/api-data.d.ts +1 -1
  41. package/dist/exchanges/polymarket/api-data.js +1 -1
  42. package/dist/exchanges/polymarket/api-gamma.d.ts +1 -1
  43. package/dist/exchanges/polymarket/api-gamma.js +1 -1
  44. package/dist/exchanges/polymarket/auth.js +5 -2
  45. package/dist/exchanges/polymarket/index.js +2 -1
  46. package/dist/exchanges/polymarket_us/normalizer.js +5 -1
  47. package/dist/exchanges/probable/api.d.ts +1 -1
  48. package/dist/exchanges/probable/api.js +1 -1
  49. package/dist/exchanges/probable/index.js +9 -6
  50. package/dist/exchanges/smarkets/fetcher.js +6 -2
  51. package/dist/index.d.ts +8 -0
  52. package/dist/index.js +9 -1
  53. package/dist/router/Router.js +55 -21
  54. package/dist/server/exchange-factory.js +9 -0
  55. package/dist/server/openapi.yaml +22 -0
  56. package/dist/server/ws-handler.js +13 -3
  57. package/package.json +3 -3
  58. package/dist/exchanges/baozi/price.test.d.ts +0 -1
  59. package/dist/exchanges/baozi/price.test.js +0 -33
  60. package/dist/exchanges/kalshi/kalshi.test.d.ts +0 -1
  61. package/dist/exchanges/kalshi/kalshi.test.js +0 -641
  62. package/dist/exchanges/kalshi/price.test.d.ts +0 -1
  63. package/dist/exchanges/kalshi/price.test.js +0 -24
  64. package/dist/exchanges/myriad/price.test.d.ts +0 -1
  65. package/dist/exchanges/myriad/price.test.js +0 -17
  66. package/dist/exchanges/polymarket_us/errors.test.d.ts +0 -1
  67. package/dist/exchanges/polymarket_us/errors.test.js +0 -54
  68. package/dist/exchanges/polymarket_us/index.test.d.ts +0 -8
  69. package/dist/exchanges/polymarket_us/index.test.js +0 -237
  70. package/dist/exchanges/polymarket_us/normalizer.test.d.ts +0 -1
  71. package/dist/exchanges/polymarket_us/normalizer.test.js +0 -224
  72. package/dist/exchanges/polymarket_us/price.test.d.ts +0 -1
  73. package/dist/exchanges/polymarket_us/price.test.js +0 -131
  74. package/dist/exchanges/polymarket_us/websocket.test.d.ts +0 -8
  75. package/dist/exchanges/polymarket_us/websocket.test.js +0 -162
  76. package/dist/exchanges/smarkets/price.test.d.ts +0 -1
  77. package/dist/exchanges/smarkets/price.test.js +0 -50
  78. package/dist/router/Router.test.d.ts +0 -1
  79. package/dist/router/Router.test.js +0 -328
  80. package/dist/router/client.test.d.ts +0 -1
  81. package/dist/router/client.test.js +0 -177
@@ -1,641 +0,0 @@
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
- const kalshi_1 = require("../kalshi");
7
- const axios_1 = __importDefault(require("axios"));
8
- const auth_1 = require("./auth");
9
- const config_1 = require("./config");
10
- // Jest hoisting means we can't use outer variables in jest.mock factory
11
- // unless they start with 'mock'. However, let's just define it inline to be safe and simple.
12
- // To access the inner methods, we'll grab the instance returned by axios.create().
13
- jest.mock("axios", () => {
14
- const mockInstance = {
15
- interceptors: {
16
- request: { use: jest.fn() },
17
- response: { use: jest.fn() },
18
- },
19
- get: jest.fn(),
20
- post: jest.fn(),
21
- delete: jest.fn(),
22
- request: jest.fn(),
23
- defaults: { headers: { common: {} } },
24
- };
25
- const actualAxios = jest.requireActual("axios");
26
- const mockAxios = {
27
- create: jest.fn(() => mockInstance),
28
- isAxiosError: actualAxios.isAxiosError,
29
- };
30
- // Support both default and named exports
31
- return {
32
- __esModule: true,
33
- ...mockAxios,
34
- default: mockAxios,
35
- };
36
- });
37
- // Access the mocked instance for assertions
38
- // Since our factory returns the same object reference, this works.
39
- const mockAxiosInstance = axios_1.default.create();
40
- const mockedAxios = axios_1.default;
41
- // Mock KalshiAuth
42
- jest.mock("./auth");
43
- const MockedKalshiAuth = auth_1.KalshiAuth;
44
- describe("KalshiExchange", () => {
45
- let exchange;
46
- const mockCredentials = {
47
- apiKey: "test-api-key",
48
- privateKey: "mock-private-key",
49
- };
50
- beforeEach(() => {
51
- jest.clearAllMocks();
52
- // Reset the mock instance methods to ensure clean state
53
- mockAxiosInstance.get.mockReset();
54
- mockAxiosInstance.post.mockReset();
55
- mockAxiosInstance.delete.mockReset();
56
- mockAxiosInstance.request.mockReset();
57
- // Mock the getHeaders method
58
- MockedKalshiAuth.prototype.getHeaders = jest.fn().mockReturnValue({
59
- "KALSHI-ACCESS-KEY": "test-api-key",
60
- "KALSHI-ACCESS-TIMESTAMP": "1234567890",
61
- "KALSHI-ACCESS-SIGNATURE": "mock-signature",
62
- "Content-Type": "application/json",
63
- });
64
- });
65
- describe("Authentication", () => {
66
- it("should throw error when trading without credentials", async () => {
67
- exchange = new kalshi_1.KalshiExchange();
68
- await expect(exchange.fetchBalance()).rejects.toThrow("Trading operations require authentication");
69
- });
70
- it("should initialize with credentials", () => {
71
- exchange = new kalshi_1.KalshiExchange(mockCredentials);
72
- expect(exchange).toBeDefined();
73
- });
74
- });
75
- describe("Market Data Methods", () => {
76
- beforeEach(() => {
77
- exchange = new kalshi_1.KalshiExchange();
78
- });
79
- it("should fetch markets", async () => {
80
- const mockResponse = {
81
- data: {
82
- markets: [
83
- {
84
- ticker: "TEST-MARKET",
85
- title: "Test Market",
86
- yes_bid: 50,
87
- yes_ask: 52,
88
- volume: 1000,
89
- },
90
- ],
91
- },
92
- };
93
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
94
- const markets = await exchange.fetchMarkets();
95
- expect(markets).toBeDefined();
96
- });
97
- });
98
- describe("Trading Methods", () => {
99
- beforeEach(() => {
100
- exchange = new kalshi_1.KalshiExchange(mockCredentials);
101
- });
102
- describe("createOrder", () => {
103
- it("should create buy order with yes_price for buy side", async () => {
104
- const orderParams = {
105
- marketId: "TEST-MARKET",
106
- outcomeId: "yes",
107
- side: "buy",
108
- type: "limit",
109
- amount: 10,
110
- price: 0.55,
111
- };
112
- const mockResponse = {
113
- data: {
114
- order: {
115
- order_id: "order-123",
116
- ticker: "TEST-MARKET",
117
- status: "resting",
118
- count: 10,
119
- remaining_count: 10,
120
- created_time: "2026-01-13T12:00:00Z",
121
- queue_position: 1,
122
- },
123
- },
124
- };
125
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
126
- const order = await exchange.createOrder(orderParams);
127
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
128
- method: "POST",
129
- url: "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders",
130
- data: expect.objectContaining({
131
- ticker: "TEST-MARKET",
132
- side: "yes",
133
- action: "buy",
134
- count: 10,
135
- type: "limit",
136
- yes_price: 55, // 0.55 * 100
137
- }),
138
- }));
139
- expect(order.id).toBe("order-123");
140
- expect(order.status).toBe("open");
141
- });
142
- it("should create sell order with no_price for sell side", async () => {
143
- const orderParams = {
144
- marketId: "TEST-MARKET",
145
- outcomeId: "no",
146
- side: "sell",
147
- type: "limit",
148
- amount: 5,
149
- price: 0.45,
150
- };
151
- const mockResponse = {
152
- data: {
153
- order: {
154
- order_id: "order-456",
155
- ticker: "TEST-MARKET",
156
- status: "resting",
157
- count: 5,
158
- remaining_count: 5,
159
- created_time: "2026-01-13T12:00:00Z",
160
- queue_position: 1,
161
- },
162
- },
163
- };
164
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
165
- await exchange.createOrder(orderParams);
166
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
167
- method: "POST",
168
- url: "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders",
169
- data: expect.objectContaining({
170
- ticker: "TEST-MARKET",
171
- side: "no",
172
- action: "sell",
173
- count: 5,
174
- type: "limit",
175
- no_price: 45, // 0.45 * 100
176
- }),
177
- }));
178
- });
179
- });
180
- describe("fetchOpenOrders", () => {
181
- it("should sign request without query parameters", async () => {
182
- const mockResponse = {
183
- data: {
184
- orders: [
185
- {
186
- order_id: "order-123",
187
- ticker: "TEST-MARKET",
188
- side: "yes",
189
- type: "limit",
190
- yes_price: 55,
191
- count: 10,
192
- remaining_count: 10,
193
- created_time: "2026-01-13T12:00:00Z",
194
- },
195
- ],
196
- },
197
- };
198
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
199
- await exchange.fetchOpenOrders();
200
- // Verify the request includes the correct params
201
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
202
- method: "GET",
203
- url: "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders",
204
- params: expect.objectContaining({ status: "resting" }),
205
- }));
206
- // Verify getHeaders was called with base path only (no query params)
207
- expect(MockedKalshiAuth.prototype.getHeaders).toHaveBeenCalledWith("GET", (0, config_1.getApiPath)("/portfolio/orders"));
208
- });
209
- it("should include ticker in query params when marketId provided", async () => {
210
- const mockResponse = { data: { orders: [] } };
211
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
212
- await exchange.fetchOpenOrders("TEST-MARKET");
213
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
214
- method: "GET",
215
- url: "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders",
216
- params: expect.objectContaining({
217
- status: "resting",
218
- ticker: "TEST-MARKET",
219
- }),
220
- }));
221
- });
222
- });
223
- describe("fetchPositions", () => {
224
- it("should handle positions with zero contracts", async () => {
225
- const mockResponse = {
226
- data: {
227
- market_positions: [
228
- {
229
- ticker: "TEST-MARKET",
230
- position: 0,
231
- total_cost: 0,
232
- market_exposure: 0,
233
- realized_pnl: 0,
234
- },
235
- ],
236
- },
237
- };
238
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
239
- const positions = await exchange.fetchPositions();
240
- expect(positions).toHaveLength(1);
241
- expect(positions[0].size).toBe(0);
242
- expect(positions[0].entryPrice).toBe(0); // Should not throw division by zero
243
- });
244
- it("should correctly calculate average price and PnL", async () => {
245
- const mockResponse = {
246
- data: {
247
- market_positions: [
248
- {
249
- ticker: "TEST-MARKET",
250
- position: 10,
251
- total_cost: 550, // 10 contracts at $0.55 each = $5.50 = 550 cents
252
- market_exposure: 100, // $1.00 unrealized PnL
253
- realized_pnl: 50, // $0.50 realized PnL
254
- },
255
- ],
256
- },
257
- };
258
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
259
- const positions = await exchange.fetchPositions();
260
- expect(positions).toHaveLength(1);
261
- expect(positions[0].size).toBe(10);
262
- expect(positions[0].entryPrice).toBe(0.55); // 550 / 10 / 100
263
- expect(positions[0].unrealizedPnL).toBe(1.0); // 100 / 100
264
- expect(positions[0].realizedPnL).toBe(0.5); // 50 / 100
265
- });
266
- it("should handle short positions", async () => {
267
- const mockResponse = {
268
- data: {
269
- market_positions: [
270
- {
271
- ticker: "TEST-MARKET",
272
- position: -5, // Short position
273
- total_cost: 250,
274
- market_exposure: -50,
275
- realized_pnl: 25,
276
- },
277
- ],
278
- },
279
- };
280
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
281
- const positions = await exchange.fetchPositions();
282
- expect(positions[0].size).toBe(-5); // Negative for short
283
- expect(Math.abs(positions[0].size)).toBe(5); // Absolute value
284
- });
285
- });
286
- describe("fetchBalance", () => {
287
- it("should correctly convert cents to dollars", async () => {
288
- const mockResponse = {
289
- data: {
290
- balance: 10000, // $100.00 available
291
- portfolio_value: 15000, // $150.00 total
292
- },
293
- };
294
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
295
- const balances = await exchange.fetchBalance();
296
- expect(balances).toHaveLength(1);
297
- expect(balances[0].currency).toBe("USD");
298
- expect(balances[0].available).toBe(100.0);
299
- expect(balances[0].total).toBe(150.0);
300
- expect(balances[0].locked).toBe(50.0); // 150 - 100
301
- });
302
- });
303
- describe("cancelOrder", () => {
304
- it("should cancel order successfully", async () => {
305
- const mockResponse = {
306
- data: {
307
- order: {
308
- order_id: "order-123",
309
- ticker: "TEST-MARKET",
310
- side: "yes",
311
- count: 10,
312
- remaining_count: 5,
313
- created_time: "2026-01-13T12:00:00Z",
314
- },
315
- },
316
- };
317
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
318
- const order = await exchange.cancelOrder("order-123");
319
- expect(order.status).toBe("cancelled");
320
- expect(order.filled).toBe(5); // count - remaining_count
321
- expect(order.remaining).toBe(0);
322
- });
323
- });
324
- describe("Trading History Methods", () => {
325
- it("should map GetFills response to UserTrade array", async () => {
326
- const mockResponse = {
327
- data: {
328
- fills: [
329
- {
330
- fill_id: "fill-abc",
331
- order_id: "order-123",
332
- created_time: "2026-01-13T12:00:00Z",
333
- yes_price: 55,
334
- count: 10,
335
- side: "yes",
336
- },
337
- {
338
- fill_id: "fill-def",
339
- order_id: "order-456",
340
- created_time: "2026-01-13T13:00:00Z",
341
- yes_price: 45,
342
- count: 5,
343
- side: "no",
344
- },
345
- ],
346
- },
347
- };
348
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
349
- const trades = await exchange.fetchMyTrades();
350
- expect(Array.isArray(trades)).toBe(true);
351
- expect(trades).toHaveLength(2);
352
- expect(trades[0].id).toBe("fill-abc");
353
- expect(trades[0].orderId).toBe("order-123");
354
- expect(trades[0].price).toBe(0.55); // 55 / 100
355
- expect(trades[0].amount).toBe(10);
356
- expect(trades[0].side).toBe("buy"); // 'yes' => 'buy'
357
- expect(trades[1].id).toBe("fill-def");
358
- expect(trades[1].side).toBe("sell"); // 'no' => 'sell'
359
- expect(trades[1].price).toBe(0.45); // 45 / 100
360
- });
361
- it("should pass outcomeId as ticker (stripping -NO suffix) and date params", async () => {
362
- const mockResponse = { data: { fills: [] } };
363
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
364
- await exchange.fetchMyTrades({
365
- outcomeId: "TEST-MARKET-NO",
366
- since: new Date("2026-01-01T00:00:00Z"),
367
- until: new Date("2026-01-31T00:00:00Z"),
368
- limit: 50,
369
- });
370
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
371
- params: expect.objectContaining({
372
- ticker: "TEST-MARKET", // -NO stripped
373
- min_ts: Math.floor(new Date("2026-01-01T00:00:00Z").getTime() / 1000),
374
- max_ts: Math.floor(new Date("2026-01-31T00:00:00Z").getTime() / 1000),
375
- limit: 50,
376
- }),
377
- }));
378
- });
379
- it("should return empty array when fills is missing", async () => {
380
- mockAxiosInstance.request.mockResolvedValue({ data: {} });
381
- const trades = await exchange.fetchMyTrades();
382
- expect(trades).toHaveLength(0);
383
- });
384
- describe("fetchClosedOrders", () => {
385
- it("should map GetHistoricalOrders response to Order array", async () => {
386
- const mockResponse = {
387
- data: {
388
- orders: [
389
- {
390
- order_id: "hist-order-1",
391
- ticker: "TEST-MARKET",
392
- side: "yes",
393
- type: "limit",
394
- yes_price: 60,
395
- count: 8,
396
- remaining_count: 0,
397
- status: "executed",
398
- created_time: "2026-01-10T10:00:00Z",
399
- },
400
- ],
401
- },
402
- };
403
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
404
- const orders = await exchange.fetchClosedOrders();
405
- expect(orders).toHaveLength(1);
406
- expect(orders[0].id).toBe("hist-order-1");
407
- expect(orders[0].marketId).toBe("TEST-MARKET");
408
- expect(orders[0].side).toBe("buy"); // 'yes' => 'buy'
409
- expect(orders[0].price).toBe(0.6); // 60 / 100
410
- expect(orders[0].amount).toBe(8);
411
- expect(orders[0].filled).toBe(8); // count - remaining_count (0)
412
- expect(orders[0].remaining).toBe(0);
413
- expect(orders[0].status).toBe("filled"); // 'executed' => 'filled'
414
- });
415
- it("should pass marketId as ticker and limit", async () => {
416
- const mockResponse = { data: { orders: [] } };
417
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
418
- await exchange.fetchClosedOrders({
419
- marketId: "TEST-MARKET",
420
- limit: 25,
421
- });
422
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
423
- params: expect.objectContaining({
424
- ticker: "TEST-MARKET",
425
- limit: 25,
426
- }),
427
- }));
428
- });
429
- });
430
- describe("fetchAllOrders", () => {
431
- it("should merge live and historical orders, dedup, and sort descending by timestamp", async () => {
432
- const liveResponse = {
433
- data: {
434
- orders: [
435
- {
436
- order_id: "order-live-1",
437
- ticker: "TEST",
438
- side: "yes",
439
- type: "limit",
440
- yes_price: 50,
441
- count: 5,
442
- remaining_count: 5,
443
- status: "resting",
444
- created_time: "2026-01-15T10:00:00Z",
445
- },
446
- {
447
- // duplicate that also appears in historical
448
- order_id: "order-hist-1",
449
- ticker: "TEST",
450
- side: "no",
451
- type: "limit",
452
- yes_price: 40,
453
- count: 3,
454
- remaining_count: 0,
455
- status: "executed",
456
- created_time: "2026-01-10T08:00:00Z",
457
- },
458
- ],
459
- },
460
- };
461
- const historicalResponse = {
462
- data: {
463
- orders: [
464
- {
465
- order_id: "order-hist-1", // duplicate
466
- ticker: "TEST",
467
- side: "no",
468
- type: "limit",
469
- yes_price: 40,
470
- count: 3,
471
- remaining_count: 0,
472
- status: "executed",
473
- created_time: "2026-01-10T08:00:00Z",
474
- },
475
- {
476
- order_id: "order-hist-2",
477
- ticker: "TEST",
478
- side: "yes",
479
- type: "limit",
480
- yes_price: 55,
481
- count: 10,
482
- remaining_count: 0,
483
- status: "executed",
484
- created_time: "2026-01-05T06:00:00Z",
485
- },
486
- ],
487
- },
488
- };
489
- mockAxiosInstance.request
490
- .mockResolvedValueOnce(liveResponse)
491
- .mockResolvedValueOnce(historicalResponse);
492
- const orders = await exchange.fetchAllOrders();
493
- // 3 unique orders (order-hist-1 deduped)
494
- expect(orders).toHaveLength(3);
495
- // sorted descending: order-live-1, order-hist-1, order-hist-2
496
- expect(orders[0].id).toBe("order-live-1");
497
- expect(orders[1].id).toBe("order-hist-1");
498
- expect(orders[2].id).toBe("order-hist-2");
499
- });
500
- });
501
- });
502
- describe("buildOrder", () => {
503
- it("should return correct raw body for a buy limit order without making HTTP call", async () => {
504
- const params = {
505
- marketId: "TEST-MARKET",
506
- outcomeId: "yes",
507
- side: "buy",
508
- type: "limit",
509
- amount: 10,
510
- price: 0.55,
511
- };
512
- const built = await exchange.buildOrder(params);
513
- expect(mockAxiosInstance.request).not.toHaveBeenCalled();
514
- expect(built.exchange).toBe("Kalshi");
515
- expect(built.params).toBe(params);
516
- expect(built.raw).toEqual(expect.objectContaining({
517
- ticker: "TEST-MARKET",
518
- side: "yes",
519
- action: "buy",
520
- count: 10,
521
- type: "limit",
522
- yes_price: 55,
523
- }));
524
- });
525
- it("should return correct raw body for a sell limit order", async () => {
526
- const params = {
527
- marketId: "TEST-MARKET",
528
- outcomeId: "no",
529
- side: "sell",
530
- type: "limit",
531
- amount: 5,
532
- price: 0.45,
533
- };
534
- const built = await exchange.buildOrder(params);
535
- expect(mockAxiosInstance.request).not.toHaveBeenCalled();
536
- expect(built.exchange).toBe("Kalshi");
537
- expect(built.raw).toEqual(expect.objectContaining({
538
- ticker: "TEST-MARKET",
539
- side: "no",
540
- action: "sell",
541
- count: 5,
542
- type: "limit",
543
- no_price: 45,
544
- }));
545
- });
546
- it("should mirror input params in built.params", async () => {
547
- const params = {
548
- marketId: "SOME-MARKET",
549
- outcomeId: "yes",
550
- side: "buy",
551
- type: "market",
552
- amount: 3,
553
- };
554
- const built = await exchange.buildOrder(params);
555
- expect(built.params).toBe(params);
556
- });
557
- });
558
- describe("submitOrder", () => {
559
- it("should POST built.raw to CreateOrder and return mapped order", async () => {
560
- const params = {
561
- marketId: "TEST-MARKET",
562
- outcomeId: "yes",
563
- side: "buy",
564
- type: "limit",
565
- amount: 10,
566
- price: 0.55,
567
- };
568
- const built = await exchange.buildOrder(params);
569
- const mockResponse = {
570
- data: {
571
- order: {
572
- order_id: "order-789",
573
- ticker: "TEST-MARKET",
574
- side: "yes",
575
- type: "limit",
576
- yes_price: 55,
577
- count: 10,
578
- remaining_count: 10,
579
- status: "resting",
580
- created_time: "2026-01-13T12:00:00Z",
581
- },
582
- },
583
- };
584
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
585
- const order = await exchange.submitOrder(built);
586
- expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
587
- method: "POST",
588
- url: "https://api.elections.kalshi.com/trade-api/v2/portfolio/orders",
589
- data: expect.objectContaining({
590
- ticker: "TEST-MARKET",
591
- side: "yes",
592
- action: "buy",
593
- count: 10,
594
- type: "limit",
595
- yes_price: 55,
596
- }),
597
- }));
598
- expect(order.id).toBe("order-789");
599
- expect(order.status).toBe("open");
600
- expect(order.price).toBe(0.55);
601
- });
602
- });
603
- describe("Order Status Mapping", () => {
604
- beforeEach(() => {
605
- exchange = new kalshi_1.KalshiExchange(mockCredentials);
606
- });
607
- it("should map resting to open", async () => {
608
- const mockResponse = {
609
- data: {
610
- order: {
611
- order_id: "order-123",
612
- ticker: "TEST",
613
- status: "resting",
614
- count: 10,
615
- created_time: "2026-01-13T12:00:00Z",
616
- },
617
- },
618
- };
619
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
620
- const order = await exchange.fetchOrder("order-123");
621
- expect(order.status).toBe("open");
622
- });
623
- it("should map executed to filled", async () => {
624
- const mockResponse = {
625
- data: {
626
- order: {
627
- order_id: "order-123",
628
- ticker: "TEST",
629
- status: "executed",
630
- count: 10,
631
- created_time: "2026-01-13T12:00:00Z",
632
- },
633
- },
634
- };
635
- mockAxiosInstance.request.mockResolvedValue(mockResponse);
636
- const order = await exchange.fetchOrder("order-123");
637
- expect(order.status).toBe("filled");
638
- });
639
- });
640
- });
641
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,24 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const price_1 = require("./price");
4
- describe("fromKalshiCents", () => {
5
- test("converts cents to a decimal probability", () => {
6
- expect((0, price_1.fromKalshiCents)(55)).toBe(0.55);
7
- expect((0, price_1.fromKalshiCents)(0)).toBe(0);
8
- expect((0, price_1.fromKalshiCents)(100)).toBe(1);
9
- });
10
- });
11
- describe("invertKalshiCents", () => {
12
- test("returns the complement of a cent value", () => {
13
- expect((0, price_1.invertKalshiCents)(45)).toBeCloseTo(0.55);
14
- expect((0, price_1.invertKalshiCents)(0)).toBe(1);
15
- expect((0, price_1.invertKalshiCents)(100)).toBe(0);
16
- });
17
- });
18
- describe("invertKalshiUnified", () => {
19
- test("returns the complement of a normalized price", () => {
20
- expect((0, price_1.invertKalshiUnified)(0.45)).toBeCloseTo(0.55);
21
- expect((0, price_1.invertKalshiUnified)(0)).toBe(1);
22
- expect((0, price_1.invertKalshiUnified)(1)).toBe(0);
23
- });
24
- });
@@ -1 +0,0 @@
1
- export {};