@spfunctions/cli 1.1.5 → 1.1.6

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 (42) hide show
  1. package/dist/client.d.ts +4 -0
  2. package/dist/client.js +14 -0
  3. package/dist/commands/agent.d.ts +1 -0
  4. package/dist/commands/agent.js +891 -14
  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/fills.d.ts +4 -0
  14. package/dist/commands/fills.js +29 -0
  15. package/dist/commands/forecast.d.ts +4 -0
  16. package/dist/commands/forecast.js +53 -0
  17. package/dist/commands/history.d.ts +3 -0
  18. package/dist/commands/history.js +38 -0
  19. package/dist/commands/milestones.d.ts +8 -0
  20. package/dist/commands/milestones.js +56 -0
  21. package/dist/commands/orders.d.ts +4 -0
  22. package/dist/commands/orders.js +28 -0
  23. package/dist/commands/publish.js +21 -2
  24. package/dist/commands/rfq.d.ts +5 -0
  25. package/dist/commands/rfq.js +35 -0
  26. package/dist/commands/schedule.d.ts +3 -0
  27. package/dist/commands/schedule.js +38 -0
  28. package/dist/commands/settlements.d.ts +6 -0
  29. package/dist/commands/settlements.js +50 -0
  30. package/dist/commands/setup.d.ts +2 -0
  31. package/dist/commands/setup.js +45 -3
  32. package/dist/commands/signal.js +12 -1
  33. package/dist/commands/strategies.d.ts +11 -0
  34. package/dist/commands/strategies.js +130 -0
  35. package/dist/commands/trade.d.ts +12 -0
  36. package/dist/commands/trade.js +78 -0
  37. package/dist/config.d.ts +2 -0
  38. package/dist/config.js +13 -0
  39. package/dist/index.js +154 -3
  40. package/dist/kalshi.d.ts +71 -0
  41. package/dist/kalshi.js +257 -17
  42. package/package.json +1 -1
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.scheduleCommand = scheduleCommand;
4
+ const utils_js_1 = require("../utils.js");
5
+ const KALSHI_API_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
6
+ async function scheduleCommand(opts) {
7
+ const statusRes = await fetch(`${KALSHI_API_BASE}/exchange/status`, {
8
+ headers: { 'Accept': 'application/json' },
9
+ });
10
+ if (!statusRes.ok)
11
+ throw new Error(`Exchange API ${statusRes.status}`);
12
+ const status = await statusRes.json();
13
+ let schedule = null;
14
+ try {
15
+ const schedRes = await fetch(`${KALSHI_API_BASE}/exchange/schedule`, {
16
+ headers: { 'Accept': 'application/json' },
17
+ });
18
+ if (schedRes.ok)
19
+ schedule = await schedRes.json();
20
+ }
21
+ catch { /* schedule endpoint may not exist */ }
22
+ if (opts.json) {
23
+ console.log(JSON.stringify({ status, schedule }, null, 2));
24
+ return;
25
+ }
26
+ const trading = status.exchange_active ? `${utils_js_1.c.green}OPEN${utils_js_1.c.reset}` : `${utils_js_1.c.red}CLOSED${utils_js_1.c.reset}`;
27
+ console.log();
28
+ console.log(` ${utils_js_1.c.bold}${utils_js_1.c.cyan}Exchange Status${utils_js_1.c.reset}`);
29
+ console.log(` ${utils_js_1.c.dim}${'─'.repeat(30)}${utils_js_1.c.reset}`);
30
+ console.log(` Trading: ${trading}`);
31
+ if (status.trading_active !== undefined) {
32
+ console.log(` Trading Active: ${status.trading_active ? 'yes' : 'no'}`);
33
+ }
34
+ if (schedule?.schedule) {
35
+ console.log(` Schedule: ${JSON.stringify(schedule.schedule).slice(0, 100)}`);
36
+ }
37
+ console.log();
38
+ }
@@ -0,0 +1,6 @@
1
+ export declare function settlementsCommand(opts: {
2
+ thesis?: string;
3
+ json?: boolean;
4
+ apiKey?: string;
5
+ apiUrl?: string;
6
+ }): Promise<void>;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.settlementsCommand = settlementsCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const client_js_1 = require("../client.js");
6
+ const utils_js_1 = require("../utils.js");
7
+ async function settlementsCommand(opts) {
8
+ // Paginate through all settlements
9
+ const all = [];
10
+ let cursor = '';
11
+ do {
12
+ const result = await (0, kalshi_js_1.getSettlements)({ limit: 200, cursor: cursor || undefined });
13
+ if (!result)
14
+ throw new Error('Kalshi not configured. Set KALSHI_API_KEY_ID + KALSHI_PRIVATE_KEY_PATH.');
15
+ all.push(...result.settlements);
16
+ cursor = result.cursor;
17
+ } while (cursor);
18
+ let filtered = all;
19
+ if (opts.thesis) {
20
+ const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
21
+ const ctx = await client.getContext(opts.thesis);
22
+ const edgeTickers = new Set((ctx.edges || []).map((e) => e.marketId));
23
+ filtered = all.filter((s) => edgeTickers.has(s.ticker));
24
+ }
25
+ if (opts.json) {
26
+ console.log(JSON.stringify(filtered, null, 2));
27
+ return;
28
+ }
29
+ if (filtered.length === 0) {
30
+ console.log(`${utils_js_1.c.dim}No settlements found.${utils_js_1.c.reset}`);
31
+ return;
32
+ }
33
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Settlements${utils_js_1.c.reset}`);
34
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(80)}${utils_js_1.c.reset}`);
35
+ console.log(`${utils_js_1.c.bold}${'Ticker'.padEnd(35)} ${'Result'.padEnd(8)} ${'Revenue'.padEnd(10)} ${'Cost'.padEnd(10)} P&L${utils_js_1.c.reset}`);
36
+ let totalPnl = 0;
37
+ for (const s of filtered.slice(0, 50)) {
38
+ const revenue = parseFloat(s.revenue || s.revenue_dollars || '0');
39
+ const cost = parseFloat(s.yes_total_cost || s.yes_total_cost_dollars || '0') +
40
+ parseFloat(s.no_total_cost || s.no_total_cost_dollars || '0');
41
+ const pnl = revenue - cost;
42
+ totalPnl += pnl;
43
+ const pnlStr = pnl >= 0 ? `${utils_js_1.c.green}+$${pnl.toFixed(2)}${utils_js_1.c.reset}` : `${utils_js_1.c.red}-$${Math.abs(pnl).toFixed(2)}${utils_js_1.c.reset}`;
44
+ const result = s.market_result || '-';
45
+ console.log(` ${(s.ticker || '').slice(0, 33).padEnd(35)} ${result.padEnd(8)} $${revenue.toFixed(2).padEnd(9)} $${cost.toFixed(2).padEnd(9)} ${pnlStr}`);
46
+ }
47
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(80)}${utils_js_1.c.reset}`);
48
+ const totalStr = totalPnl >= 0 ? `${utils_js_1.c.green}+$${totalPnl.toFixed(2)}${utils_js_1.c.reset}` : `${utils_js_1.c.red}-$${Math.abs(totalPnl).toFixed(2)}${utils_js_1.c.reset}`;
49
+ console.log(` Total: ${totalStr} (${filtered.length} settlements)`);
50
+ }
@@ -15,6 +15,8 @@ interface SetupOpts {
15
15
  check?: boolean;
16
16
  reset?: boolean;
17
17
  key?: string;
18
+ enableTrading?: boolean;
19
+ disableTrading?: boolean;
18
20
  }
19
21
  export declare function setupCommand(opts: SetupOpts): Promise<void>;
20
22
  export {};
@@ -147,6 +147,21 @@ async function setupCommand(opts) {
147
147
  ok(`保存到 ${(0, config_js_1.getConfigPath)()}`);
148
148
  return;
149
149
  }
150
+ // ── sf setup --enable-trading / --disable-trading ────────────────────────
151
+ if (opts.enableTrading) {
152
+ const existing = (0, config_js_1.loadFileConfig)();
153
+ (0, config_js_1.saveConfig)({ ...existing, tradingEnabled: true });
154
+ ok('Trading enabled. sf buy / sf sell / sf cancel now available.');
155
+ blank();
156
+ return;
157
+ }
158
+ if (opts.disableTrading) {
159
+ const existing = (0, config_js_1.loadFileConfig)();
160
+ (0, config_js_1.saveConfig)({ ...existing, tradingEnabled: false });
161
+ ok('Trading disabled.');
162
+ blank();
163
+ return;
164
+ }
150
165
  // ── Full interactive wizard ───────────────────────────────────────────────
151
166
  return runWizard();
152
167
  }
@@ -185,6 +200,13 @@ async function showCheck() {
185
200
  else {
186
201
  info(`${dim('○')} TAVILY ${dim('跳过')}`);
187
202
  }
203
+ // Trading
204
+ if (config.tradingEnabled) {
205
+ ok('TRADING 已启用');
206
+ }
207
+ else {
208
+ info(`${dim('○')} TRADING ${dim('未启用 — sf setup --enable-trading')}`);
209
+ }
188
210
  blank();
189
211
  console.log(` ${dim('配置文件: ' + (0, config_js_1.getConfigPath)())}`);
190
212
  blank();
@@ -303,6 +325,26 @@ async function runWizard() {
303
325
  if (config.tavilyKey)
304
326
  process.env.TAVILY_API_KEY = config.tavilyKey;
305
327
  // ════════════════════════════════════════════════════════════════════════════
328
+ // Step 5: Trading
329
+ // ════════════════════════════════════════════════════════════════════════════
330
+ if (config.kalshiKeyId) {
331
+ console.log(` ${bold('第 5 步:交易功能(可选)')}`);
332
+ blank();
333
+ info('⚠️ 启用后 sf buy / sf sell / sf cancel 可用。');
334
+ info('你的 Kalshi API key 必须有 read+write 权限。');
335
+ blank();
336
+ const enableTrading = await promptYN(' 启用交易功能?(y/N) ', false);
337
+ config.tradingEnabled = enableTrading;
338
+ if (enableTrading) {
339
+ ok('交易功能已启用');
340
+ }
341
+ else {
342
+ info(dim('跳过。之后可以 sf setup --enable-trading 启用。'));
343
+ }
344
+ blank();
345
+ (0, config_js_1.saveConfig)(config);
346
+ }
347
+ // ════════════════════════════════════════════════════════════════════════════
306
348
  // Summary
307
349
  // ════════════════════════════════════════════════════════════════════════════
308
350
  console.log(` ${dim('─'.repeat(25))}`);
@@ -433,7 +475,7 @@ async function promptForTavily() {
433
475
  blank();
434
476
  return answer;
435
477
  }
436
- // ─── Step 5: Thesis ──────────────────────────────────────────────────────────
478
+ // ─── Step 6: Thesis ──────────────────────────────────────────────────────────
437
479
  async function handleThesisStep(config) {
438
480
  try {
439
481
  const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
@@ -441,7 +483,7 @@ async function handleThesisStep(config) {
441
483
  const theses = data.theses || [];
442
484
  const activeTheses = theses.filter((t) => t.status === 'active');
443
485
  if (activeTheses.length > 0) {
444
- console.log(` ${bold('第 5 步:论文')}`);
486
+ console.log(` ${bold('第 6 步:论文')}`);
445
487
  blank();
446
488
  ok(`已有 ${activeTheses.length} 个活跃论文:`);
447
489
  for (const t of activeTheses.slice(0, 5)) {
@@ -482,7 +524,7 @@ async function handleThesisStep(config) {
482
524
  return;
483
525
  }
484
526
  // No theses — offer to create one
485
- console.log(` ${bold('第 5 步:创建你的第一个论文')}`);
527
+ console.log(` ${bold('第 6 步:创建你的第一个论文')}`);
486
528
  blank();
487
529
  info('论文是你对市场的一个核心判断。系统会基于它构建因果模型,');
488
530
  info('然后持续扫描预测市场寻找被错误定价的合约。');
@@ -14,5 +14,16 @@ async function signalCommand(id, content, opts) {
14
14
  if (result.signalId) {
15
15
  console.log(` ${utils_js_1.c.bold}ID:${utils_js_1.c.reset} ${result.signalId}`);
16
16
  }
17
- console.log(`\n${utils_js_1.c.dim}Signal queued for next monitor cycle.${utils_js_1.c.reset}`);
17
+ // Calculate minutes until next 15-min cron cycle (runs at :00, :15, :30, :45)
18
+ const now = new Date();
19
+ const minute = now.getMinutes();
20
+ const nextCycleMin = Math.ceil((minute + 1) / 15) * 15;
21
+ const minutesUntil = nextCycleMin - minute;
22
+ const nextRun = new Date(now);
23
+ nextRun.setMinutes(nextCycleMin % 60, 0, 0);
24
+ if (nextCycleMin >= 60)
25
+ nextRun.setHours(nextRun.getHours() + 1);
26
+ const timeStr = nextRun.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
27
+ console.log(`\n${utils_js_1.c.dim}Signal queued. Next monitor cycle in ~${minutesUntil}min (${timeStr}).${utils_js_1.c.reset}`);
28
+ console.log(`${utils_js_1.c.dim}Or run ${utils_js_1.c.reset}sf evaluate ${id}${utils_js_1.c.dim} to consume immediately.${utils_js_1.c.reset}`);
18
29
  }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * sf strategies — List strategies across theses
3
+ *
4
+ * Usage:
5
+ * sf strategies — all active strategies across all theses
6
+ * sf strategies f582bf76 — strategies for a specific thesis
7
+ * sf strategies --status executed — filter by status
8
+ * sf strategies --all — all statuses
9
+ */
10
+ import { Command } from 'commander';
11
+ export declare function registerStrategies(program: Command): void;
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ /**
3
+ * sf strategies — List strategies across theses
4
+ *
5
+ * Usage:
6
+ * sf strategies — all active strategies across all theses
7
+ * sf strategies f582bf76 — strategies for a specific thesis
8
+ * sf strategies --status executed — filter by status
9
+ * sf strategies --all — all statuses
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.registerStrategies = registerStrategies;
13
+ const client_js_1 = require("../client.js");
14
+ const STATUS_COLORS = {
15
+ active: '\x1b[32m', // green
16
+ watching: '\x1b[33m', // yellow
17
+ executed: '\x1b[36m', // cyan
18
+ cancelled: '\x1b[90m', // gray
19
+ review: '\x1b[31m', // red
20
+ };
21
+ const RESET = '\x1b[0m';
22
+ const DIM = '\x1b[2m';
23
+ const BOLD = '\x1b[1m';
24
+ function formatStrategy(s, showThesis = false) {
25
+ const statusColor = STATUS_COLORS[s.status] || '';
26
+ const lines = [];
27
+ // Header line
28
+ const thesisPrefix = showThesis ? `${DIM}${s.thesisTitle || s.thesisId?.slice(0, 8)}${RESET} ` : '';
29
+ lines.push(` ${thesisPrefix}${statusColor}[${s.status}]${RESET} ${BOLD}${s.marketId}${RESET} ${s.direction.toUpperCase()} ${DIM}${s.horizon}${RESET} priority ${s.priority || 0}`);
30
+ // Entry conditions
31
+ const entryParts = [];
32
+ if (s.entryBelow != null)
33
+ entryParts.push(`ask ≤ ${s.entryBelow}¢`);
34
+ if (s.entryAbove != null)
35
+ entryParts.push(`ask ≥ ${s.entryAbove}¢`);
36
+ const stopPart = s.stopLoss != null ? `Stop: ${s.stopLoss}¢` : '';
37
+ const tpPart = s.takeProfit != null ? `TP: ${s.takeProfit}¢` : '';
38
+ const maxPart = `Max: ${s.maxQuantity || 500}`;
39
+ const filledPart = `Filled: ${s.executedQuantity || 0}/${s.maxQuantity || 500}`;
40
+ const conditionLine = [
41
+ entryParts.length > 0 ? `Entry: ${entryParts.join(', ')}` : null,
42
+ stopPart || null,
43
+ tpPart || null,
44
+ maxPart,
45
+ filledPart,
46
+ ].filter(Boolean).join(' | ');
47
+ lines.push(` ${conditionLine}`);
48
+ // Soft conditions
49
+ if (s.softConditions) {
50
+ lines.push(` ${DIM}Soft: ${s.softConditions}${RESET}`);
51
+ }
52
+ // Review warning
53
+ if (s.status === 'review') {
54
+ lines.push(` \x1b[31m⚠️ Needs review${RESET}`);
55
+ }
56
+ // Rationale (truncated)
57
+ if (s.rationale) {
58
+ const truncated = s.rationale.length > 120 ? s.rationale.slice(0, 117) + '...' : s.rationale;
59
+ lines.push(` ${DIM}${truncated}${RESET}`);
60
+ }
61
+ // Footer
62
+ const createdBy = s.createdBy || 'user';
63
+ const date = s.createdAt ? new Date(s.createdAt).toLocaleDateString('en-US', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' }) : '';
64
+ lines.push(` ${DIM}created by ${createdBy} · ${date}${RESET}`);
65
+ return lines.join('\n');
66
+ }
67
+ function registerStrategies(program) {
68
+ program
69
+ .command('strategies')
70
+ .argument('[thesisId]', 'thesis ID or prefix (omit for all theses)')
71
+ .option('--status <status>', 'filter by status (active|watching|executed|cancelled|review)')
72
+ .option('--all', 'show all statuses (default: active only)')
73
+ .description('List strategies across theses')
74
+ .action(async (thesisId, opts) => {
75
+ try {
76
+ const client = new client_js_1.SFClient();
77
+ if (thesisId) {
78
+ // Strategies for a specific thesis
79
+ const statusParam = opts?.all ? '' : (opts?.status || 'active');
80
+ const url = statusParam
81
+ ? `/api/thesis/${thesisId}/strategies?status=${statusParam}`
82
+ : `/api/thesis/${thesisId}/strategies`;
83
+ const data = await client.getStrategies(thesisId, opts?.all ? undefined : (opts?.status || 'active'));
84
+ const strategies = data.strategies || [];
85
+ if (strategies.length === 0) {
86
+ console.log(`\n No strategies found for thesis ${thesisId}`);
87
+ if (!opts?.all && !opts?.status) {
88
+ console.log(` ${DIM}Try --all to see all statuses${RESET}`);
89
+ }
90
+ console.log();
91
+ return;
92
+ }
93
+ console.log(`\n Strategies for ${thesisId}\n`);
94
+ for (const s of strategies) {
95
+ console.log(formatStrategy(s));
96
+ console.log();
97
+ }
98
+ }
99
+ else {
100
+ // All strategies across all theses
101
+ const { theses } = await client.listTheses();
102
+ let totalStrategies = 0;
103
+ for (const thesis of theses) {
104
+ const statusFilter = opts?.all ? undefined : (opts?.status || 'active');
105
+ const data = await client.getStrategies(thesis.id, statusFilter);
106
+ const strategies = data.strategies || [];
107
+ if (strategies.length === 0)
108
+ continue;
109
+ totalStrategies += strategies.length;
110
+ console.log(`\n ${BOLD}${thesis.title}${RESET} ${DIM}(${thesis.id.slice(0, 8)})${RESET}\n`);
111
+ for (const s of strategies) {
112
+ console.log(formatStrategy(s));
113
+ console.log();
114
+ }
115
+ }
116
+ if (totalStrategies === 0) {
117
+ console.log(`\n No strategies found`);
118
+ if (!opts?.all && !opts?.status) {
119
+ console.log(` ${DIM}Try --all to see all statuses${RESET}`);
120
+ }
121
+ console.log();
122
+ }
123
+ }
124
+ }
125
+ catch (err) {
126
+ console.error(`\x1b[31mError:\x1b[0m ${err.message}`);
127
+ process.exit(1);
128
+ }
129
+ });
130
+ }
@@ -0,0 +1,12 @@
1
+ export declare function buyCommand(ticker: string, qty: string, opts: {
2
+ price?: string;
3
+ market?: boolean;
4
+ side?: string;
5
+ yesIAmSure?: boolean;
6
+ }): Promise<void>;
7
+ export declare function sellCommand(ticker: string, qty: string, opts: {
8
+ price?: string;
9
+ market?: boolean;
10
+ side?: string;
11
+ yesIAmSure?: boolean;
12
+ }): Promise<void>;
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buyCommand = buyCommand;
4
+ exports.sellCommand = sellCommand;
5
+ const kalshi_js_1 = require("../kalshi.js");
6
+ const config_js_1 = require("../config.js");
7
+ const utils_js_1 = require("../utils.js");
8
+ async function buyCommand(ticker, qty, opts) {
9
+ (0, config_js_1.requireTrading)();
10
+ await executeOrder(ticker, qty, 'buy', opts);
11
+ }
12
+ async function sellCommand(ticker, qty, opts) {
13
+ (0, config_js_1.requireTrading)();
14
+ await executeOrder(ticker, qty, 'sell', opts);
15
+ }
16
+ async function executeOrder(ticker, qty, action, opts) {
17
+ const quantity = parseInt(qty);
18
+ if (isNaN(quantity) || quantity <= 0)
19
+ throw new Error('Quantity must be a positive integer');
20
+ const side = (opts.side || 'yes');
21
+ const orderType = opts.market ? 'market' : 'limit';
22
+ if (orderType === 'limit' && !opts.price) {
23
+ throw new Error('Limit order requires --price <cents>. Use --market for market orders.');
24
+ }
25
+ const priceCents = opts.price ? parseInt(opts.price) : undefined;
26
+ if (priceCents !== undefined && (priceCents < 1 || priceCents > 99)) {
27
+ throw new Error('Price must be 1-99 cents.');
28
+ }
29
+ const priceDollars = priceCents ? (priceCents / 100).toFixed(2) : undefined;
30
+ const maxCost = ((priceCents || 99) * quantity / 100).toFixed(2);
31
+ console.log();
32
+ console.log(` ${utils_js_1.c.bold}${utils_js_1.c.cyan}${action.toUpperCase()} Order${utils_js_1.c.reset}`);
33
+ console.log(` ${utils_js_1.c.dim}${'─'.repeat(35)}${utils_js_1.c.reset}`);
34
+ console.log(` Ticker: ${ticker}`);
35
+ console.log(` Side: ${side === 'yes' ? utils_js_1.c.green + 'YES' + utils_js_1.c.reset : utils_js_1.c.red + 'NO' + utils_js_1.c.reset}`);
36
+ console.log(` Quantity: ${quantity}`);
37
+ console.log(` Type: ${orderType}`);
38
+ if (priceDollars)
39
+ console.log(` Price: ${priceCents}¢`);
40
+ console.log(` Max cost: $${maxCost}`);
41
+ console.log();
42
+ if (!opts.yesIAmSure) {
43
+ for (let i = 3; i > 0; i--) {
44
+ process.stdout.write(` Executing in ${i}... (Ctrl+C to cancel)\r`);
45
+ await new Promise(r => setTimeout(r, 1000));
46
+ }
47
+ process.stdout.write(' Executing... \n');
48
+ }
49
+ try {
50
+ const result = await (0, kalshi_js_1.createOrder)({
51
+ ticker,
52
+ side,
53
+ action,
54
+ type: orderType,
55
+ count: quantity,
56
+ ...(priceDollars ? { yes_price: priceDollars } : {}),
57
+ });
58
+ const order = result.order || result;
59
+ console.log();
60
+ console.log(` ${utils_js_1.c.green}✓${utils_js_1.c.reset} Order placed: ${order.order_id || 'OK'}`);
61
+ if (order.status)
62
+ console.log(` Status: ${order.status}`);
63
+ if (order.fill_count_fp)
64
+ console.log(` Filled: ${order.fill_count_fp}/${order.initial_count_fp || quantity}`);
65
+ console.log();
66
+ }
67
+ catch (err) {
68
+ const msg = err.message || String(err);
69
+ if (msg.includes('403')) {
70
+ console.error(`\n ${utils_js_1.c.red}✗${utils_js_1.c.reset} 403 Forbidden — your Kalshi key lacks write permission.`);
71
+ console.error(` Get a read+write key at https://kalshi.com/account/api-keys\n`);
72
+ }
73
+ else {
74
+ console.error(`\n ${utils_js_1.c.red}✗${utils_js_1.c.reset} ${msg}\n`);
75
+ }
76
+ process.exit(1);
77
+ }
78
+ }
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
  }