@spfunctions/cli 1.1.5 → 1.1.7
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/client.d.ts +5 -0
- package/dist/client.js +17 -0
- package/dist/commands/agent.d.ts +1 -0
- package/dist/commands/agent.js +1170 -76
- package/dist/commands/announcements.d.ts +3 -0
- package/dist/commands/announcements.js +28 -0
- package/dist/commands/balance.d.ts +3 -0
- package/dist/commands/balance.js +17 -0
- package/dist/commands/cancel.d.ts +5 -0
- package/dist/commands/cancel.js +41 -0
- package/dist/commands/dashboard.d.ts +11 -0
- package/dist/commands/dashboard.js +195 -0
- package/dist/commands/feed.d.ts +13 -0
- package/dist/commands/feed.js +73 -0
- package/dist/commands/fills.d.ts +4 -0
- package/dist/commands/fills.js +29 -0
- package/dist/commands/forecast.d.ts +4 -0
- package/dist/commands/forecast.js +53 -0
- package/dist/commands/history.d.ts +3 -0
- package/dist/commands/history.js +38 -0
- package/dist/commands/milestones.d.ts +8 -0
- package/dist/commands/milestones.js +56 -0
- package/dist/commands/orders.d.ts +4 -0
- package/dist/commands/orders.js +28 -0
- package/dist/commands/publish.js +21 -2
- package/dist/commands/rfq.d.ts +5 -0
- package/dist/commands/rfq.js +35 -0
- package/dist/commands/schedule.d.ts +3 -0
- package/dist/commands/schedule.js +38 -0
- package/dist/commands/settlements.d.ts +6 -0
- package/dist/commands/settlements.js +50 -0
- package/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +45 -3
- package/dist/commands/signal.js +12 -1
- package/dist/commands/strategies.d.ts +11 -0
- package/dist/commands/strategies.js +130 -0
- package/dist/commands/trade.d.ts +12 -0
- package/dist/commands/trade.js +78 -0
- package/dist/commands/whatif.d.ts +17 -0
- package/dist/commands/whatif.js +209 -0
- package/dist/config.d.ts +2 -0
- package/dist/config.js +13 -0
- package/dist/index.js +177 -3
- package/dist/kalshi.d.ts +71 -0
- package/dist/kalshi.js +257 -17
- package/package.json +1 -1
package/dist/kalshi.js
CHANGED
|
@@ -16,8 +16,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
17
|
exports.isKalshiConfigured = isKalshiConfigured;
|
|
18
18
|
exports.getPositions = getPositions;
|
|
19
|
+
exports.kalshiPriceCents = kalshiPriceCents;
|
|
19
20
|
exports.getMarketPrice = getMarketPrice;
|
|
20
21
|
exports.getOrderbook = getOrderbook;
|
|
22
|
+
exports.getSettlements = getSettlements;
|
|
23
|
+
exports.getBalance = getBalance;
|
|
24
|
+
exports.getOrders = getOrders;
|
|
25
|
+
exports.getFills = getFills;
|
|
26
|
+
exports.getForecastHistory = getForecastHistory;
|
|
27
|
+
exports.getExchangeAnnouncements = getExchangeAnnouncements;
|
|
28
|
+
exports.getHistoricalMarket = getHistoricalMarket;
|
|
29
|
+
exports.createOrder = createOrder;
|
|
30
|
+
exports.cancelOrder = cancelOrder;
|
|
31
|
+
exports.batchCancelOrders = batchCancelOrders;
|
|
32
|
+
exports.amendOrder = amendOrder;
|
|
33
|
+
exports.createRFQ = createRFQ;
|
|
21
34
|
const fs_1 = __importDefault(require("fs"));
|
|
22
35
|
const path_1 = __importDefault(require("path"));
|
|
23
36
|
const crypto_1 = __importDefault(require("crypto"));
|
|
@@ -87,7 +100,9 @@ async function kalshiAuthGet(apiPath) {
|
|
|
87
100
|
throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
|
|
88
101
|
}
|
|
89
102
|
const url = `${KALSHI_API_BASE}${apiPath}`;
|
|
90
|
-
|
|
103
|
+
// Kalshi signing MUST exclude query params — sign only the path portion
|
|
104
|
+
const pathOnly = apiPath.split('?')[0];
|
|
105
|
+
const { headers } = signRequest('GET', `/trade-api/v2${pathOnly}`, privateKey);
|
|
91
106
|
const res = await fetch(url, { method: 'GET', headers });
|
|
92
107
|
if (!res.ok) {
|
|
93
108
|
const text = await res.text();
|
|
@@ -95,6 +110,44 @@ async function kalshiAuthGet(apiPath) {
|
|
|
95
110
|
}
|
|
96
111
|
return res.json();
|
|
97
112
|
}
|
|
113
|
+
async function kalshiAuthPost(apiPath, body) {
|
|
114
|
+
const privateKey = loadPrivateKey();
|
|
115
|
+
if (!privateKey) {
|
|
116
|
+
throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
|
|
117
|
+
}
|
|
118
|
+
const url = `${KALSHI_API_BASE}${apiPath}`;
|
|
119
|
+
const pathOnly = apiPath.split('?')[0];
|
|
120
|
+
const { headers } = signRequest('POST', `/trade-api/v2${pathOnly}`, privateKey);
|
|
121
|
+
const res = await fetch(url, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
headers,
|
|
124
|
+
body: JSON.stringify(body),
|
|
125
|
+
});
|
|
126
|
+
if (!res.ok) {
|
|
127
|
+
const text = await res.text();
|
|
128
|
+
throw new Error(`Kalshi API ${res.status}: ${text}`);
|
|
129
|
+
}
|
|
130
|
+
return res.json();
|
|
131
|
+
}
|
|
132
|
+
async function kalshiAuthDelete(apiPath) {
|
|
133
|
+
const privateKey = loadPrivateKey();
|
|
134
|
+
if (!privateKey) {
|
|
135
|
+
throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
|
|
136
|
+
}
|
|
137
|
+
const url = `${KALSHI_API_BASE}${apiPath}`;
|
|
138
|
+
const pathOnly = apiPath.split('?')[0];
|
|
139
|
+
const { headers } = signRequest('DELETE', `/trade-api/v2${pathOnly}`, privateKey);
|
|
140
|
+
const res = await fetch(url, { method: 'DELETE', headers });
|
|
141
|
+
if (!res.ok) {
|
|
142
|
+
const text = await res.text();
|
|
143
|
+
throw new Error(`Kalshi API ${res.status}: ${text}`);
|
|
144
|
+
}
|
|
145
|
+
const contentType = res.headers.get('content-type');
|
|
146
|
+
if (contentType?.includes('application/json')) {
|
|
147
|
+
return res.json();
|
|
148
|
+
}
|
|
149
|
+
return {};
|
|
150
|
+
}
|
|
98
151
|
/**
|
|
99
152
|
* Get user's live positions from Kalshi.
|
|
100
153
|
* Returns null if Kalshi is not configured.
|
|
@@ -138,6 +191,32 @@ async function getPositions() {
|
|
|
138
191
|
return null;
|
|
139
192
|
}
|
|
140
193
|
}
|
|
194
|
+
/**
|
|
195
|
+
* Extract price in cents (0-100) from a Kalshi market object.
|
|
196
|
+
* Dollars fields first (Kalshi API returns null for integer cents fields).
|
|
197
|
+
*/
|
|
198
|
+
function kalshiPriceCents(market) {
|
|
199
|
+
if (market.last_price_dollars) {
|
|
200
|
+
const p = parseFloat(market.last_price_dollars);
|
|
201
|
+
if (!isNaN(p) && p > 0)
|
|
202
|
+
return Math.round(p * 100);
|
|
203
|
+
}
|
|
204
|
+
if (market.yes_bid_dollars) {
|
|
205
|
+
const p = parseFloat(market.yes_bid_dollars);
|
|
206
|
+
if (!isNaN(p) && p > 0)
|
|
207
|
+
return Math.round(p * 100);
|
|
208
|
+
}
|
|
209
|
+
if (market.yes_ask_dollars) {
|
|
210
|
+
const p = parseFloat(market.yes_ask_dollars);
|
|
211
|
+
if (!isNaN(p) && p > 0)
|
|
212
|
+
return Math.round(p * 100);
|
|
213
|
+
}
|
|
214
|
+
if (market.last_price != null && market.last_price > 0)
|
|
215
|
+
return market.last_price;
|
|
216
|
+
if (market.yes_bid != null && market.yes_bid > 0)
|
|
217
|
+
return market.yes_bid;
|
|
218
|
+
return 50;
|
|
219
|
+
}
|
|
141
220
|
/**
|
|
142
221
|
* Get the current market price for a given ticker (public, no auth).
|
|
143
222
|
* Returns price in cents (0-100) or null.
|
|
@@ -150,22 +229,8 @@ async function getMarketPrice(ticker) {
|
|
|
150
229
|
return null;
|
|
151
230
|
const data = await res.json();
|
|
152
231
|
const m = data.market || data;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
return m.last_price;
|
|
156
|
-
if (m.yes_bid && m.yes_bid > 0)
|
|
157
|
-
return m.yes_bid;
|
|
158
|
-
if (m.last_price_dollars) {
|
|
159
|
-
const parsed = parseFloat(m.last_price_dollars);
|
|
160
|
-
if (!isNaN(parsed) && parsed > 0)
|
|
161
|
-
return Math.round(parsed * 100);
|
|
162
|
-
}
|
|
163
|
-
if (m.yes_bid_dollars) {
|
|
164
|
-
const parsed = parseFloat(m.yes_bid_dollars);
|
|
165
|
-
if (!isNaN(parsed) && parsed > 0)
|
|
166
|
-
return Math.round(parsed * 100);
|
|
167
|
-
}
|
|
168
|
-
return null;
|
|
232
|
+
const price = kalshiPriceCents(m);
|
|
233
|
+
return price === 50 && !m.last_price_dollars && !m.yes_bid_dollars ? null : price;
|
|
169
234
|
}
|
|
170
235
|
catch {
|
|
171
236
|
return null;
|
|
@@ -213,3 +278,178 @@ async function getOrderbook(ticker) {
|
|
|
213
278
|
return null;
|
|
214
279
|
}
|
|
215
280
|
}
|
|
281
|
+
// ============================================================================
|
|
282
|
+
// SETTLEMENTS (Authenticated)
|
|
283
|
+
// ============================================================================
|
|
284
|
+
async function getSettlements(params) {
|
|
285
|
+
if (!isKalshiConfigured())
|
|
286
|
+
return null;
|
|
287
|
+
try {
|
|
288
|
+
const searchParams = new URLSearchParams();
|
|
289
|
+
if (params?.limit)
|
|
290
|
+
searchParams.set('limit', params.limit.toString());
|
|
291
|
+
if (params?.cursor)
|
|
292
|
+
searchParams.set('cursor', params.cursor);
|
|
293
|
+
if (params?.ticker)
|
|
294
|
+
searchParams.set('ticker', params.ticker);
|
|
295
|
+
const data = await kalshiAuthGet(`/portfolio/settlements?${searchParams.toString()}`);
|
|
296
|
+
return { settlements: data.settlements || [], cursor: data.cursor || '' };
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
console.warn('[Kalshi] Failed to fetch settlements:', err);
|
|
300
|
+
return null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// ============================================================================
|
|
304
|
+
// BALANCE (Authenticated)
|
|
305
|
+
// ============================================================================
|
|
306
|
+
async function getBalance() {
|
|
307
|
+
if (!isKalshiConfigured())
|
|
308
|
+
return null;
|
|
309
|
+
try {
|
|
310
|
+
const data = await kalshiAuthGet('/portfolio/balance');
|
|
311
|
+
// API returns cents integers; convert to dollars
|
|
312
|
+
const balance = (data.balance || 0) / 100;
|
|
313
|
+
const portfolioValue = (data.portfolio_value || 0) / 100;
|
|
314
|
+
return { balance, portfolioValue };
|
|
315
|
+
}
|
|
316
|
+
catch (err) {
|
|
317
|
+
console.warn('[Kalshi] Failed to fetch balance:', err);
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ============================================================================
|
|
322
|
+
// ORDERS (Authenticated)
|
|
323
|
+
// ============================================================================
|
|
324
|
+
async function getOrders(params) {
|
|
325
|
+
if (!isKalshiConfigured())
|
|
326
|
+
return null;
|
|
327
|
+
try {
|
|
328
|
+
const searchParams = new URLSearchParams();
|
|
329
|
+
if (params?.status)
|
|
330
|
+
searchParams.set('status', params.status);
|
|
331
|
+
if (params?.ticker)
|
|
332
|
+
searchParams.set('ticker', params.ticker);
|
|
333
|
+
if (params?.limit)
|
|
334
|
+
searchParams.set('limit', params.limit.toString());
|
|
335
|
+
if (params?.cursor)
|
|
336
|
+
searchParams.set('cursor', params.cursor);
|
|
337
|
+
const data = await kalshiAuthGet(`/portfolio/orders?${searchParams.toString()}`);
|
|
338
|
+
return { orders: data.orders || [], cursor: data.cursor || '' };
|
|
339
|
+
}
|
|
340
|
+
catch (err) {
|
|
341
|
+
console.warn('[Kalshi] Failed to fetch orders:', err);
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// ============================================================================
|
|
346
|
+
// FILLS (Authenticated)
|
|
347
|
+
// ============================================================================
|
|
348
|
+
async function getFills(params) {
|
|
349
|
+
if (!isKalshiConfigured())
|
|
350
|
+
return null;
|
|
351
|
+
try {
|
|
352
|
+
const searchParams = new URLSearchParams();
|
|
353
|
+
if (params?.ticker)
|
|
354
|
+
searchParams.set('ticker', params.ticker);
|
|
355
|
+
if (params?.limit)
|
|
356
|
+
searchParams.set('limit', params.limit.toString());
|
|
357
|
+
if (params?.cursor)
|
|
358
|
+
searchParams.set('cursor', params.cursor);
|
|
359
|
+
const data = await kalshiAuthGet(`/portfolio/fills?${searchParams.toString()}`);
|
|
360
|
+
return { fills: data.fills || [], cursor: data.cursor || '' };
|
|
361
|
+
}
|
|
362
|
+
catch (err) {
|
|
363
|
+
console.warn('[Kalshi] Failed to fetch fills:', err);
|
|
364
|
+
return null;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
// ============================================================================
|
|
368
|
+
// FORECAST PERCENTILE HISTORY (Authenticated)
|
|
369
|
+
// ============================================================================
|
|
370
|
+
async function getForecastHistory(params) {
|
|
371
|
+
if (!isKalshiConfigured())
|
|
372
|
+
return null;
|
|
373
|
+
try {
|
|
374
|
+
const searchParams = new URLSearchParams();
|
|
375
|
+
for (const p of params.percentiles)
|
|
376
|
+
searchParams.append('percentiles', p.toString());
|
|
377
|
+
searchParams.set('start_ts', params.startTs.toString());
|
|
378
|
+
// Kalshi returns 400 if end_ts is past the latest available forecast data.
|
|
379
|
+
// Clamp to start of today UTC to avoid hitting future timestamps.
|
|
380
|
+
const todayMidnight = Math.floor(new Date().setUTCHours(0, 0, 0, 0) / 1000);
|
|
381
|
+
const clampedEnd = Math.min(params.endTs, todayMidnight);
|
|
382
|
+
searchParams.set('end_ts', clampedEnd.toString());
|
|
383
|
+
searchParams.set('period_interval', params.periodInterval.toString());
|
|
384
|
+
const apiPath = `/series/${params.seriesTicker}/events/${params.eventTicker}/forecast_percentile_history?${searchParams.toString()}`;
|
|
385
|
+
const data = await kalshiAuthGet(apiPath);
|
|
386
|
+
return data.forecast_history || [];
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
console.warn('[Kalshi] Failed to fetch forecast:', err);
|
|
390
|
+
return null;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
// ============================================================================
|
|
394
|
+
// EXCHANGE (Public, no auth)
|
|
395
|
+
// ============================================================================
|
|
396
|
+
async function getExchangeAnnouncements() {
|
|
397
|
+
try {
|
|
398
|
+
const res = await fetch(`${KALSHI_API_BASE}/exchange/announcements`, { headers: { 'Accept': 'application/json' } });
|
|
399
|
+
if (!res.ok)
|
|
400
|
+
return [];
|
|
401
|
+
const data = await res.json();
|
|
402
|
+
return data.announcements || [];
|
|
403
|
+
}
|
|
404
|
+
catch {
|
|
405
|
+
return [];
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function getHistoricalMarket(ticker) {
|
|
409
|
+
try {
|
|
410
|
+
const res = await fetch(`${KALSHI_API_BASE}/historical/markets/${ticker}`, { headers: { 'Accept': 'application/json' } });
|
|
411
|
+
if (!res.ok)
|
|
412
|
+
return null;
|
|
413
|
+
const data = await res.json();
|
|
414
|
+
return data.market || data || null;
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
return null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
// ============================================================================
|
|
421
|
+
// TRADING — ORDER MANAGEMENT (Authenticated, requires write key)
|
|
422
|
+
// ============================================================================
|
|
423
|
+
async function createOrder(params) {
|
|
424
|
+
return kalshiAuthPost('/portfolio/orders', params);
|
|
425
|
+
}
|
|
426
|
+
async function cancelOrder(orderId) {
|
|
427
|
+
await kalshiAuthDelete(`/portfolio/orders/${orderId}`);
|
|
428
|
+
}
|
|
429
|
+
async function batchCancelOrders(orderIds) {
|
|
430
|
+
await kalshiAuthPost('/portfolio/orders/batched', {
|
|
431
|
+
orders: orderIds.map(id => ({ action: 'cancel', order_id: id })),
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
async function amendOrder(orderId, params) {
|
|
435
|
+
// PATCH - need to implement kalshiAuthPatch or use POST
|
|
436
|
+
const privateKey = loadPrivateKey();
|
|
437
|
+
if (!privateKey)
|
|
438
|
+
throw new Error('Kalshi private key not loaded.');
|
|
439
|
+
const apiPath = `/portfolio/orders/${orderId}`;
|
|
440
|
+
const url = `${KALSHI_API_BASE}${apiPath}`;
|
|
441
|
+
const { headers } = signRequest('PATCH', `/trade-api/v2${apiPath}`, privateKey);
|
|
442
|
+
const res = await fetch(url, {
|
|
443
|
+
method: 'PATCH',
|
|
444
|
+
headers,
|
|
445
|
+
body: JSON.stringify(params),
|
|
446
|
+
});
|
|
447
|
+
if (!res.ok) {
|
|
448
|
+
const text = await res.text();
|
|
449
|
+
throw new Error(`Kalshi API ${res.status}: ${text}`);
|
|
450
|
+
}
|
|
451
|
+
return res.json();
|
|
452
|
+
}
|
|
453
|
+
async function createRFQ(params) {
|
|
454
|
+
return kalshiAuthPost('/communications/rfqs', params);
|
|
455
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.7",
|
|
4
4
|
"description": "Prediction market intelligence CLI. Causal thesis model, 24/7 Kalshi/Polymarket scan, live orderbook, edge detection. Interactive agent mode with tool calling.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf": "./dist/index.js"
|