@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.
Files changed (46) hide show
  1. package/dist/client.d.ts +5 -0
  2. package/dist/client.js +17 -0
  3. package/dist/commands/agent.d.ts +1 -0
  4. package/dist/commands/agent.js +1170 -76
  5. package/dist/commands/announcements.d.ts +3 -0
  6. package/dist/commands/announcements.js +28 -0
  7. package/dist/commands/balance.d.ts +3 -0
  8. package/dist/commands/balance.js +17 -0
  9. package/dist/commands/cancel.d.ts +5 -0
  10. package/dist/commands/cancel.js +41 -0
  11. package/dist/commands/dashboard.d.ts +11 -0
  12. package/dist/commands/dashboard.js +195 -0
  13. package/dist/commands/feed.d.ts +13 -0
  14. package/dist/commands/feed.js +73 -0
  15. package/dist/commands/fills.d.ts +4 -0
  16. package/dist/commands/fills.js +29 -0
  17. package/dist/commands/forecast.d.ts +4 -0
  18. package/dist/commands/forecast.js +53 -0
  19. package/dist/commands/history.d.ts +3 -0
  20. package/dist/commands/history.js +38 -0
  21. package/dist/commands/milestones.d.ts +8 -0
  22. package/dist/commands/milestones.js +56 -0
  23. package/dist/commands/orders.d.ts +4 -0
  24. package/dist/commands/orders.js +28 -0
  25. package/dist/commands/publish.js +21 -2
  26. package/dist/commands/rfq.d.ts +5 -0
  27. package/dist/commands/rfq.js +35 -0
  28. package/dist/commands/schedule.d.ts +3 -0
  29. package/dist/commands/schedule.js +38 -0
  30. package/dist/commands/settlements.d.ts +6 -0
  31. package/dist/commands/settlements.js +50 -0
  32. package/dist/commands/setup.d.ts +2 -0
  33. package/dist/commands/setup.js +45 -3
  34. package/dist/commands/signal.js +12 -1
  35. package/dist/commands/strategies.d.ts +11 -0
  36. package/dist/commands/strategies.js +130 -0
  37. package/dist/commands/trade.d.ts +12 -0
  38. package/dist/commands/trade.js +78 -0
  39. package/dist/commands/whatif.d.ts +17 -0
  40. package/dist/commands/whatif.js +209 -0
  41. package/dist/config.d.ts +2 -0
  42. package/dist/config.js +13 -0
  43. package/dist/index.js +177 -3
  44. package/dist/kalshi.d.ts +71 -0
  45. package/dist/kalshi.js +257 -17
  46. package/package.json +1 -1
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ /**
3
+ * sf whatif — What-if scenario analysis.
4
+ *
5
+ * Pure computation, zero LLM cost. Answers:
6
+ * "If node X drops to 10%, what happens to my edges and positions?"
7
+ *
8
+ * Usage:
9
+ * sf whatif f582bf76 --set "n1=0.1"
10
+ * sf whatif f582bf76 --set "n1=0.1" --set "n3.1=0.2"
11
+ * sf whatif f582bf76 --set "n1=0.1" --json
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.whatifCommand = whatifCommand;
15
+ const client_js_1 = require("../client.js");
16
+ const kalshi_js_1 = require("../kalshi.js");
17
+ const utils_js_1 = require("../utils.js");
18
+ // Inline what-if simulation (mirrors server-side logic, zero dependency)
19
+ function simulateWhatIf(ctx, overrides) {
20
+ const allNodes = [];
21
+ function flatten(nodes) {
22
+ for (const n of nodes) {
23
+ allNodes.push(n);
24
+ if (n.children?.length)
25
+ flatten(n.children);
26
+ }
27
+ }
28
+ const rawNodes = ctx.causalTree?.nodes || [];
29
+ flatten(rawNodes);
30
+ // Top-level nodes only (depth=0 or no depth field + no dot in id)
31
+ const treeNodes = rawNodes.filter((n) => n.depth === 0 || (n.depth === undefined && !n.id.includes('.')));
32
+ const overrideMap = new Map(overrides.map(o => [o.nodeId, o.newProbability]));
33
+ const overrideDetails = overrides.map(o => {
34
+ const node = allNodes.find((n) => n.id === o.nodeId);
35
+ return {
36
+ nodeId: o.nodeId,
37
+ oldProb: node?.probability ?? 0,
38
+ newProb: o.newProbability,
39
+ label: node?.label || o.nodeId,
40
+ };
41
+ });
42
+ // Confidence (only top-level nodes)
43
+ const oldConf = treeNodes.reduce((s, n) => s + (n.probability || 0) * (n.importance || 0), 0);
44
+ const newConf = treeNodes.reduce((s, n) => {
45
+ const p = overrideMap.get(n.id) ?? n.probability ?? 0;
46
+ return s + p * (n.importance || 0);
47
+ }, 0);
48
+ // Per-node override ratios — only scale edges directly related to overridden nodes.
49
+ // No global scale: edges unrelated to any override stay unchanged.
50
+ // User must explicitly override each node they think is affected.
51
+ const nodeScales = new Map();
52
+ for (const [nodeId, newProb] of overrideMap.entries()) {
53
+ const node = allNodes.find((n) => n.id === nodeId);
54
+ if (node && node.probability > 0) {
55
+ nodeScales.set(nodeId, Math.max(0, Math.min(2, newProb / node.probability)));
56
+ }
57
+ }
58
+ // Edges
59
+ const edges = (ctx.edges || []).map((edge) => {
60
+ const relNode = edge.relatedNodeId;
61
+ let scaleFactor = 1; // default: no change
62
+ // Only scale if edge's related node (or its ancestor) was overridden
63
+ if (relNode) {
64
+ const candidates = [relNode, relNode.split('.').slice(0, -1).join('.'), relNode.split('.')[0]].filter(Boolean);
65
+ for (const cid of candidates) {
66
+ if (nodeScales.has(cid)) {
67
+ scaleFactor = nodeScales.get(cid);
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ const mkt = edge.marketPrice || 0;
73
+ const oldTP = edge.thesisPrice || edge.thesisImpliedPrice || mkt;
74
+ const oldEdge = edge.edge || edge.edgeSize || 0;
75
+ const premium = oldTP - mkt;
76
+ const newTP = Math.round((mkt + premium * scaleFactor) * 100) / 100;
77
+ const dir = edge.direction || 'yes';
78
+ const newEdge = Math.round((dir === 'yes' ? newTP - mkt : mkt - newTP) * 100) / 100;
79
+ const delta = Math.round((newEdge - oldEdge) * 100) / 100;
80
+ let signal = 'unchanged';
81
+ if (Math.abs(delta) < 1)
82
+ signal = 'unchanged';
83
+ else if ((oldEdge > 0 && newEdge < 0) || (oldEdge < 0 && newEdge > 0))
84
+ signal = 'reversed';
85
+ else if (Math.abs(newEdge) < 2)
86
+ signal = 'gone';
87
+ else if (Math.abs(newEdge) < Math.abs(oldEdge))
88
+ signal = 'reduced';
89
+ return {
90
+ marketId: edge.marketId,
91
+ market: edge.market || edge.marketTitle || edge.marketId,
92
+ venue: edge.venue,
93
+ direction: dir,
94
+ marketPrice: mkt,
95
+ oldThesisPrice: oldTP,
96
+ newThesisPrice: newTP,
97
+ oldEdge,
98
+ newEdge,
99
+ delta,
100
+ signal,
101
+ relatedNodeId: relNode,
102
+ };
103
+ });
104
+ edges.sort((a, b) => Math.abs(b.delta) - Math.abs(a.delta));
105
+ return {
106
+ overrides: overrideDetails,
107
+ oldConfidence: oldConf,
108
+ newConfidence: newConf,
109
+ confidenceDelta: Math.round((newConf - oldConf) * 100) / 100,
110
+ edges,
111
+ };
112
+ }
113
+ async function whatifCommand(thesisId, opts) {
114
+ if (!opts.set || opts.set.length === 0) {
115
+ throw new Error('Usage: sf whatif <thesisId> --set "n1=0.1" [--set "n3=0.5"]');
116
+ }
117
+ // Parse overrides
118
+ const overrides = opts.set.map(s => {
119
+ const [nodeId, valStr] = s.split('=');
120
+ if (!nodeId || !valStr)
121
+ throw new Error(`Invalid override: "${s}". Format: nodeId=probability`);
122
+ const prob = parseFloat(valStr);
123
+ if (isNaN(prob) || prob < 0 || prob > 1)
124
+ throw new Error(`Invalid probability: "${valStr}". Must be 0-1.`);
125
+ return { nodeId: nodeId.trim(), newProbability: prob };
126
+ });
127
+ const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
128
+ const ctx = await client.getContext(thesisId);
129
+ const result = simulateWhatIf(ctx, overrides);
130
+ if (opts.json) {
131
+ console.log(JSON.stringify(result, null, 2));
132
+ return;
133
+ }
134
+ // Render
135
+ console.log();
136
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}WHAT-IF Scenario${utils_js_1.c.reset}`);
137
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(65)}${utils_js_1.c.reset}`);
138
+ // Overrides
139
+ for (const o of result.overrides) {
140
+ const oldPct = Math.round(o.oldProb * 100);
141
+ const newPct = Math.round(o.newProb * 100);
142
+ const arrow = newPct > oldPct ? utils_js_1.c.green + '↑' + utils_js_1.c.reset : utils_js_1.c.red + '↓' + utils_js_1.c.reset;
143
+ console.log(` ${utils_js_1.c.cyan}${o.nodeId}${utils_js_1.c.reset} ${o.label.slice(0, 40)}`);
144
+ console.log(` ${oldPct}% ${arrow} ${utils_js_1.c.bold}${newPct}%${utils_js_1.c.reset}`);
145
+ }
146
+ // Confidence
147
+ const oldPct = Math.round(result.oldConfidence * 100);
148
+ const newPct = Math.round(result.newConfidence * 100);
149
+ const deltaSign = result.confidenceDelta > 0 ? '+' : '';
150
+ const confColor = result.confidenceDelta >= 0 ? utils_js_1.c.green : utils_js_1.c.red;
151
+ console.log();
152
+ console.log(` Confidence: ${oldPct}% → ${confColor}${utils_js_1.c.bold}${newPct}%${utils_js_1.c.reset} (${confColor}${deltaSign}${Math.round(result.confidenceDelta * 100)}${utils_js_1.c.reset})`);
153
+ console.log();
154
+ // Edges
155
+ const affected = result.edges.filter((e) => e.signal !== 'unchanged');
156
+ if (affected.length === 0) {
157
+ console.log(` ${utils_js_1.c.dim}No edges affected.${utils_js_1.c.reset}`);
158
+ }
159
+ else {
160
+ console.log(` ${utils_js_1.c.bold}Edges Affected${utils_js_1.c.reset}`);
161
+ console.log(` ${'Market'.padEnd(35)} ${'Now'.padEnd(6)} ${'Edge'.padEnd(8)} ${'→'.padEnd(3)} ${'New Edge'.padEnd(8)} Signal`);
162
+ console.log(` ${utils_js_1.c.dim}${'─'.repeat(65)}${utils_js_1.c.reset}`);
163
+ for (const e of affected) {
164
+ const name = (e.market || e.marketId).slice(0, 33).padEnd(35);
165
+ const mkt = `${Math.round(e.marketPrice)}¢`.padEnd(6);
166
+ const oldE = `${e.oldEdge > 0 ? '+' : ''}${Math.round(e.oldEdge)}`.padEnd(8);
167
+ const newE = `${e.newEdge > 0 ? '+' : ''}${Math.round(e.newEdge)}`.padEnd(8);
168
+ let signalStr;
169
+ switch (e.signal) {
170
+ case 'reversed':
171
+ signalStr = `${utils_js_1.c.red}${utils_js_1.c.bold}REVERSED${utils_js_1.c.reset}`;
172
+ break;
173
+ case 'gone':
174
+ signalStr = `${utils_js_1.c.red}GONE${utils_js_1.c.reset}`;
175
+ break;
176
+ case 'reduced':
177
+ signalStr = `${utils_js_1.c.dim}reduced${utils_js_1.c.reset}`;
178
+ break;
179
+ default: signalStr = `${utils_js_1.c.dim}-${utils_js_1.c.reset}`;
180
+ }
181
+ console.log(` ${utils_js_1.c.dim}${name}${utils_js_1.c.reset} ${mkt} ${oldE} → ${newE} ${signalStr}`);
182
+ }
183
+ }
184
+ // Position risk (if positions available)
185
+ try {
186
+ const positions = await (0, kalshi_js_1.getPositions)();
187
+ if (positions && positions.length > 0) {
188
+ const edgeMap = new Map(result.edges.map((e) => [e.marketId, e]));
189
+ const atRisk = positions.filter((p) => {
190
+ const e = edgeMap.get(p.ticker);
191
+ return e && (e.signal === 'reversed' || e.signal === 'gone');
192
+ });
193
+ if (atRisk.length > 0) {
194
+ console.log();
195
+ console.log(` ${utils_js_1.c.red}${utils_js_1.c.bold}⚠ Positions at Risk${utils_js_1.c.reset}`);
196
+ for (const p of atRisk) {
197
+ const e = edgeMap.get(p.ticker);
198
+ const livePrice = await (0, kalshi_js_1.getMarketPrice)(p.ticker);
199
+ const currentPnl = livePrice !== null
200
+ ? ((livePrice - p.average_price_paid) * p.quantity / 100).toFixed(2)
201
+ : '?';
202
+ console.log(` ${utils_js_1.c.red}${p.ticker}${utils_js_1.c.reset} ${p.quantity} ${p.side} P&L $${currentPnl} edge ${e.oldEdge > 0 ? '+' : ''}${Math.round(e.oldEdge)} → ${utils_js_1.c.red}${e.newEdge > 0 ? '+' : ''}${Math.round(e.newEdge)}${utils_js_1.c.reset}`);
203
+ }
204
+ }
205
+ }
206
+ }
207
+ catch { /* positions not available, skip */ }
208
+ console.log();
209
+ }
package/dist/config.d.ts CHANGED
@@ -15,6 +15,7 @@ export interface SFConfig {
15
15
  kalshiPrivateKeyPath?: string;
16
16
  tavilyKey?: string;
17
17
  model?: string;
18
+ tradingEnabled?: boolean;
18
19
  configuredAt?: string;
19
20
  }
