@spfunctions/cli 1.7.17 → 1.7.20

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 (180) hide show
  1. package/dist/101.index.js +1 -0
  2. package/dist/12.index.js +1 -0
  3. package/dist/160.index.js +1 -0
  4. package/dist/174.index.js +1 -0
  5. package/dist/278.index.js +6 -0
  6. package/dist/582.index.js +1 -0
  7. package/dist/641.index.js +324 -0
  8. package/dist/669.index.js +1 -0
  9. package/dist/722.index.js +1 -0
  10. package/dist/788.index.js +1 -0
  11. package/dist/816.index.js +12 -0
  12. package/dist/830.index.js +1 -0
  13. package/dist/921.index.js +1 -0
  14. package/dist/index.js +1 -813
  15. package/package.json +6 -2
  16. package/dist/cache.d.ts +0 -6
  17. package/dist/cache.js +0 -31
  18. package/dist/cache.test.d.ts +0 -1
  19. package/dist/cache.test.js +0 -73
  20. package/dist/client.d.ts +0 -56
  21. package/dist/client.js +0 -205
  22. package/dist/client.test.d.ts +0 -1
  23. package/dist/client.test.js +0 -89
  24. package/dist/commands/agent.d.ts +0 -20
  25. package/dist/commands/agent.js +0 -4119
  26. package/dist/commands/announcements.d.ts +0 -3
  27. package/dist/commands/announcements.js +0 -28
  28. package/dist/commands/augment.d.ts +0 -12
  29. package/dist/commands/augment.js +0 -56
  30. package/dist/commands/balance.d.ts +0 -3
  31. package/dist/commands/balance.js +0 -17
  32. package/dist/commands/book.d.ts +0 -17
  33. package/dist/commands/book.js +0 -220
  34. package/dist/commands/cancel.d.ts +0 -5
  35. package/dist/commands/cancel.js +0 -41
  36. package/dist/commands/context.d.ts +0 -6
  37. package/dist/commands/context.js +0 -208
  38. package/dist/commands/create.d.ts +0 -7
  39. package/dist/commands/create.js +0 -42
  40. package/dist/commands/dashboard.d.ts +0 -14
  41. package/dist/commands/dashboard.js +0 -215
  42. package/dist/commands/delta.d.ts +0 -16
  43. package/dist/commands/delta.js +0 -115
  44. package/dist/commands/edges.d.ts +0 -26
  45. package/dist/commands/edges.js +0 -237
  46. package/dist/commands/evaluate.d.ts +0 -4
  47. package/dist/commands/evaluate.js +0 -30
  48. package/dist/commands/explore.d.ts +0 -14
  49. package/dist/commands/explore.js +0 -115
  50. package/dist/commands/feed.d.ts +0 -13
  51. package/dist/commands/feed.js +0 -73
  52. package/dist/commands/fills.d.ts +0 -4
  53. package/dist/commands/fills.js +0 -29
  54. package/dist/commands/forecast.d.ts +0 -4
  55. package/dist/commands/forecast.js +0 -53
  56. package/dist/commands/get.d.ts +0 -5
  57. package/dist/commands/get.js +0 -98
  58. package/dist/commands/heartbeat.d.ts +0 -20
  59. package/dist/commands/heartbeat.js +0 -73
  60. package/dist/commands/history.d.ts +0 -3
  61. package/dist/commands/history.js +0 -38
  62. package/dist/commands/liquidity.d.ts +0 -14
  63. package/dist/commands/liquidity.js +0 -378
  64. package/dist/commands/list.d.ts +0 -5
  65. package/dist/commands/list.js +0 -38
  66. package/dist/commands/login.d.ts +0 -9
  67. package/dist/commands/login.js +0 -98
  68. package/dist/commands/markets.d.ts +0 -10
  69. package/dist/commands/markets.js +0 -39
  70. package/dist/commands/milestones.d.ts +0 -8
  71. package/dist/commands/milestones.js +0 -56
  72. package/dist/commands/orders.d.ts +0 -4
  73. package/dist/commands/orders.js +0 -28
  74. package/dist/commands/performance.d.ts +0 -11
  75. package/dist/commands/performance.js +0 -250
  76. package/dist/commands/positions.d.ts +0 -19
  77. package/dist/commands/positions.js +0 -294
  78. package/dist/commands/prompt.d.ts +0 -13
  79. package/dist/commands/prompt.js +0 -35
  80. package/dist/commands/publish.d.ts +0 -15
  81. package/dist/commands/publish.js +0 -39
  82. package/dist/commands/query.d.ts +0 -15
  83. package/dist/commands/query.js +0 -132
  84. package/dist/commands/rfq.d.ts +0 -5
  85. package/dist/commands/rfq.js +0 -35
  86. package/dist/commands/scan.d.ts +0 -11
  87. package/dist/commands/scan.js +0 -230
  88. package/dist/commands/schedule.d.ts +0 -3
  89. package/dist/commands/schedule.js +0 -38
  90. package/dist/commands/settlements.d.ts +0 -6
  91. package/dist/commands/settlements.js +0 -50
  92. package/dist/commands/setup.d.ts +0 -24
  93. package/dist/commands/setup.js +0 -700
  94. package/dist/commands/signal.d.ts +0 -6
  95. package/dist/commands/signal.js +0 -32
  96. package/dist/commands/strategies.d.ts +0 -11
  97. package/dist/commands/strategies.js +0 -130
  98. package/dist/commands/telegram.d.ts +0 -15
  99. package/dist/commands/telegram.js +0 -125
  100. package/dist/commands/trade.d.ts +0 -12
  101. package/dist/commands/trade.js +0 -112
  102. package/dist/commands/watch.d.ts +0 -19
  103. package/dist/commands/watch.js +0 -157
  104. package/dist/commands/whatif.d.ts +0 -17
  105. package/dist/commands/whatif.js +0 -209
  106. package/dist/commands/x.d.ts +0 -28
  107. package/dist/commands/x.js +0 -167
  108. package/dist/config.d.ts +0 -55
  109. package/dist/config.js +0 -139
  110. package/dist/config.test.d.ts +0 -1
  111. package/dist/config.test.js +0 -138
  112. package/dist/index.d.ts +0 -20
  113. package/dist/kalshi.d.ts +0 -144
  114. package/dist/kalshi.js +0 -498
  115. package/dist/polymarket.d.ts +0 -237
  116. package/dist/polymarket.js +0 -353
  117. package/dist/polymarket.test.d.ts +0 -1
  118. package/dist/polymarket.test.js +0 -424
  119. package/dist/share.d.ts +0 -4
  120. package/dist/share.js +0 -27
  121. package/dist/skills/loader.d.ts +0 -19
  122. package/dist/skills/loader.js +0 -86
  123. package/dist/telegram/agent-bridge.d.ts +0 -15
  124. package/dist/telegram/agent-bridge.js +0 -573
  125. package/dist/telegram/bot.d.ts +0 -10
  126. package/dist/telegram/bot.js +0 -297
  127. package/dist/telegram/commands.d.ts +0 -11
  128. package/dist/telegram/commands.js +0 -120
  129. package/dist/telegram/format.d.ts +0 -11
  130. package/dist/telegram/format.js +0 -51
  131. package/dist/telegram/format.test.d.ts +0 -1
  132. package/dist/telegram/format.test.js +0 -73
  133. package/dist/telegram/poller.d.ts +0 -6
  134. package/dist/telegram/poller.js +0 -32
  135. package/dist/topics.d.ts +0 -17
  136. package/dist/topics.js +0 -102
  137. package/dist/topics.test.d.ts +0 -1
  138. package/dist/topics.test.js +0 -131
  139. package/dist/tui/border.d.ts +0 -33
  140. package/dist/tui/border.js +0 -87
  141. package/dist/tui/chart.d.ts +0 -19
  142. package/dist/tui/chart.js +0 -117
  143. package/dist/tui/dashboard.d.ts +0 -9
  144. package/dist/tui/dashboard.js +0 -814
  145. package/dist/tui/layout.d.ts +0 -16
  146. package/dist/tui/layout.js +0 -41
  147. package/dist/tui/screen.d.ts +0 -33
  148. package/dist/tui/screen.js +0 -102
  149. package/dist/tui/state.d.ts +0 -40
  150. package/dist/tui/state.js +0 -36
  151. package/dist/tui/widgets/commandbar.d.ts +0 -8
  152. package/dist/tui/widgets/commandbar.js +0 -82
  153. package/dist/tui/widgets/detail.d.ts +0 -9
  154. package/dist/tui/widgets/detail.js +0 -151
  155. package/dist/tui/widgets/edges.d.ts +0 -4
  156. package/dist/tui/widgets/edges.js +0 -34
  157. package/dist/tui/widgets/liquidity.d.ts +0 -9
  158. package/dist/tui/widgets/liquidity.js +0 -142
  159. package/dist/tui/widgets/orders.d.ts +0 -4
  160. package/dist/tui/widgets/orders.js +0 -37
  161. package/dist/tui/widgets/portfolio.d.ts +0 -4
  162. package/dist/tui/widgets/portfolio.js +0 -59
  163. package/dist/tui/widgets/signals.d.ts +0 -4
  164. package/dist/tui/widgets/signals.js +0 -31
  165. package/dist/tui/widgets/statusbar.d.ts +0 -8
  166. package/dist/tui/widgets/statusbar.js +0 -72
  167. package/dist/tui/widgets/thesis.d.ts +0 -4
  168. package/dist/tui/widgets/thesis.js +0 -66
  169. package/dist/tui/widgets/trade.d.ts +0 -9
  170. package/dist/tui/widgets/trade.js +0 -117
  171. package/dist/tui/widgets/upcoming.d.ts +0 -4
  172. package/dist/tui/widgets/upcoming.js +0 -41
  173. package/dist/tui/widgets/whatif.d.ts +0 -7
  174. package/dist/tui/widgets/whatif.js +0 -113
  175. package/dist/types/output.d.ts +0 -412
  176. package/dist/types/output.js +0 -9
  177. package/dist/utils.d.ts +0 -42
  178. package/dist/utils.js +0 -124
  179. package/dist/utils.test.d.ts +0 -1
  180. package/dist/utils.test.js +0 -111
