pmxt-core 2.8.0 → 2.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BaseExchange.d.ts +48 -0
- package/dist/BaseExchange.js +116 -0
- package/dist/exchanges/kalshi/api.d.ts +1763 -0
- package/dist/exchanges/kalshi/api.js +2554 -0
- package/dist/exchanges/kalshi/fetchEvents.d.ts +3 -2
- package/dist/exchanges/kalshi/fetchEvents.js +9 -16
- package/dist/exchanges/kalshi/fetchMarkets.d.ts +3 -2
- package/dist/exchanges/kalshi/fetchMarkets.js +24 -32
- package/dist/exchanges/kalshi/fetchOHLCV.d.ts +1 -2
- package/dist/exchanges/kalshi/fetchOHLCV.js +9 -11
- package/dist/exchanges/kalshi/index.d.ts +2 -1
- package/dist/exchanges/kalshi/index.js +161 -183
- package/dist/exchanges/kalshi/kalshi.test.js +51 -31
- package/dist/exchanges/limitless/api.d.ts +555 -0
- package/dist/exchanges/limitless/api.js +863 -0
- package/dist/exchanges/limitless/fetchEvents.d.ts +1 -2
- package/dist/exchanges/limitless/fetchEvents.js +9 -15
- package/dist/exchanges/limitless/fetchMarkets.d.ts +1 -2
- package/dist/exchanges/limitless/fetchMarkets.js +9 -16
- package/dist/exchanges/limitless/fetchOHLCV.d.ts +1 -2
- package/dist/exchanges/limitless/fetchOHLCV.js +2 -11
- package/dist/exchanges/limitless/fetchOrderBook.d.ts +1 -2
- package/dist/exchanges/limitless/fetchOrderBook.js +2 -11
- package/dist/exchanges/limitless/index.d.ts +1 -0
- package/dist/exchanges/limitless/index.js +28 -7
- package/dist/exchanges/limitless/websocket.d.ts +2 -1
- package/dist/exchanges/limitless/websocket.js +6 -4
- package/dist/exchanges/myriad/api.d.ts +294 -0
- package/dist/exchanges/myriad/api.js +690 -0
- package/dist/exchanges/myriad/fetchOHLCV.d.ts +1 -2
- package/dist/exchanges/myriad/fetchOHLCV.js +3 -11
- package/dist/exchanges/myriad/fetchOrderBook.d.ts +1 -2
- package/dist/exchanges/myriad/fetchOrderBook.js +3 -11
- package/dist/exchanges/myriad/index.d.ts +2 -0
- package/dist/exchanges/myriad/index.js +116 -103
- package/dist/exchanges/myriad/websocket.d.ts +2 -2
- package/dist/exchanges/myriad/websocket.js +28 -6
- package/dist/exchanges/polymarket/api-clob.d.ts +346 -0
- package/dist/exchanges/polymarket/api-clob.js +517 -0
- package/dist/exchanges/polymarket/api-data.d.ts +789 -0
- package/dist/exchanges/polymarket/api-data.js +860 -0
- package/dist/exchanges/polymarket/api-gamma.d.ts +556 -0
- package/dist/exchanges/polymarket/api-gamma.js +1161 -0
- package/dist/exchanges/polymarket/fetchEvents.js +0 -68
- package/dist/exchanges/polymarket/fetchOHLCV.d.ts +1 -2
- package/dist/exchanges/polymarket/fetchOHLCV.js +4 -10
- package/dist/exchanges/polymarket/fetchOrderBook.d.ts +1 -2
- package/dist/exchanges/polymarket/fetchOrderBook.js +2 -10
- package/dist/exchanges/polymarket/fetchTrades.d.ts +1 -2
- package/dist/exchanges/polymarket/fetchTrades.js +2 -11
- package/dist/exchanges/polymarket/index.d.ts +10 -0
- package/dist/exchanges/polymarket/index.js +110 -5
- package/dist/exchanges/probable/api.d.ts +605 -0
- package/dist/exchanges/probable/api.js +887 -0
- package/dist/exchanges/probable/fetchEvents.d.ts +3 -3
- package/dist/exchanges/probable/fetchEvents.js +28 -25
- package/dist/exchanges/probable/fetchMarkets.d.ts +1 -1
- package/dist/exchanges/probable/fetchMarkets.js +25 -21
- package/dist/exchanges/probable/index.d.ts +1 -0
- package/dist/exchanges/probable/index.js +92 -10
- package/dist/exchanges/probable/utils.d.ts +1 -2
- package/dist/exchanges/probable/utils.js +4 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -1
- package/dist/utils/openapi.d.ts +9 -0
- package/dist/utils/openapi.js +59 -0
- package/package.json +4 -3
- package/dist/exchanges/kalshi/fetchOrderBook.d.ts +0 -3
- package/dist/exchanges/kalshi/fetchOrderBook.js +0 -59
- package/dist/exchanges/kalshi/fetchTrades.d.ts +0 -4
- package/dist/exchanges/kalshi/fetchTrades.js +0 -31
- package/dist/exchanges/limitless/fetchPositions.d.ts +0 -2
- package/dist/exchanges/limitless/fetchPositions.js +0 -34
- package/dist/exchanges/myriad/fetchTrades.d.ts +0 -4
- package/dist/exchanges/myriad/fetchTrades.js +0 -62
- package/dist/exchanges/polymarket/fetchPositions.d.ts +0 -2
- package/dist/exchanges/polymarket/fetchPositions.js +0 -34
- package/dist/exchanges/probable/fetchOHLCV.d.ts +0 -4
- package/dist/exchanges/probable/fetchOHLCV.js +0 -83
- package/dist/exchanges/probable/fetchOrderBook.d.ts +0 -3
- package/dist/exchanges/probable/fetchOrderBook.js +0 -37
- package/dist/exchanges/probable/fetchPositions.d.ts +0 -2
- package/dist/exchanges/probable/fetchPositions.js +0 -33
|
@@ -5,12 +5,13 @@ const BaseExchange_1 = require("../../BaseExchange");
|
|
|
5
5
|
const fetchMarkets_1 = require("./fetchMarkets");
|
|
6
6
|
const fetchEvents_1 = require("./fetchEvents");
|
|
7
7
|
const fetchOHLCV_1 = require("./fetchOHLCV");
|
|
8
|
-
const fetchOrderBook_1 = require("./fetchOrderBook");
|
|
9
|
-
const fetchTrades_1 = require("./fetchTrades");
|
|
10
8
|
const auth_1 = require("./auth");
|
|
9
|
+
const validation_1 = require("../../utils/validation");
|
|
11
10
|
const websocket_1 = require("./websocket");
|
|
12
11
|
const errors_1 = require("./errors");
|
|
13
12
|
const errors_2 = require("../../errors");
|
|
13
|
+
const openapi_1 = require("../../utils/openapi");
|
|
14
|
+
const api_1 = require("./api");
|
|
14
15
|
class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
15
16
|
has = {
|
|
16
17
|
fetchMarkets: true,
|
|
@@ -47,12 +48,23 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
47
48
|
if (credentials?.apiKey && credentials?.privateKey) {
|
|
48
49
|
this.auth = new auth_1.KalshiAuth(credentials);
|
|
49
50
|
}
|
|
51
|
+
const descriptor = (0, openapi_1.parseOpenApiSpec)(api_1.kalshiApiSpec);
|
|
52
|
+
this.defineImplicitApi(descriptor);
|
|
50
53
|
}
|
|
51
54
|
get name() {
|
|
52
55
|
return "Kalshi";
|
|
53
56
|
}
|
|
54
|
-
|
|
55
|
-
|
|
57
|
+
// ----------------------------------------------------------------------------
|
|
58
|
+
// Implicit API Auth & Error Mapping
|
|
59
|
+
// ----------------------------------------------------------------------------
|
|
60
|
+
sign(method, path, _params) {
|
|
61
|
+
const auth = this.ensureAuth();
|
|
62
|
+
// The implicit API passes just the spec path (e.g. /portfolio/balance),
|
|
63
|
+
// but Kalshi's signature requires the full path including /trade-api/v2.
|
|
64
|
+
return auth.getHeaders(method, '/trade-api/v2' + path);
|
|
65
|
+
}
|
|
66
|
+
mapImplicitApiError(error) {
|
|
67
|
+
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
56
68
|
}
|
|
57
69
|
// ----------------------------------------------------------------------------
|
|
58
70
|
// Helpers
|
|
@@ -68,218 +80,184 @@ class KalshiExchange extends BaseExchange_1.PredictionMarketExchange {
|
|
|
68
80
|
// Market Data Methods - Implementation for CCXT-style API
|
|
69
81
|
// ----------------------------------------------------------------------------
|
|
70
82
|
async fetchMarketsImpl(params) {
|
|
71
|
-
return (0, fetchMarkets_1.fetchMarkets)(params, this.
|
|
83
|
+
return (0, fetchMarkets_1.fetchMarkets)(params, this.callApi.bind(this));
|
|
72
84
|
}
|
|
73
85
|
async fetchEventsImpl(params) {
|
|
74
|
-
return (0, fetchEvents_1.fetchEvents)(params, this.
|
|
86
|
+
return (0, fetchEvents_1.fetchEvents)(params, this.callApi.bind(this));
|
|
75
87
|
}
|
|
76
88
|
async fetchOHLCV(id, params) {
|
|
77
|
-
return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.
|
|
89
|
+
return (0, fetchOHLCV_1.fetchOHLCV)(id, params, this.callApi.bind(this));
|
|
78
90
|
}
|
|
79
91
|
async fetchOrderBook(id) {
|
|
80
|
-
|
|
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;
|
|
96
|
+
let bids;
|
|
97
|
+
let asks;
|
|
98
|
+
if (isNoOutcome) {
|
|
99
|
+
bids = (data.no || []).map((level) => ({
|
|
100
|
+
price: level[0] / 100,
|
|
101
|
+
size: level[1],
|
|
102
|
+
}));
|
|
103
|
+
asks = (data.yes || []).map((level) => ({
|
|
104
|
+
price: 1 - (level[0] / 100),
|
|
105
|
+
size: level[1],
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
bids = (data.yes || []).map((level) => ({
|
|
110
|
+
price: level[0] / 100,
|
|
111
|
+
size: level[1],
|
|
112
|
+
}));
|
|
113
|
+
asks = (data.no || []).map((level) => ({
|
|
114
|
+
price: 1 - (level[0] / 100),
|
|
115
|
+
size: level[1],
|
|
116
|
+
}));
|
|
117
|
+
}
|
|
118
|
+
bids.sort((a, b) => b.price - a.price);
|
|
119
|
+
asks.sort((a, b) => a.price - b.price);
|
|
120
|
+
return { bids, asks, timestamp: Date.now() };
|
|
81
121
|
}
|
|
82
122
|
async fetchTrades(id, params) {
|
|
83
|
-
// Deprecation warning
|
|
84
123
|
if ('resolution' in params && params.resolution !== undefined) {
|
|
85
124
|
console.warn('[pmxt] Warning: The "resolution" parameter is deprecated for fetchTrades() and will be ignored. ' +
|
|
86
125
|
'It will be removed in v3.0.0. Please remove it from your code.');
|
|
87
126
|
}
|
|
88
|
-
|
|
127
|
+
const ticker = id.replace(/-NO$/, '');
|
|
128
|
+
const data = await this.callApi('GetTrades', { ticker, limit: params.limit || 100 });
|
|
129
|
+
const trades = data.trades || [];
|
|
130
|
+
return trades.map((t) => ({
|
|
131
|
+
id: t.trade_id,
|
|
132
|
+
timestamp: new Date(t.created_time).getTime(),
|
|
133
|
+
price: t.yes_price / 100,
|
|
134
|
+
amount: t.count,
|
|
135
|
+
side: t.taker_side === 'yes' ? 'buy' : 'sell',
|
|
136
|
+
}));
|
|
89
137
|
}
|
|
90
138
|
// ----------------------------------------------------------------------------
|
|
91
139
|
// User Data Methods
|
|
92
140
|
// ----------------------------------------------------------------------------
|
|
93
141
|
async fetchBalance() {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
// Kalshi response structure:
|
|
104
|
-
// - balance: Available balance in cents (for trading)
|
|
105
|
-
// - portfolio_value: Total portfolio value in cents (includes positions)
|
|
106
|
-
// - updated_ts: Unix timestamp of last update
|
|
107
|
-
const balanceCents = response.data.balance;
|
|
108
|
-
const portfolioValueCents = response.data.portfolio_value;
|
|
109
|
-
const available = balanceCents / 100;
|
|
110
|
-
const total = portfolioValueCents / 100;
|
|
111
|
-
const locked = total - available;
|
|
112
|
-
return [{
|
|
113
|
-
currency: 'USD',
|
|
114
|
-
total: total, // Total portfolio value (cash + positions)
|
|
115
|
-
available: available, // Available for trading
|
|
116
|
-
locked: locked // Value locked in positions
|
|
117
|
-
}];
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
121
|
-
}
|
|
142
|
+
const data = await this.callApi('GetBalance');
|
|
143
|
+
const available = data.balance / 100;
|
|
144
|
+
const total = data.portfolio_value / 100;
|
|
145
|
+
return [{
|
|
146
|
+
currency: 'USD',
|
|
147
|
+
total,
|
|
148
|
+
available,
|
|
149
|
+
locked: total - available,
|
|
150
|
+
}];
|
|
122
151
|
}
|
|
123
152
|
// ----------------------------------------------------------------------------
|
|
124
153
|
// Trading Methods
|
|
125
154
|
// ----------------------------------------------------------------------------
|
|
126
155
|
async createOrder(params) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
// Add price field based on side (yes_price for yes side, no_price for no side)
|
|
144
|
-
if (params.price) {
|
|
145
|
-
const priceInCents = Math.round(params.price * 100);
|
|
146
|
-
if (isYesSide) {
|
|
147
|
-
kalshiOrder.yes_price = priceInCents;
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
kalshiOrder.no_price = priceInCents;
|
|
151
|
-
}
|
|
156
|
+
const isYesSide = params.side === 'buy';
|
|
157
|
+
const kalshiOrder = {
|
|
158
|
+
ticker: params.marketId,
|
|
159
|
+
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',
|
|
162
|
+
count: params.amount,
|
|
163
|
+
type: params.type === 'limit' ? 'limit' : 'market',
|
|
164
|
+
};
|
|
165
|
+
if (params.price) {
|
|
166
|
+
const priceInCents = Math.round(params.price * 100);
|
|
167
|
+
if (isYesSide) {
|
|
168
|
+
kalshiOrder.yes_price = priceInCents;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
kalshiOrder.no_price = priceInCents;
|
|
152
172
|
}
|
|
153
|
-
const response = await this.http.post(`${baseUrl}${path}`, kalshiOrder, { headers });
|
|
154
|
-
const order = response.data.order;
|
|
155
|
-
return {
|
|
156
|
-
id: order.order_id,
|
|
157
|
-
marketId: params.marketId,
|
|
158
|
-
outcomeId: params.outcomeId,
|
|
159
|
-
side: params.side,
|
|
160
|
-
type: params.type,
|
|
161
|
-
price: params.price,
|
|
162
|
-
amount: params.amount,
|
|
163
|
-
status: this.mapKalshiOrderStatus(order.status),
|
|
164
|
-
filled: order.queue_position === 0 ? params.amount : 0,
|
|
165
|
-
remaining: order.remaining_count || params.amount,
|
|
166
|
-
timestamp: new Date(order.created_time).getTime()
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
catch (error) {
|
|
170
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
171
173
|
}
|
|
174
|
+
const data = await this.callApi('CreateOrder', kalshiOrder);
|
|
175
|
+
const order = data.order;
|
|
176
|
+
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(),
|
|
188
|
+
};
|
|
172
189
|
}
|
|
173
190
|
async cancelOrder(orderId) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
status: 'cancelled',
|
|
189
|
-
filled: order.count - (order.remaining_count || 0),
|
|
190
|
-
remaining: 0,
|
|
191
|
-
timestamp: new Date(order.created_time).getTime()
|
|
192
|
-
};
|
|
193
|
-
}
|
|
194
|
-
catch (error) {
|
|
195
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
196
|
-
}
|
|
191
|
+
const data = await this.callApi('CancelOrder', { order_id: orderId });
|
|
192
|
+
const order = data.order;
|
|
193
|
+
return {
|
|
194
|
+
id: order.order_id,
|
|
195
|
+
marketId: order.ticker,
|
|
196
|
+
outcomeId: order.ticker,
|
|
197
|
+
side: order.side === 'yes' ? 'buy' : 'sell',
|
|
198
|
+
type: 'limit',
|
|
199
|
+
amount: order.count,
|
|
200
|
+
status: 'cancelled',
|
|
201
|
+
filled: order.count - (order.remaining_count || 0),
|
|
202
|
+
remaining: 0,
|
|
203
|
+
timestamp: new Date(order.created_time).getTime(),
|
|
204
|
+
};
|
|
197
205
|
}
|
|
198
206
|
async fetchOrder(orderId) {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
status: this.mapKalshiOrderStatus(order.status),
|
|
215
|
-
filled: order.count - (order.remaining_count || 0),
|
|
216
|
-
remaining: order.remaining_count || 0,
|
|
217
|
-
timestamp: new Date(order.created_time).getTime()
|
|
218
|
-
};
|
|
219
|
-
}
|
|
220
|
-
catch (error) {
|
|
221
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
222
|
-
}
|
|
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
|
+
};
|
|
223
222
|
}
|
|
224
223
|
async fetchOpenOrders(marketId) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const basePath = '/trade-api/v2/portfolio/orders';
|
|
229
|
-
let queryParams = '?status=resting';
|
|
230
|
-
if (marketId) {
|
|
231
|
-
queryParams += `&ticker=${marketId}`;
|
|
232
|
-
}
|
|
233
|
-
const baseUrl = this.getBaseUrl();
|
|
234
|
-
// Sign only the base path, not the query parameters
|
|
235
|
-
const headers = auth.getHeaders('GET', basePath);
|
|
236
|
-
const response = await this.http.get(`${baseUrl}${basePath}${queryParams}`, { headers });
|
|
237
|
-
const orders = response.data.orders || [];
|
|
238
|
-
return orders.map((order) => ({
|
|
239
|
-
id: order.order_id,
|
|
240
|
-
marketId: order.ticker,
|
|
241
|
-
outcomeId: order.ticker,
|
|
242
|
-
side: order.side === 'yes' ? 'buy' : 'sell',
|
|
243
|
-
type: order.type === 'limit' ? 'limit' : 'market',
|
|
244
|
-
price: order.yes_price ? order.yes_price / 100 : undefined,
|
|
245
|
-
amount: order.count,
|
|
246
|
-
status: 'open',
|
|
247
|
-
filled: order.count - (order.remaining_count || 0),
|
|
248
|
-
remaining: order.remaining_count || 0,
|
|
249
|
-
timestamp: new Date(order.created_time).getTime()
|
|
250
|
-
}));
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
224
|
+
const queryParams = { status: 'resting' };
|
|
225
|
+
if (marketId) {
|
|
226
|
+
queryParams.ticker = marketId;
|
|
254
227
|
}
|
|
228
|
+
const data = await this.callApi('GetOrders', queryParams);
|
|
229
|
+
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(),
|
|
242
|
+
}));
|
|
255
243
|
}
|
|
256
244
|
async fetchPositions() {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
const
|
|
261
|
-
const
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
entryPrice: entryPrice,
|
|
274
|
-
currentPrice: pos.market_price ? pos.market_price / 100 : entryPrice,
|
|
275
|
-
unrealizedPnL: pos.market_exposure ? pos.market_exposure / 100 : 0,
|
|
276
|
-
realizedPnL: pos.realized_pnl ? pos.realized_pnl / 100 : 0
|
|
277
|
-
};
|
|
278
|
-
});
|
|
279
|
-
}
|
|
280
|
-
catch (error) {
|
|
281
|
-
throw errors_1.kalshiErrorMapper.mapError(error);
|
|
282
|
-
}
|
|
245
|
+
const data = await this.callApi('GetPositions');
|
|
246
|
+
const positions = data.market_positions || [];
|
|
247
|
+
return positions.map((pos) => {
|
|
248
|
+
const absPosition = Math.abs(pos.position);
|
|
249
|
+
const entryPrice = absPosition > 0 ? pos.total_cost / absPosition / 100 : 0;
|
|
250
|
+
return {
|
|
251
|
+
marketId: pos.ticker,
|
|
252
|
+
outcomeId: pos.ticker,
|
|
253
|
+
outcomeLabel: pos.ticker,
|
|
254
|
+
size: pos.position,
|
|
255
|
+
entryPrice,
|
|
256
|
+
currentPrice: pos.market_price ? pos.market_price / 100 : entryPrice,
|
|
257
|
+
unrealizedPnL: pos.market_exposure ? pos.market_exposure / 100 : 0,
|
|
258
|
+
realizedPnL: pos.realized_pnl ? pos.realized_pnl / 100 : 0,
|
|
259
|
+
};
|
|
260
|
+
});
|
|
283
261
|
}
|
|
284
262
|
// Helper to map Kalshi order status to unified status
|
|
285
263
|
mapKalshiOrderStatus(status) {
|
|
@@ -18,10 +18,13 @@ jest.mock('axios', () => {
|
|
|
18
18
|
get: jest.fn(),
|
|
19
19
|
post: jest.fn(),
|
|
20
20
|
delete: jest.fn(),
|
|
21
|
+
request: jest.fn(),
|
|
21
22
|
defaults: { headers: { common: {} } },
|
|
22
23
|
};
|
|
24
|
+
const actualAxios = jest.requireActual('axios');
|
|
23
25
|
const mockAxios = {
|
|
24
26
|
create: jest.fn(() => mockInstance),
|
|
27
|
+
isAxiosError: actualAxios.isAxiosError,
|
|
25
28
|
};
|
|
26
29
|
// Support both default and named exports
|
|
27
30
|
return {
|
|
@@ -49,6 +52,7 @@ describe('KalshiExchange', () => {
|
|
|
49
52
|
mockAxiosInstance.get.mockReset();
|
|
50
53
|
mockAxiosInstance.post.mockReset();
|
|
51
54
|
mockAxiosInstance.delete.mockReset();
|
|
55
|
+
mockAxiosInstance.request.mockReset();
|
|
52
56
|
// Mock the getHeaders method
|
|
53
57
|
MockedKalshiAuth.prototype.getHeaders = jest.fn().mockReturnValue({
|
|
54
58
|
'KALSHI-ACCESS-KEY': 'test-api-key',
|
|
@@ -85,7 +89,7 @@ describe('KalshiExchange', () => {
|
|
|
85
89
|
]
|
|
86
90
|
}
|
|
87
91
|
};
|
|
88
|
-
mockAxiosInstance.
|
|
92
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
89
93
|
const markets = await exchange.fetchMarkets();
|
|
90
94
|
expect(markets).toBeDefined();
|
|
91
95
|
});
|
|
@@ -117,16 +121,20 @@ describe('KalshiExchange', () => {
|
|
|
117
121
|
}
|
|
118
122
|
}
|
|
119
123
|
};
|
|
120
|
-
mockAxiosInstance.
|
|
124
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
121
125
|
const order = await exchange.createOrder(orderParams);
|
|
122
|
-
expect(mockAxiosInstance.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
|
|
127
|
+
method: 'POST',
|
|
128
|
+
url: 'https://api.elections.kalshi.com/trade-api/v2/portfolio/orders',
|
|
129
|
+
data: expect.objectContaining({
|
|
130
|
+
ticker: 'TEST-MARKET',
|
|
131
|
+
side: 'yes',
|
|
132
|
+
action: 'buy',
|
|
133
|
+
count: 10,
|
|
134
|
+
type: 'limit',
|
|
135
|
+
yes_price: 55 // 0.55 * 100
|
|
136
|
+
}),
|
|
137
|
+
}));
|
|
130
138
|
expect(order.id).toBe('order-123');
|
|
131
139
|
expect(order.status).toBe('open');
|
|
132
140
|
});
|
|
@@ -152,16 +160,20 @@ describe('KalshiExchange', () => {
|
|
|
152
160
|
}
|
|
153
161
|
}
|
|
154
162
|
};
|
|
155
|
-
mockAxiosInstance.
|
|
163
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
156
164
|
await exchange.createOrder(orderParams);
|
|
157
|
-
expect(mockAxiosInstance.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
|
|
166
|
+
method: 'POST',
|
|
167
|
+
url: 'https://api.elections.kalshi.com/trade-api/v2/portfolio/orders',
|
|
168
|
+
data: expect.objectContaining({
|
|
169
|
+
ticker: 'TEST-MARKET',
|
|
170
|
+
side: 'no',
|
|
171
|
+
action: 'sell',
|
|
172
|
+
count: 5,
|
|
173
|
+
type: 'limit',
|
|
174
|
+
no_price: 45 // 0.45 * 100
|
|
175
|
+
}),
|
|
176
|
+
}));
|
|
165
177
|
});
|
|
166
178
|
});
|
|
167
179
|
describe('fetchOpenOrders', () => {
|
|
@@ -182,18 +194,26 @@ describe('KalshiExchange', () => {
|
|
|
182
194
|
]
|
|
183
195
|
}
|
|
184
196
|
};
|
|
185
|
-
mockAxiosInstance.
|
|
197
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
186
198
|
await exchange.fetchOpenOrders();
|
|
187
|
-
// Verify the request
|
|
188
|
-
expect(mockAxiosInstance.
|
|
199
|
+
// Verify the request includes the correct params
|
|
200
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
|
|
201
|
+
method: 'GET',
|
|
202
|
+
url: 'https://api.elections.kalshi.com/trade-api/v2/portfolio/orders',
|
|
203
|
+
params: expect.objectContaining({ status: 'resting' }),
|
|
204
|
+
}));
|
|
189
205
|
// Verify getHeaders was called with base path only (no query params)
|
|
190
206
|
expect(MockedKalshiAuth.prototype.getHeaders).toHaveBeenCalledWith('GET', '/trade-api/v2/portfolio/orders');
|
|
191
207
|
});
|
|
192
208
|
it('should include ticker in query params when marketId provided', async () => {
|
|
193
209
|
const mockResponse = { data: { orders: [] } };
|
|
194
|
-
mockAxiosInstance.
|
|
210
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
195
211
|
await exchange.fetchOpenOrders('TEST-MARKET');
|
|
196
|
-
expect(mockAxiosInstance.
|
|
212
|
+
expect(mockAxiosInstance.request).toHaveBeenCalledWith(expect.objectContaining({
|
|
213
|
+
method: 'GET',
|
|
214
|
+
url: 'https://api.elections.kalshi.com/trade-api/v2/portfolio/orders',
|
|
215
|
+
params: expect.objectContaining({ status: 'resting', ticker: 'TEST-MARKET' }),
|
|
216
|
+
}));
|
|
197
217
|
});
|
|
198
218
|
});
|
|
199
219
|
describe('fetchPositions', () => {
|
|
@@ -211,7 +231,7 @@ describe('KalshiExchange', () => {
|
|
|
211
231
|
]
|
|
212
232
|
}
|
|
213
233
|
};
|
|
214
|
-
mockAxiosInstance.
|
|
234
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
215
235
|
const positions = await exchange.fetchPositions();
|
|
216
236
|
expect(positions).toHaveLength(1);
|
|
217
237
|
expect(positions[0].size).toBe(0);
|
|
@@ -231,7 +251,7 @@ describe('KalshiExchange', () => {
|
|
|
231
251
|
]
|
|
232
252
|
}
|
|
233
253
|
};
|
|
234
|
-
mockAxiosInstance.
|
|
254
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
235
255
|
const positions = await exchange.fetchPositions();
|
|
236
256
|
expect(positions).toHaveLength(1);
|
|
237
257
|
expect(positions[0].size).toBe(10);
|
|
@@ -253,7 +273,7 @@ describe('KalshiExchange', () => {
|
|
|
253
273
|
]
|
|
254
274
|
}
|
|
255
275
|
};
|
|
256
|
-
mockAxiosInstance.
|
|
276
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
257
277
|
const positions = await exchange.fetchPositions();
|
|
258
278
|
expect(positions[0].size).toBe(-5); // Negative for short
|
|
259
279
|
expect(Math.abs(positions[0].size)).toBe(5); // Absolute value
|
|
@@ -267,7 +287,7 @@ describe('KalshiExchange', () => {
|
|
|
267
287
|
portfolio_value: 15000 // $150.00 total
|
|
268
288
|
}
|
|
269
289
|
};
|
|
270
|
-
mockAxiosInstance.
|
|
290
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
271
291
|
const balances = await exchange.fetchBalance();
|
|
272
292
|
expect(balances).toHaveLength(1);
|
|
273
293
|
expect(balances[0].currency).toBe('USD');
|
|
@@ -290,7 +310,7 @@ describe('KalshiExchange', () => {
|
|
|
290
310
|
}
|
|
291
311
|
}
|
|
292
312
|
};
|
|
293
|
-
mockAxiosInstance.
|
|
313
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
294
314
|
const order = await exchange.cancelOrder('order-123');
|
|
295
315
|
expect(order.status).toBe('cancelled');
|
|
296
316
|
expect(order.filled).toBe(5); // count - remaining_count
|
|
@@ -314,7 +334,7 @@ describe('KalshiExchange', () => {
|
|
|
314
334
|
}
|
|
315
335
|
}
|
|
316
336
|
};
|
|
317
|
-
mockAxiosInstance.
|
|
337
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
318
338
|
const order = await exchange.fetchOrder('order-123');
|
|
319
339
|
expect(order.status).toBe('open');
|
|
320
340
|
});
|
|
@@ -330,7 +350,7 @@ describe('KalshiExchange', () => {
|
|
|
330
350
|
}
|
|
331
351
|
}
|
|
332
352
|
};
|
|
333
|
-
mockAxiosInstance.
|
|
353
|
+
mockAxiosInstance.request.mockResolvedValue(mockResponse);
|
|
334
354
|
const order = await exchange.fetchOrder('order-123');
|
|
335
355
|
expect(order.status).toBe('filled');
|
|
336
356
|
});
|