20
21
  /**
@@ -47,4 +48,5 @@ export declare function applyConfig(): void;
47
48
  * Check if SF API key is configured (from any source).
48
49
  */
49
50
  export declare function isConfigured(): boolean;
51
+ export declare function requireTrading(): void;
50
52
  export declare function getConfigPath(): string;
package/dist/config.js CHANGED
@@ -18,6 +18,7 @@ exports.saveConfig = saveConfig;
18
18
  exports.resetConfig = resetConfig;
19
19
  exports.applyConfig = applyConfig;
20
20
  exports.isConfigured = isConfigured;
21
+ exports.requireTrading = requireTrading;
21
22
  exports.getConfigPath = getConfigPath;
22
23
  const fs_1 = __importDefault(require("fs"));
23
24
  const path_1 = __importDefault(require("path"));
@@ -51,6 +52,7 @@ function loadConfig() {
51
52
  kalshiPrivateKeyPath: process.env.KALSHI_PRIVATE_KEY_PATH || file.kalshiPrivateKeyPath,
52
53
  tavilyKey: process.env.TAVILY_API_KEY || file.tavilyKey,
53
54
  model: process.env.SF_MODEL || file.model || DEFAULT_MODEL,
55
+ tradingEnabled: file.tradingEnabled || false,
54
56
  };