package/dist/kalshi.js DELETED
@@ -1,498 +0,0 @@
1
- "use strict";
2
- /**
3
- * Kalshi Authenticated Client (CLI-side only)
4
- *
5
- * Uses the kalshi-typescript SDK with RSA-PSS private key auth.
6
- * Credentials NEVER leave the user's machine — they are read from env vars
7
- * pointing to a local PEM file.
8
- *
9
- * Required env vars:
10
- * KALSHI_API_KEY_ID — Key ID from Kalshi dashboard
11
- * KALSHI_PRIVATE_KEY_PATH — Path to RSA private key PEM file (e.g. ~/.kalshi/private.pem)
12
- */
13
- var __importDefault = (this && this.__importDefault) || function (mod) {
14
- return (mod && mod.__esModule) ? mod : { "default": mod };
15
- };
16
- Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.isKalshiConfigured = isKalshiConfigured;
18
- exports.getPositions = getPositions;
19
- exports.kalshiPriceCents = kalshiPriceCents;
20
- exports.getMarketPrice = getMarketPrice;
21
- exports.getOrderbook = getOrderbook;
22
- exports.getPublicOrderbook = getPublicOrderbook;
23
- exports.getSettlements = getSettlements;
24
- exports.getBalance = getBalance;
25
- exports.getOrders = getOrders;
26
- exports.getFills = getFills;
27
- exports.getForecastHistory = getForecastHistory;
28
- exports.getExchangeAnnouncements = getExchangeAnnouncements;
29
- exports.getHistoricalMarket = getHistoricalMarket;
30
- exports.createOrder = createOrder;
31
- exports.cancelOrder = cancelOrder;
32
- exports.batchCancelOrders = batchCancelOrders;
33
- exports.amendOrder = amendOrder;
34
- exports.getBatchCandlesticks = getBatchCandlesticks;
35
- exports.createRFQ = createRFQ;
36
- const fs_1 = __importDefault(require("fs"));
37
- const path_1 = __importDefault(require("path"));
38
- const crypto_1 = __importDefault(require("crypto"));
39
- const KALSHI_API_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
40
- // ============================================================================
41
- // AUTH
42
- // ============================================================================
43
- /**
44
- * Check if Kalshi credentials are configured locally.
45
- */
46
- function isKalshiConfigured() {
47
- return !!(process.env.KALSHI_API_KEY_ID && process.env.KALSHI_PRIVATE_KEY_PATH);
48
- }
49
- /**
50
- * Load the RSA private key from disk.
51
- */
52
- function loadPrivateKey() {
53
- const keyPath = process.env.KALSHI_PRIVATE_KEY_PATH;
54
- if (!keyPath)
55
- return null;
56
- const resolved = keyPath.startsWith('~')
57
- ? path_1.default.join(process.env.HOME || '', keyPath.slice(1))
58
- : path_1.default.resolve(keyPath);
59
- try {
60
- const pem = fs_1.default.readFileSync(resolved, 'utf-8');
61
- return crypto_1.default.createPrivateKey(pem);
62
- }
63
- catch (err) {
64
- console.warn(`[Kalshi] Failed to load private key from ${resolved}:`, err);
65
- return null;
66
- }
67
- }
68
- /**
69
- * Sign a request using RSA-PSS (Kalshi's auth scheme).
70
- *
71
- * Kalshi expects:
72
- * Header: KALSHI-ACCESS-KEY = API key ID
73
- * Header: KALSHI-ACCESS-SIGNATURE = base64(RSA-PSS-sign(timestamp_ms + method + path))
74
- * Header: KALSHI-ACCESS-TIMESTAMP = milliseconds since epoch (string)
75
- */
76
- function signRequest(method, urlPath, privateKey) {
77
- const keyId = process.env.KALSHI_API_KEY_ID;
78
- const timestampMs = Date.now().toString();
79
- // Kalshi signing payload: timestamp_ms + method + path (no body)
80
- const payload = timestampMs + method.toUpperCase() + urlPath;
81
- const signature = crypto_1.default.sign('sha256', Buffer.from(payload), {
82
- key: privateKey,
83
- padding: crypto_1.default.constants.RSA_PKCS1_PSS_PADDING,
84
- saltLength: crypto_1.default.constants.RSA_PSS_SALTLEN_DIGEST,
85
- });
86
- return {
87
- headers: {
88
- 'KALSHI-ACCESS-KEY': keyId,
89
- 'KALSHI-ACCESS-SIGNATURE': signature.toString('base64'),
90
- 'KALSHI-ACCESS-TIMESTAMP': timestampMs,
91
- 'Content-Type': 'application/json',
92
- 'Accept': 'application/json',
93
- },
94
- };
95
- }
96
- /**
97
- * Make an authenticated Kalshi API request.
98
- */
99
- async function kalshiAuthGet(apiPath) {
100
- const privateKey = loadPrivateKey();
101
- if (!privateKey) {
102
- throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
103
- }
104
- const url = `${KALSHI_API_BASE}${apiPath}`;
105
- // Kalshi signing MUST exclude query params — sign only the path portion
106
- const pathOnly = apiPath.split('?')[0];
107
- const { headers } = signRequest('GET', `/trade-api/v2${pathOnly}`, privateKey);
108
- const res = await fetch(url, { method: 'GET', headers });
109
- if (!res.ok) {
110
- const text = await res.text();
111
- throw new Error(`Kalshi API ${res.status}: ${text}`);
112
- }
113
- return res.json();
114
- }
115
- async function kalshiAuthPost(apiPath, body) {
116
- const privateKey = loadPrivateKey();
117
- if (!privateKey) {
118
- throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
119
- }
120
- const url = `${KALSHI_API_BASE}${apiPath}`;
121
- const pathOnly = apiPath.split('?')[0];
122
- const { headers } = signRequest('POST', `/trade-api/v2${pathOnly}`, privateKey);
123
- const res = await fetch(url, {
124
- method: 'POST',
125
- headers,
126
- body: JSON.stringify(body),
127
- });
128
- if (!res.ok) {
129
- const text = await res.text();
130
- throw new Error(`Kalshi API ${res.status}: ${text}`);
131
- }
132
- return res.json();
133
- }
134
- async function kalshiAuthDelete(apiPath) {
135
- const privateKey = loadPrivateKey();
136
- if (!privateKey) {
137
- throw new Error('Kalshi private key not loaded. Check KALSHI_PRIVATE_KEY_PATH.');
138
- }
139
- const url = `${KALSHI_API_BASE}${apiPath}`;
140
- const pathOnly = apiPath.split('?')[0];
141
- const { headers } = signRequest('DELETE', `/trade-api/v2${pathOnly}`, privateKey);
142
- const res = await fetch(url, { method: 'DELETE', headers });
143
- if (!res.ok) {
144
- const text = await res.text();
145
- throw new Error(`Kalshi API ${res.status}: ${text}`);
146
- }
147
- const contentType = res.headers.get('content-type');
148
- if (contentType?.includes('application/json')) {
149
- return res.json();
150
- }
151
- return {};
152
- }
153
- /**
154
- * Get user's live positions from Kalshi.
155
- * Returns null if Kalshi is not configured.
156
- */
157
- async function getPositions() {
158
- if (!isKalshiConfigured())
159
- return null;
160
- try {
161
- const data = await kalshiAuthGet('/portfolio/positions');
162
- // Kalshi returns { market_positions: [...] } or { positions: [...] }
163
- const raw = data.market_positions || data.positions || [];
164
- return raw.map((p) => {
165
- // Kalshi actual fields:
166
- // position_fp: "795.00" (string, contract count — positive=YES, negative=NO)
167
- // total_traded_dollars: "453.1500" (string, total cost in dollars)
168
- // ticker: "KXWTIMAX-26DEC31-T135"
169
- // event_ticker: "KXWTIMAX-26DEC31"
170
- // market_result: "", resting_orders_count: 0, etc.
171
- const positionFp = parseFloat(p.position_fp || '0');
172
- const totalTradedDollars = parseFloat(p.total_traded_dollars || '0');
173
- const quantity = Math.abs(positionFp);
174
- const side = positionFp >= 0 ? 'yes' : 'no';
175
- // avg price in cents = (total_traded_dollars / quantity) * 100
176
- const avgPriceCents = quantity > 0 ? Math.round((totalTradedDollars / quantity) * 100) : 0;
177
- return {
178
- ticker: p.ticker || p.market_ticker || '',
179
- event_ticker: p.event_ticker || '',
180
- market_title: p.market_title || p.title || '',
181
- side,
182
- quantity,
183
- average_price_paid: avgPriceCents,
184
- current_value: 0, // will be enriched by live price lookup if needed
185
- realized_pnl: Math.round(parseFloat(p.realized_pnl || '0') * 100),
186
- unrealized_pnl: 0, // Kalshi doesn't give this directly, needs live price
187
- total_cost: Math.round(totalTradedDollars * 100), // dollars → cents
188
- };
189
- });
190
- }
191
- catch (err) {
192
- console.warn(`[Kalshi] Failed to fetch positions:`, err);
193
- return null;
194
- }
195
- }
196
- /**
197
- * Extract price in cents (0-100) from a Kalshi market object.
198
- * Dollars fields first (Kalshi API returns null for integer cents fields).
199
- */
200
- function kalshiPriceCents(market) {
201
- if (market.last_price_dollars) {
202
- const p = parseFloat(market.last_price_dollars);
203
- if (!isNaN(p) && p > 0)
204
- return Math.round(p * 100);
205
- }
206
- if (market.yes_bid_dollars) {
207
- const p = parseFloat(market.yes_bid_dollars);
208
- if (!isNaN(p) && p > 0)
209
- return Math.round(p * 100);
210
- }
211
- if (market.yes_ask_dollars) {
212
- const p = parseFloat(market.yes_ask_dollars);
213
- if (!isNaN(p) && p > 0)
214
- return Math.round(p * 100);
215
- }
216
- if (market.last_price != null && market.last_price > 0)
217
- return market.last_price;
218
- if (market.yes_bid != null && market.yes_bid > 0)
219
- return market.yes_bid;
220
- return 50;
221
- }
222
- /**
223
- * Get the current market price for a given ticker (public, no auth).
224
- * Returns price in cents (0-100) or null.
225
- */
226
- async function getMarketPrice(ticker) {
227
- try {
228
- const url = `https://api.elections.kalshi.com/trade-api/v2/markets/${ticker}`;
229
- const res = await fetch(url, { headers: { 'Accept': 'application/json' } });
230
- if (!res.ok)
231
- return null;
232
- const data = await res.json();
233
- const m = data.market || data;
234
- const price = kalshiPriceCents(m);
235
- return price === 50 && !m.last_price_dollars && !m.yes_bid_dollars ? null : price;
236
- }
237
- catch {
238
- return null;
239
- }
240
- }
241
- async function getOrderbook(ticker) {
242
- if (!isKalshiConfigured())
243
- return null;
244
- try {
245
- const data = await kalshiAuthGet(`/markets/${ticker}/orderbook`);
246
- const ob = data.orderbook_fp || data.orderbook;
247
- if (!ob)
248
- return null;
249
- // Kalshi orderbook_fp uses yes_dollars/no_dollars: [["0.57", "100"], ...]
250
- // Fallback to yes/no (cents format)
251
- const rawYes = ob.yes_dollars || ob.yes || [];
252
- const rawNo = ob.no_dollars || ob.no || [];
253
- const isDollar = !!(ob.yes_dollars || ob.no_dollars);
254
- // Parse to cents
255
- const parsedYes = rawYes.map(l => ({
256
- price: isDollar ? Math.round(parseFloat(l[0]) * 100) : Number(l[0]),
257
- qty: parseFloat(l[1]),
258
- })).filter(l => l.price > 0);
259
- const parsedNo = rawNo.map(l => ({
260
- price: isDollar ? Math.round(parseFloat(l[0]) * 100) : Number(l[0]),
261
- qty: parseFloat(l[1]),
262
- })).filter(l => l.price > 0);
263
- // Sort descending by price
264
- parsedYes.sort((a, b) => b.price - a.price);
265
- parsedNo.sort((a, b) => b.price - a.price);
266
- const bestBid = parsedYes.length > 0 ? parsedYes[0].price : 0;
267
- const bestAsk = parsedNo.length > 0 ? (100 - parsedNo[0].price) : 100;
268
- const spread = bestAsk - bestBid;
269
- const bidDepth = parsedYes.slice(0, 3).reduce((sum, l) => sum + l.qty, 0);
270
- const askDepth = parsedNo.slice(0, 3).reduce((sum, l) => sum + l.qty, 0);
271
- const minDepth = Math.min(bidDepth, askDepth);
272
- let liquidityScore = 'low';
273
- if (spread <= 2 && minDepth >= 500)
274
- liquidityScore = 'high';
275
- else if (spread <= 5 && minDepth >= 100)
276
- liquidityScore = 'medium';
277
- return { bestBid, bestAsk, spread, bidDepth, askDepth, liquidityScore };
278
- }
279
- catch {
280
- return null;
281
- }
282
- }
283
- /**
284
- * Fetch orderbook for a ticker using the public (unauthenticated) endpoint.
285
- * Returns raw yes_dollars/no_dollars arrays or null on failure.
286
- */
287
- async function getPublicOrderbook(ticker, depth = 20) {
288
- try {
289
- const url = `${KALSHI_API_BASE}/markets/${ticker}/orderbook?depth=${depth}`;
290
- const res = await fetch(url, { headers: { 'Accept': 'application/json' } });
291
- if (!res.ok)
292
- return null;
293
- const data = await res.json();
294
- const ob = data.orderbook_fp || data.orderbook || data;
295
- return {
296
- yes_dollars: ob.yes_dollars || ob.yes || [],
297
- no_dollars: ob.no_dollars || ob.no || [],
298
- };
299
- }
300
- catch {
301
- return null;
302
- }
303
- }
304
- // ============================================================================
305
- // SETTLEMENTS (Authenticated)
306
- // ============================================================================
307
- async function getSettlements(params) {
308
- if (!isKalshiConfigured())
309
- return null;
310
- try {
311
- const searchParams = new URLSearchParams();
312
- if (params?.limit)
313
- searchParams.set('limit', params.limit.toString());
314
- if (params?.cursor)
315
- searchParams.set('cursor', params.cursor);
316
- if (params?.ticker)
317
- searchParams.set('ticker', params.ticker);
318
- const data = await kalshiAuthGet(`/portfolio/settlements?${searchParams.toString()}`);
319
- return { settlements: data.settlements || [], cursor: data.cursor || '' };
320
- }
321
- catch (err) {
322
- console.warn('[Kalshi] Failed to fetch settlements:', err);
323
- return null;
324
- }
325
- }
326
- // ============================================================================
327
- // BALANCE (Authenticated)
328
- // ============================================================================
329
- async function getBalance() {
330
- if (!isKalshiConfigured())
331
- return null;
332
- try {
333
- const data = await kalshiAuthGet('/portfolio/balance');
334
- // API returns cents integers; convert to dollars
335
- const balance = (data.balance || 0) / 100;
336
- const portfolioValue = (data.portfolio_value || 0) / 100;
337
- return { balance, portfolioValue };
338
- }
339
- catch (err) {
340
- console.warn('[Kalshi] Failed to fetch balance:', err);
341
- return null;
342
- }
343
- }
344
- // ============================================================================
345
- // ORDERS (Authenticated)
346
- // ============================================================================
347
- async function getOrders(params) {
348
- if (!isKalshiConfigured())
349
- return null;
350
- try {
351
- const searchParams = new URLSearchParams();
352
- if (params?.status)
353
- searchParams.set('status', params.status);
354
- if (params?.ticker)
355
- searchParams.set('ticker', params.ticker);
356
- if (params?.limit)
357
- searchParams.set('limit', params.limit.toString());
358
- if (params?.cursor)
359
- searchParams.set('cursor', params.cursor);
360
- const data = await kalshiAuthGet(`/portfolio/orders?${searchParams.toString()}`);
361
- return { orders: data.orders || [], cursor: data.cursor || '' };
362
- }
363
- catch (err) {
364
- console.warn('[Kalshi] Failed to fetch orders:', err);
365
- return null;
366
- }
367
- }
368
- // ============================================================================
369
- // FILLS (Authenticated)
370
- // ============================================================================
371
- async function getFills(params) {
372
- if (!isKalshiConfigured())
373
- return null;
374
- try {
375
- const searchParams = new URLSearchParams();
376
- if (params?.ticker)
377
- searchParams.set('ticker', params.ticker);
378
- if (params?.limit)
379
- searchParams.set('limit', params.limit.toString());
380
- if (params?.cursor)
381
- searchParams.set('cursor', params.cursor);
382
- const data = await kalshiAuthGet(`/portfolio/fills?${searchParams.toString()}`);
383
- return { fills: data.fills || [], cursor: data.cursor || '' };
384
- }
385
- catch (err) {
386
- console.warn('[Kalshi] Failed to fetch fills:', err);
387
- return null;
388
- }
389
- }
390
- // ============================================================================
391
- // FORECAST PERCENTILE HISTORY (Authenticated)
392
- // ============================================================================
393
- async function getForecastHistory(params) {
394
- if (!isKalshiConfigured())
395
- return null;
396
- try {
397
- const searchParams = new URLSearchParams();
398
- for (const p of params.percentiles)
399
- searchParams.append('percentiles', p.toString());
400
- searchParams.set('start_ts', params.startTs.toString());
401
- // Kalshi returns 400 if end_ts is past the latest available forecast data.
402
- // Clamp to start of today UTC to avoid hitting future timestamps.
403
- const todayMidnight = Math.floor(new Date().setUTCHours(0, 0, 0, 0) / 1000);
404
- const clampedEnd = Math.min(params.endTs, todayMidnight);
405
- searchParams.set('end_ts', clampedEnd.toString());
406
- searchParams.set('period_interval', params.periodInterval.toString());
407
- const apiPath = `/series/${params.seriesTicker}/events/${params.eventTicker}/forecast_percentile_history?${searchParams.toString()}`;
408
- const data = await kalshiAuthGet(apiPath);
409
- return data.forecast_history || [];
410
- }
411
- catch (err) {
412
- console.warn('[Kalshi] Failed to fetch forecast:', err);
413
- return null;
414
- }
415
- }
416
- // ============================================================================
417
- // EXCHANGE (Public, no auth)
418
- // ============================================================================
419
- async function getExchangeAnnouncements() {
420
- try {
421
- const res = await fetch(`${KALSHI_API_BASE}/exchange/announcements`, { headers: { 'Accept': 'application/json' } });
422
- if (!res.ok)
423
- return [];
424
- const data = await res.json();
425
- return data.announcements || [];
426
- }
427
- catch {
428
- return [];
429
- }
430
- }
431
- async function getHistoricalMarket(ticker) {
432
- try {
433
- const res = await fetch(`${KALSHI_API_BASE}/historical/markets/${ticker}`, { headers: { 'Accept': 'application/json' } });
434
- if (!res.ok)
435
- return null;
436
- const data = await res.json();
437
- return data.market || data || null;
438
- }
439
- catch {
440
- return null;
441
- }
442
- }
443
- // ============================================================================
444
- // TRADING — ORDER MANAGEMENT (Authenticated, requires write key)
445
- // ============================================================================
446
- async function createOrder(params) {
447
- return kalshiAuthPost('/portfolio/orders', params);
448
- }
449
- async function cancelOrder(orderId) {
450
- await kalshiAuthDelete(`/portfolio/orders/${orderId}`);
451
- }
452
- async function batchCancelOrders(orderIds) {
453
- await kalshiAuthPost('/portfolio/orders/batched', {
454
- orders: orderIds.map(id => ({ action: 'cancel', order_id: id })),
455
- });
456
- }
457
- async function amendOrder(orderId, params) {
458
- // PATCH - need to implement kalshiAuthPatch or use POST
459
- const privateKey = loadPrivateKey();
460
- if (!privateKey)
461
- throw new Error('Kalshi private key not loaded.');
462
- const apiPath = `/portfolio/orders/${orderId}`;
463
- const url = `${KALSHI_API_BASE}${apiPath}`;
464
- const { headers } = signRequest('PATCH', `/trade-api/v2${apiPath}`, privateKey);
465
- const res = await fetch(url, {
466
- method: 'PATCH',
467
- headers,
468
- body: JSON.stringify(params),
469
- });
470
- if (!res.ok) {
471
- const text = await res.text();
472
- throw new Error(`Kalshi API ${res.status}: ${text}`);
473
- }
474
- return res.json();
475
- }
476
- // ============================================================================
477
- // CANDLESTICKS (Authenticated)
478
- // ============================================================================
479
- async function getBatchCandlesticks(params) {
480
- if (!isKalshiConfigured())
481
- return [];
482
- try {
483
- const searchParams = new URLSearchParams();
484
- searchParams.set('market_tickers', params.tickers.join(','));
485
- searchParams.set('start_ts', params.startTs.toString());
486
- searchParams.set('end_ts', params.endTs.toString());
487
- searchParams.set('period_interval', (params.periodInterval ?? 1440).toString());
488
- const data = await kalshiAuthGet(`/markets/candlesticks?${searchParams.toString()}`);
489
- return data.markets || [];
490
- }
491
- catch (err) {
492
- console.warn('[Kalshi] Failed to fetch candlesticks:', err);
493
- return [];
494
- }
495
- }
496
- async function createRFQ(params) {
497
- return kalshiAuthPost('/communications/rfqs', params);
498
- }