55
57
  }
56
58
  /**
@@ -112,6 +114,17 @@ function isConfigured() {
112
114
  const config = loadConfig();
113
115
  return !!config.apiKey;
114
116
  }
117
+ function requireTrading() {
118
+ const config = loadConfig();
119
+ if (!config.tradingEnabled) {
120
+ console.error('\n ⚠️ Trading is disabled. Run: sf setup --enable-trading\n');
121
+ process.exit(1);
122
+ }
123
+ if (!config.kalshiKeyId && !process.env.KALSHI_API_KEY_ID) {
124
+ console.error('\n ⚠️ Kalshi API key not configured. Run: sf setup\n');
125
+ process.exit(1);
126
+ }
127
+ }
115
128
  function getConfigPath() {
116
129
  return CONFIG_PATH;
117
130
  }
package/dist/index.js CHANGED
@@ -34,6 +34,22 @@ const agent_js_1 = require("./commands/agent.js");
34
34
  const setup_js_1 = require("./commands/setup.js");
35
35
  const publish_js_1 = require("./commands/publish.js");
36
36
  const explore_js_1 = require("./commands/explore.js");
37
+ const dashboard_js_1 = require("./commands/dashboard.js");
38
+ const strategies_js_1 = require("./commands/strategies.js");
39
+ const milestones_js_1 = require("./commands/milestones.js");
40
+ const forecast_js_1 = require("./commands/forecast.js");
41
+ const settlements_js_1 = require("./commands/settlements.js");
42
+ const balance_js_1 = require("./commands/balance.js");
43
+ const orders_js_1 = require("./commands/orders.js");
44
+ const fills_js_1 = require("./commands/fills.js");
45
+ const feed_js_1 = require("./commands/feed.js");
46
+ const whatif_js_1 = require("./commands/whatif.js");
47
+ const trade_js_1 = require("./commands/trade.js");
48
+ const cancel_js_1 = require("./commands/cancel.js");
49
+ const schedule_js_1 = require("./commands/schedule.js");
50
+ const rfq_js_1 = require("./commands/rfq.js");
51
+ const announcements_js_1 = require("./commands/announcements.js");
52
+ const history_js_1 = require("./commands/history.js");
37
53
  const utils_js_1 = require("./utils.js");
38
54
  // ── Apply ~/.sf/config.json to process.env BEFORE any command ────────────────
39
55
  // This means client.ts, kalshi.ts, agent.ts keep reading process.env and just work.
@@ -46,7 +62,7 @@ program
46
62
  .option('--api-key <key>', 'API key (or set SF_API_KEY env var)')
47
63
  .option('--api-url <url>', 'API base URL (or set SF_API_URL env var)');
48
64
  // ── Pre-action guard: check configuration ────────────────────────────────────
49
- const NO_CONFIG_COMMANDS = new Set(['setup', 'help', 'scan', 'explore']);
65
+ const NO_CONFIG_COMMANDS = new Set(['setup', 'help', 'scan', 'explore', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history']);
50
66
  program.hook('preAction', (thisCommand) => {
51
67
  const cmdName = thisCommand.name();
52
68
  if (NO_CONFIG_COMMANDS.has(cmdName))
@@ -69,8 +85,10 @@ program
69
85
  .option('--check', 'Show current configuration status')
70
86
  .option('--reset', 'Delete config and start over')
71
87
  .option('--key <key>', 'Set SF API key (non-interactive, for CI)')
88
+ .option('--enable-trading', 'Enable trading (sf buy/sell/cancel)')
89
+ .option('--disable-trading', 'Disable trading')
72
90
  .action(async (opts) => {
73
- await run(() => (0, setup_js_1.setupCommand)({ check: opts.check, reset: opts.reset, key: opts.key }));
91
+ await run(() => (0, setup_js_1.setupCommand)({ check: opts.check, reset: opts.reset, key: opts.key, enableTrading: opts.enableTrading, disableTrading: opts.disableTrading }));
74
92
  });
75
93
  // ── sf list ──────────────────────────────────────────────────────────────────
76
94
  program
@@ -183,9 +201,10 @@ program
183
201
  .option('--model <model>', 'Model via OpenRouter (default: anthropic/claude-sonnet-4.6)')
184
202
  .option('--model-key <key>', 'OpenRouter API key (or set OPENROUTER_API_KEY)')
185
203
  .option('--new', 'Start a fresh session (default: continue last session)')
204
+ .option('--plain', 'Plain text mode (no TUI, works in pipes and scripts)')
186
205
  .action(async (thesisId, opts, cmd) => {
187
206
  const g = cmd.optsWithGlobals();
188
- await run(() => (0, agent_js_1.agentCommand)(thesisId, { model: opts.model, modelKey: opts.modelKey, newSession: opts.new }));
207
+ await run(() => (0, agent_js_1.agentCommand)(thesisId, { model: opts.model, modelKey: opts.modelKey, newSession: opts.new, noTui: opts.plain }));
189
208
  });
190
209
  // ── sf publish <thesisId> ─────────────────────────────────────────────────────
191
210
  program
@@ -213,6 +232,161 @@ program
213
232
  .action(async (slug, opts) => {
214
233
  await run(() => (0, explore_js_1.exploreCommand)(slug, { json: opts.json }));
215
234
  });
235
+ // ── sf dashboard ──────────────────────────────────────────────────────────────
236
+ program
237
+ .command('dashboard')
238
+ .description('Portfolio overview — theses, positions, risk, unpositioned edges')
239
+ .option('--json', 'JSON output')
240
+ .action(async (opts, cmd) => {
241
+ const g = cmd.optsWithGlobals();
242
+ await run(() => (0, dashboard_js_1.dashboardCommand)({ json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
243
+ });
244
+ // ── sf milestones ────────────────────────────────────────────────────────────
245
+ program
246
+ .command('milestones')
247
+ .description('Upcoming events from Kalshi calendar')
248
+ .option('--category <cat>', 'Filter by category')
249
+ .option('--thesis <id>', 'Show milestones matching thesis edges')
250
+ .option('--hours <n>', 'Hours ahead (default 168)', '168')
251
+ .option('--json', 'JSON output')
252
+ .action(async (opts, cmd) => {
253
+ const g = cmd.optsWithGlobals();
254
+ await run(() => (0, milestones_js_1.milestonesCommand)({ ...opts, apiKey: g.apiKey, apiUrl: g.apiUrl }));
255
+ });
256
+ // ── sf forecast <eventTicker> ────────────────────────────────────────────────
257
+ program
258
+ .command('forecast <eventTicker>')
259
+ .description('Market distribution forecast (P50/P75/P90 history)')
260
+ .option('--days <n>', 'Days of history (default 7)', '7')
261
+ .option('--json', 'JSON output')
262
+ .action(async (eventTicker, opts) => {
263
+ await run(() => (0, forecast_js_1.forecastCommand)(eventTicker, opts));
264
+ });
265
+ // ── sf settlements ───────────────────────────────────────────────────────────
266
+ program
267
+ .command('settlements')
268
+ .description('Settled (resolved) contracts with P&L')
269
+ .option('--thesis <id>', 'Filter to thesis edge tickers')
270
+ .option('--json', 'JSON output')
271
+ .action(async (opts, cmd) => {
272
+ const g = cmd.optsWithGlobals();
273
+ await run(() => (0, settlements_js_1.settlementsCommand)({ ...opts, apiKey: g.apiKey, apiUrl: g.apiUrl }));
274
+ });
275
+ // ── sf balance ───────────────────────────────────────────────────────────────
276
+ program
277
+ .command('balance')
278
+ .description('Kalshi account balance')
279
+ .option('--json', 'JSON output')
280
+ .action(async (opts) => {
281
+ await run(() => (0, balance_js_1.balanceCommand)(opts));
282
+ });
283
+ // ── sf orders ────────────────────────────────────────────────────────────────
284
+ program
285
+ .command('orders')
286
+ .description('Kalshi resting orders')
287
+ .option('--status <status>', 'Order status filter (default: resting)', 'resting')
288
+ .option('--json', 'JSON output')
289
+ .action(async (opts) => {
290
+ await run(() => (0, orders_js_1.ordersCommand)(opts));
291
+ });
292
+ // ── sf fills ─────────────────────────────────────────────────────────────────
293
+ program
294
+ .command('fills')
295
+ .description('Recent trade fills')
296
+ .option('--ticker <ticker>', 'Filter by market ticker')
297
+ .option('--json', 'JSON output')
298
+ .action(async (opts) => {
299
+ await run(() => (0, fills_js_1.fillsCommand)(opts));
300
+ });
301
+ // ── sf feed ──────────────────────────────────────────────────────────────────
302
+ program
303
+ .command('feed')
304
+ .description('Evaluation history stream — what the heartbeat engine has been thinking')
305
+ .option('--hours <n>', 'Hours to look back (default 24)', '24')
306
+ .option('--thesis <id>', 'Filter by thesis')
307
+ .option('--json', 'JSON output')
308
+ .action(async (opts, cmd) => {
309
+ const g = cmd.optsWithGlobals();
310
+ await run(() => (0, feed_js_1.feedCommand)({ ...opts, apiKey: g.apiKey, apiUrl: g.apiUrl }));
311
+ });
312
+ // ── sf whatif <thesisId> ──────────────────────────────────────────────────────
313
+ program
314
+ .command('whatif <thesisId>')
315
+ .description('What-if scenario — "if node X drops to 10%, what happens to my edges?"')
316
+ .option('--set <override>', 'Node override: nodeId=probability (0-1). Repeatable.', (val, prev) => [...prev, val], [])
317
+ .option('--json', 'JSON output')
318
+ .action(async (thesisId, opts, cmd) => {
319
+ const g = cmd.optsWithGlobals();
320
+ await run(() => (0, whatif_js_1.whatifCommand)(thesisId, { set: opts.set, json: opts.json, apiKey: g.apiKey, apiUrl: g.apiUrl }));
321
+ });
322
+ // ── sf schedule ──────────────────────────────────────────────────────────────
323
+ program
324
+ .command('schedule')
325
+ .description('Exchange status and trading hours')
326
+ .option('--json', 'JSON output')
327
+ .action(async (opts) => {
328
+ await run(() => (0, schedule_js_1.scheduleCommand)(opts));
329
+ });
330
+ // ── sf buy <ticker> <qty> ───────────────────────────────────────────────────
331
+ program
332
+ .command('buy <ticker> <qty>')
333
+ .description('Buy contracts (requires --enable-trading)')
334
+ .option('--price <cents>', 'Limit price in cents (required for limit orders)')
335
+ .option('--market', 'Market order (no price needed)')
336
+ .option('--side <s>', 'yes or no', 'yes')
337
+ .option('--yes-i-am-sure', 'Skip 3-second countdown')
338
+ .action(async (ticker, qty, opts) => {
339
+ await run(() => (0, trade_js_1.buyCommand)(ticker, qty, opts));
340
+ });
341
+ // ── sf sell <ticker> <qty> ───────────────────────────────────────────────────
342
+ program
343
+ .command('sell <ticker> <qty>')
344
+ .description('Sell contracts (requires --enable-trading)')
345
+ .option('--price <cents>', 'Limit price in cents')
346
+ .option('--market', 'Market order')
347
+ .option('--side <s>', 'yes or no', 'yes')
348
+ .option('--yes-i-am-sure', 'Skip 3-second countdown')
349
+ .action(async (ticker, qty, opts) => {
350
+ await run(() => (0, trade_js_1.sellCommand)(ticker, qty, opts));
351
+ });
352
+ // ── sf cancel [orderId] ─────────────────────────────────────────────────────
353
+ program
354
+ .command('cancel [orderId]')
355
+ .description('Cancel order(s) (requires --enable-trading)')
356
+ .option('--all', 'Cancel all resting orders')
357
+ .option('--ticker <t>', 'Cancel orders matching ticker prefix (with --all)')
358
+ .option('--yes-i-am-sure', 'Skip countdown')
359
+ .action(async (orderId, opts) => {
360
+ await run(() => (0, cancel_js_1.cancelCommand)(orderId, opts));
361
+ });
362
+ // ── sf rfq <ticker> <qty> ───────────────────────────────────────────────────
363
+ program
364
+ .command('rfq <ticker> <qty>')
365
+ .description('Request for quote — large order pricing (requires --enable-trading)')
366
+ .option('--target-cost <cents>', 'Target cost per contract in cents')
367
+ .option('--rest-remainder', 'Rest unfilled portion as limit order')
368
+ .option('--json', 'JSON output')
369
+ .action(async (ticker, qty, opts) => {
370
+ await run(() => (0, rfq_js_1.rfqCommand)(ticker, qty, opts));
371
+ });
372
+ // ── sf announcements ─────────────────────────────────────────────────────────
373
+ program
374
+ .command('announcements')
375
+ .description('Exchange announcements (rule changes, maintenance)')
376
+ .option('--json', 'JSON output')
377
+ .action(async (opts) => {
378
+ await run(() => (0, announcements_js_1.announcementsCommand)(opts));
379
+ });
380
+ // ── sf history <ticker> ──────────────────────────────────────────────────────
381
+ program
382
+ .command('history <ticker>')
383
+ .description('Historical market data (settled/closed)')
384
+ .option('--json', 'JSON output')
385
+ .action(async (ticker, opts) => {
386
+ await run(() => (0, history_js_1.historyCommand)(ticker, opts));
387
+ });
388
+ // ── sf strategies ─────────────────────────────────────────────────────────────
389
+ (0, strategies_js_1.registerStrategies)(program);
216
390
  // ── Error wrapper ─────────────────────────────────────────────────────────────
217
391
  async function run(fn) {
218
392
  try {
package/dist/kalshi.d.ts CHANGED
@@ -35,6 +35,11 @@ export interface KalshiPortfolio {
35
35
  * Returns null if Kalshi is not configured.
36
36
  */
37
37
  export declare function getPositions(): Promise<KalshiPosition[] | null>;
38
+ /**
39
+ * Extract price in cents (0-100) from a Kalshi market object.
40
+ * Dollars fields first (Kalshi API returns null for integer cents fields).
41
+ */
42
+ export declare function kalshiPriceCents(market: any): number;
38
43
  /**
39
44
  * Get the current market price for a given ticker (public, no auth).
40
45
  * Returns price in cents (0-100) or null.
@@ -53,3 +58,69 @@ export interface LocalOrderbook {
53
58
  liquidityScore: 'high' | 'medium' | 'low';
54
59
  }
55
60
  export declare function getOrderbook(ticker: string): Promise<LocalOrderbook | null>;
61
+ export declare function getSettlements(params?: {
62
+ limit?: number;
63
+ cursor?: string;
64
+ ticker?: string;
65
+ }): Promise<{
66
+ settlements: any[];
67
+ cursor: string;
68
+ } | null>;
69
+ export declare function getBalance(): Promise<{
70
+ balance: number;
71
+ portfolioValue: number;
72
+ } | null>;
73
+ export declare function getOrders(params?: {
74
+ status?: string;
75
+ ticker?: string;
76
+ limit?: number;
77
+ cursor?: string;
78
+ }): Promise<{
79
+ orders: any[];
80
+ cursor: string;
81
+ } | null>;
82
+ export declare function getFills(params?: {
83
+ ticker?: string;
84
+ limit?: number;
85
+ cursor?: string;
86
+ }): Promise<{
87
+ fills: any[];
88
+ cursor: string;
89
+ } | null>;
90
+ export declare function getForecastHistory(params: {
91
+ seriesTicker: string;
92
+ eventTicker: string;
93
+ percentiles: number[];
94
+ startTs: number;
95
+ endTs: number;
96
+ periodInterval: number;
97
+ }): Promise<any[] | null>;
98
+ export declare function getExchangeAnnouncements(): Promise<any[]>;
99
+ export declare function getHistoricalMarket(ticker: string): Promise<any | null>;
100
+ export declare function createOrder(params: {
101
+ ticker: string;
102
+ side: 'yes' | 'no';
103
+ action: 'buy' | 'sell';
104
+ type: 'limit' | 'market';
105
+ count: number;
106
+ yes_price?: string;
107
+ client_order_id?: string;
108
+ }): Promise<{
109
+ order: any;
110
+ }>;
111
+ export declare function cancelOrder(orderId: string): Promise<void>;
112
+ export declare function batchCancelOrders(orderIds: string[]): Promise<void>;
113
+ export declare function amendOrder(orderId: string, params: {
114
+ price?: string;
115
+ count?: number;
116
+ }): Promise<{
117
+ order: any;
118
+ }>;
119
+ export declare function createRFQ(params: {
120
+ market_ticker: string;
121
+ contracts: number;
122
+ rest_remainder: boolean;
123
+ target_cost_centi_cents?: number;
124
+ }): Promise<{
125
+ id: string;
126
+ }>;