@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,3 @@
1
+ export declare function announcementsCommand(opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.announcementsCommand = announcementsCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ async function announcementsCommand(opts) {
7
+ const announcements = await (0, kalshi_js_1.getExchangeAnnouncements)();
8
+ if (opts.json) {
9
+ console.log(JSON.stringify(announcements, null, 2));
10
+ return;
11
+ }
12
+ if (announcements.length === 0) {
13
+ console.log(`${utils_js_1.c.dim}No announcements.${utils_js_1.c.reset}`);
14
+ return;
15
+ }
16
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Exchange Announcements${utils_js_1.c.reset}`);
17
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(70)}${utils_js_1.c.reset}`);
18
+ for (const a of announcements.slice(0, 20)) {
19
+ const time = a.created_time ? new Date(a.created_time).toLocaleDateString() : '';
20
+ const type = a.type ? `[${a.type}]` : '';
21
+ console.log(` ${utils_js_1.c.dim}${time}${utils_js_1.c.reset} ${type} ${a.title || a.message || ''}`);
22
+ if (a.body) {
23
+ const body = String(a.body).slice(0, 120);
24
+ console.log(` ${utils_js_1.c.dim}${body}${utils_js_1.c.reset}`);
25
+ }
26
+ }
27
+ console.log(`\n${utils_js_1.c.dim}${announcements.length} announcement(s)${utils_js_1.c.reset}`);
28
+ }
@@ -0,0 +1,3 @@
1
+ export declare function balanceCommand(opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.balanceCommand = balanceCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ async function balanceCommand(opts) {
7
+ const result = await (0, kalshi_js_1.getBalance)();
8
+ if (!result)
9
+ throw new Error('Kalshi not configured. Set KALSHI_API_KEY_ID + KALSHI_PRIVATE_KEY_PATH.');
10
+ if (opts.json) {
11
+ console.log(JSON.stringify(result, null, 2));
12
+ return;
13
+ }
14
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Kalshi Account${utils_js_1.c.reset}`);
15
+ console.log(` Balance: $${result.balance.toFixed(2)}`);
16
+ console.log(` Portfolio Value: $${result.portfolioValue.toFixed(2)}`);
17
+ }
@@ -0,0 +1,5 @@
1
+ export declare function cancelCommand(orderId: string | undefined, opts: {
2
+ all?: boolean;
3
+ ticker?: string;
4
+ yesIAmSure?: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.cancelCommand = cancelCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const config_js_1 = require("../config.js");
6
+ const utils_js_1 = require("../utils.js");
7
+ async function cancelCommand(orderId, opts) {
8
+ (0, config_js_1.requireTrading)();
9
+ if (opts.all) {
10
+ const result = await (0, kalshi_js_1.getOrders)({ status: 'resting', limit: 200 });
11
+ if (!result)
12
+ throw new Error('Kalshi not configured.');
13
+ let toCancel = result.orders;
14
+ if (opts.ticker) {
15
+ toCancel = toCancel.filter((o) => (o.ticker || '').startsWith(opts.ticker));
16
+ }
17
+ if (toCancel.length === 0) {
18
+ console.log(`\n ${utils_js_1.c.dim}No resting orders to cancel.${utils_js_1.c.reset}\n`);
19
+ return;
20
+ }
21
+ console.log(`\n Cancelling ${toCancel.length} order(s)...`);
22
+ if (!opts.yesIAmSure) {
23
+ for (let i = 3; i > 0; i--) {
24
+ process.stdout.write(` Executing in ${i}... (Ctrl+C to cancel)\r`);
25
+ await new Promise(r => setTimeout(r, 1000));
26
+ }
27
+ process.stdout.write(' Executing... \n');
28
+ }
29
+ for (let i = 0; i < toCancel.length; i += 20) {
30
+ const batch = toCancel.slice(i, i + 20).map((o) => o.order_id);
31
+ await (0, kalshi_js_1.batchCancelOrders)(batch);
32
+ }
33
+ console.log(`\n ${utils_js_1.c.green}✓${utils_js_1.c.reset} Cancelled ${toCancel.length} order(s).\n`);
34
+ return;
35
+ }
36
+ if (!orderId) {
37
+ throw new Error('Usage: sf cancel <orderId> or sf cancel --all');
38
+ }
39
+ await (0, kalshi_js_1.cancelOrder)(orderId);
40
+ console.log(`\n ${utils_js_1.c.green}✓${utils_js_1.c.reset} Order ${orderId} cancelled.\n`);
41
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * sf dashboard — Portfolio overview
3
+ *
4
+ * One-screen summary: theses, positions, risk exposure, top unpositioned edges.
5
+ * Uses existing APIs + local Kalshi positions.
6
+ */
7
+ export declare function dashboardCommand(opts?: {
8
+ json?: boolean;
9
+ apiKey?: string;
10
+ apiUrl?: string;
11
+ }): Promise<void>;
@@ -0,0 +1,195 @@
1
+ "use strict";
2
+ /**
3
+ * sf dashboard — Portfolio overview
4
+ *
5
+ * One-screen summary: theses, positions, risk exposure, top unpositioned edges.
6
+ * Uses existing APIs + local Kalshi positions.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.dashboardCommand = dashboardCommand;
10
+ const client_js_1 = require("../client.js");
11
+ const kalshi_js_1 = require("../kalshi.js");
12
+ // ── Risk category mapping by Kalshi ticker prefix ────────────────────────────
13
+ const RISK_CATEGORIES = {
14
+ KXWTIMAX: 'Oil',
15
+ KXWTI: 'Oil',
16
+ KXRECSSNBER: 'Recession',
17
+ KXAAAGASM: 'Gas',
18
+ KXCPI: 'Inflation',
19
+ KXINXY: 'S&P 500',
20
+ KXFEDDECISION: 'Fed Rate',
21
+ KXUNEMPLOYMENT: 'Unemployment',
22
+ KXCLOSEHORMUZ: 'Hormuz',
23
+ };
24
+ function categorize(ticker) {
25
+ // Match longest prefix first
26
+ const sorted = Object.keys(RISK_CATEGORIES).sort((a, b) => b.length - a.length);
27
+ for (const prefix of sorted) {
28
+ if (ticker.startsWith(prefix))
29
+ return RISK_CATEGORIES[prefix];
30
+ }
31
+ return 'Other';
32
+ }
33
+ function timeAgo(dateStr) {
34
+ const diff = Date.now() - new Date(dateStr).getTime();
35
+ const mins = Math.floor(diff / 60000);
36
+ if (mins < 60)
37
+ return `${mins}m ago`;
38
+ const hrs = Math.floor(mins / 60);
39
+ if (hrs < 24)
40
+ return `${hrs}h ago`;
41
+ const days = Math.floor(hrs / 24);
42
+ return `${days}d ago`;
43
+ }
44
+ async function dashboardCommand(opts) {
45
+ const client = new client_js_1.SFClient(opts?.apiKey, opts?.apiUrl);
46
+ // ── Fetch data in parallel ─────────────────────────────────────────────────
47
+ const [thesesResult, positions] = await Promise.all([
48
+ client.listTheses(),
49
+ (0, kalshi_js_1.getPositions)().catch(() => null),
50
+ ]);
51
+ const theses = thesesResult.theses || thesesResult;
52
+ // Fetch context for each thesis (edges)
53
+ const contexts = [];
54
+ for (const t of theses) {
55
+ try {
56
+ const ctx = await client.getContext(t.id);
57
+ contexts.push(ctx);
58
+ }
59
+ catch {
60
+ contexts.push(null);
61
+ }
62
+ }
63
+ // Enrich positions with live prices
64
+ if (positions) {
65
+ for (const pos of positions) {
66
+ const livePrice = await (0, kalshi_js_1.getMarketPrice)(pos.ticker);
67
+ if (livePrice !== null) {
68
+ pos.current_value = livePrice;
69
+ pos.unrealized_pnl = Math.round((livePrice - pos.average_price_paid) * pos.quantity);
70
+ }
71
+ }
72
+ }
73
+ // ── Collect all edges across all theses ────────────────────────────────────
74
+ const allEdges = [];
75
+ for (const ctx of contexts) {
76
+ if (!ctx?.edges)
77
+ continue;
78
+ for (const e of ctx.edges) {
79
+ allEdges.push(e);
80
+ }
81
+ }
82
+ // Dedupe edges by marketId (keep highest absolute edge)
83
+ const edgeMap = new Map();
84
+ for (const e of allEdges) {
85
+ const existing = edgeMap.get(e.marketId);
86
+ if (!existing || Math.abs(e.edge) > Math.abs(existing.edge)) {
87
+ edgeMap.set(e.marketId, e);
88
+ }
89
+ }
90
+ // Find positioned tickers
91
+ const positionedTickers = new Set(positions?.map((p) => p.ticker) || []);
92
+ // Unpositioned edges = edges where no position exists on that marketId
93
+ const unpositionedEdges = [...edgeMap.values()]
94
+ .filter(e => !positionedTickers.has(e.marketId))
95
+ .sort((a, b) => Math.abs(b.edge) - Math.abs(a.edge))
96
+ .slice(0, 10);
97
+ // ── JSON output ────────────────────────────────────────────────────────────
98
+ if (opts?.json) {
99
+ console.log(JSON.stringify({
100
+ theses,
101
+ positions,
102
+ unpositionedEdges,
103
+ }, null, 2));
104
+ return;
105
+ }
106
+ // ── Formatted output ───────────────────────────────────────────────────────
107
+ console.log();
108
+ console.log(' SimpleFunctions Dashboard');
109
+ console.log(' ' + '─'.repeat(50));
110
+ console.log();
111
+ // ── Theses ─────────────────────────────────────────────────────────────────
112
+ console.log(' Theses');
113
+ if (theses.length === 0) {
114
+ console.log(' (none)');
115
+ }
116
+ else {
117
+ for (let i = 0; i < theses.length; i++) {
118
+ const t = theses[i];
119
+ const ctx = contexts[i];
120
+ const id = t.id.slice(0, 8);
121
+ const title = (t.title || '').slice(0, 35).padEnd(35);
122
+ const conf = t.confidence != null ? `${Math.round(t.confidence * 100)}%` : '?%';
123
+ const edgeCount = ctx?.edges?.length || 0;
124
+ const updated = t.updatedAt ? timeAgo(t.updatedAt) : '?';
125
+ console.log(` ${id} ${title} ${conf.padStart(4)} ${String(edgeCount).padStart(2)} edges updated ${updated}`);
126
+ }
127
+ }
128
+ console.log();
129
+ // ── Positions ──────────────────────────────────────────────────────────────
130
+ console.log(' Positions');
131
+ if (!positions || positions.length === 0) {
132
+ console.log(' (no Kalshi positions or Kalshi not configured)');
133
+ }
134
+ else {
135
+ let totalCost = 0;
136
+ let totalPnl = 0;
137
+ for (const p of positions) {
138
+ const ticker = (p.ticker || '').padEnd(22);
139
+ const qty = String(p.quantity || 0).padStart(5);
140
+ const avg = `${p.average_price_paid || 0}¢`;
141
+ const now = typeof p.current_value === 'number' ? `${p.current_value}¢` : '?¢';
142
+ const pnlCents = p.unrealized_pnl || 0;
143
+ const pnlDollars = (pnlCents / 100).toFixed(2);
144
+ const pnlStr = pnlCents >= 0 ? `+$${pnlDollars}` : `-$${Math.abs(parseFloat(pnlDollars)).toFixed(2)}`;
145
+ const cost = (p.average_price_paid || 0) * (p.quantity || 0);
146
+ totalCost += cost;
147
+ totalPnl += pnlCents;
148
+ console.log(` ${ticker} ${qty} @ ${avg.padEnd(5)} now ${now.padEnd(5)} ${pnlStr}`);
149
+ }
150
+ console.log(' ' + '─'.repeat(45));
151
+ const totalCostDollars = (totalCost / 100).toFixed(0);
152
+ const totalPnlDollars = (totalPnl / 100).toFixed(2);
153
+ const pnlDisplay = totalPnl >= 0 ? `+$${totalPnlDollars}` : `-$${Math.abs(parseFloat(totalPnlDollars)).toFixed(2)}`;
154
+ console.log(` Total cost: $${totalCostDollars} | P&L: ${pnlDisplay}`);
155
+ }
156
+ console.log();
157
+ // ── Risk Exposure ──────────────────────────────────────────────────────────
158
+ if (positions && positions.length > 0) {
159
+ console.log(' Risk Exposure');
160
+ const riskGroups = new Map();
161
+ for (const p of positions) {
162
+ const cat = categorize(p.ticker || '');
163
+ const existing = riskGroups.get(cat) || { cost: 0, contracts: 0, tickers: [] };
164
+ const cost = (p.average_price_paid || 0) * (p.quantity || 0);
165
+ existing.cost += cost;
166
+ existing.contracts += p.quantity || 0;
167
+ if (!existing.tickers.includes(p.ticker))
168
+ existing.tickers.push(p.ticker);
169
+ riskGroups.set(cat, existing);
170
+ }
171
+ // Sort by cost descending
172
+ const sorted = [...riskGroups.entries()].sort((a, b) => b[1].cost - a[1].cost);
173
+ for (const [category, data] of sorted) {
174
+ const costDollars = `$${(data.cost / 100).toFixed(0)}`;
175
+ const tickerSummary = data.tickers.length <= 2
176
+ ? ` (${data.tickers.join('+')})`
177
+ : ` (${data.tickers.length} markets)`;
178
+ console.log(` ${(category + tickerSummary + ':').padEnd(35)} ${costDollars.padStart(7)} cost | ${String(data.contracts).padStart(5)} contracts`);
179
+ }
180
+ console.log();
181
+ }
182
+ // ── Top Unpositioned Edges ─────────────────────────────────────────────────
183
+ if (unpositionedEdges.length > 0) {
184
+ console.log(' Top Unpositioned Edges');
185
+ for (const e of unpositionedEdges) {
186
+ const name = (e.market || e.marketId || '').slice(0, 25).padEnd(25);
187
+ const mkt = `${e.marketPrice}¢`;
188
+ const thesis = `${e.thesisPrice}¢`;
189
+ const edge = e.edge > 0 ? `+${e.edge}` : `${e.edge}`;
190
+ const liq = e.orderbook?.liquidityScore || '?';
191
+ console.log(` ${name} ${mkt.padStart(5)} → ${thesis.padStart(5)} edge ${edge.padStart(4)} ${liq}`);
192
+ }
193
+ console.log();
194
+ }
195
+ }
@@ -0,0 +1,4 @@
1
+ export declare function fillsCommand(opts: {
2
+ ticker?: string;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fillsCommand = fillsCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ async function fillsCommand(opts) {
7
+ const result = await (0, kalshi_js_1.getFills)({ ticker: opts.ticker, limit: 50 });
8
+ if (!result)
9
+ throw new Error('Kalshi not configured. Set KALSHI_API_KEY_ID + KALSHI_PRIVATE_KEY_PATH.');
10
+ if (opts.json) {
11
+ console.log(JSON.stringify(result.fills, null, 2));
12
+ return;
13
+ }
14
+ if (result.fills.length === 0) {
15
+ console.log(`${utils_js_1.c.dim}No fills.${utils_js_1.c.reset}`);
16
+ return;
17
+ }
18
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Recent Fills${utils_js_1.c.reset}`);
19
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(80)}${utils_js_1.c.reset}`);
20
+ for (const f of result.fills) {
21
+ const price = f.yes_price_dollars ? `${parseFloat(f.yes_price_dollars) * 100}¢` : `${f.yes_price || '?'}¢`;
22
+ const side = f.side === 'yes' ? `${utils_js_1.c.green}YES${utils_js_1.c.reset}` : `${utils_js_1.c.red}NO${utils_js_1.c.reset}`;
23
+ const action = f.action || 'buy';
24
+ const count = f.count_fp || f.count || '?';
25
+ const time = f.created_time ? new Date(f.created_time).toLocaleString() : '';
26
+ console.log(` ${(f.ticker || '').padEnd(35)} ${action.padEnd(5)} ${side} ${price.padEnd(8)} x${count} ${utils_js_1.c.dim}${time}${utils_js_1.c.reset}`);
27
+ }
28
+ console.log(`\n${utils_js_1.c.dim}${result.fills.length} fill(s)${utils_js_1.c.reset}`);
29
+ }
@@ -0,0 +1,4 @@
1
+ export declare function forecastCommand(eventTicker: string, opts: {
2
+ days?: string;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.forecastCommand = forecastCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ const KALSHI_API_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
7
+ async function forecastCommand(eventTicker, opts) {
8
+ const days = parseInt(opts.days || '7');
9
+ // Get series ticker from event
10
+ const evtRes = await fetch(`${KALSHI_API_BASE}/events/${eventTicker}`, {
11
+ headers: { 'Accept': 'application/json' },
12
+ });
13
+ if (!evtRes.ok)
14
+ throw new Error(`Event not found: ${eventTicker}`);
15
+ const evtData = await evtRes.json();
16
+ const seriesTicker = evtData.event?.series_ticker;
17
+ if (!seriesTicker)
18
+ throw new Error(`No series_ticker for ${eventTicker}`);
19
+ // Align timestamps to midnight UTC — Kalshi API rejects unaligned/future timestamps
20
+ const todayMidnight = new Date();
21
+ todayMidnight.setUTCHours(0, 0, 0, 0);
22
+ const endTs = Math.floor(todayMidnight.getTime() / 1000);
23
+ const startTs = endTs - days * 86400;
24
+ const history = await (0, kalshi_js_1.getForecastHistory)({
25
+ seriesTicker,
26
+ eventTicker,
27
+ percentiles: [5000, 7500, 9000],
28
+ startTs,
29
+ endTs,
30
+ periodInterval: 1440,
31
+ });
32
+ if (!history || history.length === 0) {
33
+ console.log(`${utils_js_1.c.dim}No forecast data for ${eventTicker}${utils_js_1.c.reset}`);
34
+ return;
35
+ }
36
+ if (opts.json) {
37
+ console.log(JSON.stringify(history, null, 2));
38
+ return;
39
+ }
40
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Forecast: ${evtData.event?.title || eventTicker}${utils_js_1.c.reset}`);
41
+ console.log(`${utils_js_1.c.dim}Series: ${seriesTicker} | ${days} days${utils_js_1.c.reset}`);
42
+ console.log();
43
+ console.log(`${utils_js_1.c.bold}${'Date'.padEnd(14)} ${'P50'.padEnd(12)} ${'P75'.padEnd(12)} P90${utils_js_1.c.reset}`);
44
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(52)}${utils_js_1.c.reset}`);
45
+ for (const point of history) {
46
+ const date = new Date(point.end_period_ts * 1000).toISOString().slice(0, 10);
47
+ const pts = point.percentile_points || [];
48
+ const p50 = pts.find((p) => p.percentile === 5000)?.formatted_forecast || '-';
49
+ const p75 = pts.find((p) => p.percentile === 7500)?.formatted_forecast || '-';
50
+ const p90 = pts.find((p) => p.percentile === 9000)?.formatted_forecast || '-';
51
+ console.log(` ${date.padEnd(14)} ${p50.padEnd(12)} ${p75.padEnd(12)} ${p90}`);
52
+ }
53
+ }
@@ -0,0 +1,3 @@
1
+ export declare function historyCommand(ticker: string, opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.historyCommand = historyCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ async function historyCommand(ticker, opts) {
7
+ const market = await (0, kalshi_js_1.getHistoricalMarket)(ticker);
8
+ if (!market) {
9
+ console.log(`${utils_js_1.c.dim}No historical data for ${ticker}${utils_js_1.c.reset}`);
10
+ return;
11
+ }
12
+ if (opts.json) {
13
+ console.log(JSON.stringify(market, null, 2));
14
+ return;
15
+ }
16
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}${market.title || ticker}${utils_js_1.c.reset}`);
17
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(60)}${utils_js_1.c.reset}`);
18
+ console.log(` Ticker: ${market.ticker || ticker}`);
19
+ console.log(` Event: ${market.event_ticker || '-'}`);
20
+ console.log(` Status: ${market.status || '-'}`);
21
+ console.log(` Result: ${market.result || market.market_result || '-'}`);
22
+ if (market.last_price_dollars) {
23
+ console.log(` Last Price: ${Math.round(parseFloat(market.last_price_dollars) * 100)}¢`);
24
+ }
25
+ if (market.settlement_value !== undefined) {
26
+ console.log(` Settlement: ${market.settlement_value}`);
27
+ }
28
+ if (market.volume) {
29
+ console.log(` Volume: ${market.volume}`);
30
+ }
31
+ if (market.open_interest) {
32
+ console.log(` Open Int: ${market.open_interest}`);
33
+ }
34
+ if (market.expiration_time) {
35
+ console.log(` Expired: ${market.expiration_time}`);
36
+ }
37
+ console.log();
38
+ }
@@ -0,0 +1,8 @@
1
+ export declare function milestonesCommand(opts: {
2
+ category?: string;
3
+ thesis?: string;
4
+ hours?: string;
5
+ json?: boolean;
6
+ apiKey?: string;
7
+ apiUrl?: string;
8
+ }): Promise<void>;
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.milestonesCommand = milestonesCommand;
4
+ const client_js_1 = require("../client.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ const KALSHI_API_BASE = 'https://api.elections.kalshi.com/trade-api/v2';
7
+ async function milestonesCommand(opts) {
8
+ const hours = parseInt(opts.hours || '168');
9
+ const now = new Date();
10
+ const cutoff = new Date(now.getTime() + hours * 3600000);
11
+ // Fetch milestones (public endpoint)
12
+ const url = `${KALSHI_API_BASE}/milestones?limit=200&minimum_start_date=${now.toISOString()}` +
13
+ (opts.category ? `&category=${opts.category}` : '');
14
+ const res = await fetch(url, { headers: { 'Accept': 'application/json' } });
15
+ if (!res.ok)
16
+ throw new Error(`Kalshi API ${res.status}`);
17
+ const data = await res.json();
18
+ let milestones = (data.milestones || []).filter((m) => new Date(m.start_date).getTime() <= cutoff.getTime());
19
+ // If thesis specified, filter to matching event tickers
20
+ if (opts.thesis) {
21
+ const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
22
+ const ctx = await client.getContext(opts.thesis);
23
+ const edgeEventTickers = new Set((ctx.edges || []).map((e) => e.eventTicker).filter(Boolean));
24
+ // Also match by related_event_tickers overlap
25
+ const edgeSeriesTickers = new Set((ctx.edges || []).map((e) => e.seriesTicker).filter(Boolean));
26
+ milestones = milestones.filter((m) => {
27
+ const related = m.related_event_tickers || m.primary_event_tickers || [];
28
+ return related.some((t) => edgeEventTickers.has(t) || edgeSeriesTickers.has(t.split('-')[0]));
29
+ });
30
+ }
31
+ if (opts.json) {
32
+ console.log(JSON.stringify(milestones, null, 2));
33
+ return;
34
+ }
35
+ if (milestones.length === 0) {
36
+ console.log(`${utils_js_1.c.dim}No milestones in the next ${hours} hours.${utils_js_1.c.reset}`);
37
+ return;
38
+ }
39
+ // Sort by date
40
+ milestones.sort((a, b) => new Date(a.start_date).getTime() - new Date(b.start_date).getTime());
41
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Upcoming Milestones (next ${hours}h)${utils_js_1.c.reset}`);
42
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(80)}${utils_js_1.c.reset}`);
43
+ for (const m of milestones) {
44
+ const date = new Date(m.start_date);
45
+ const hoursUntil = Math.round((date.getTime() - now.getTime()) / 3600000);
46
+ const timeStr = hoursUntil <= 24
47
+ ? `${utils_js_1.c.bold}${hoursUntil}h${utils_js_1.c.reset}`
48
+ : `${utils_js_1.c.dim}${Math.round(hoursUntil / 24)}d${utils_js_1.c.reset}`;
49
+ const cat = `${utils_js_1.c.dim}[${m.category}]${utils_js_1.c.reset}`;
50
+ const tickers = (m.related_event_tickers || []).slice(0, 3).join(', ');
51
+ console.log(` ${timeStr.padEnd(12)} ${cat.padEnd(25)} ${m.title}`);
52
+ if (tickers)
53
+ console.log(` ${' '.repeat(10)} ${utils_js_1.c.dim}${tickers}${utils_js_1.c.reset}`);
54
+ }
55
+ console.log(`\n${utils_js_1.c.dim}${milestones.length} milestone(s)${utils_js_1.c.reset}`);
56
+ }
@@ -0,0 +1,4 @@
1
+ export declare function ordersCommand(opts: {
2
+ status?: string;
3
+ json?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ordersCommand = ordersCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const utils_js_1 = require("../utils.js");
6
+ async function ordersCommand(opts) {
7
+ const status = opts.status || 'resting';
8
+ const result = await (0, kalshi_js_1.getOrders)({ status, limit: 100 });
9
+ if (!result)
10
+ throw new Error('Kalshi not configured. Set KALSHI_API_KEY_ID + KALSHI_PRIVATE_KEY_PATH.');
11
+ if (opts.json) {
12
+ console.log(JSON.stringify(result.orders, null, 2));
13
+ return;
14
+ }
15
+ if (result.orders.length === 0) {
16
+ console.log(`${utils_js_1.c.dim}No ${status} orders.${utils_js_1.c.reset}`);
17
+ return;
18
+ }
19
+ console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Orders (${status})${utils_js_1.c.reset}`);
20
+ console.log(`${utils_js_1.c.dim}${'─'.repeat(80)}${utils_js_1.c.reset}`);
21
+ for (const o of result.orders) {
22
+ const price = o.yes_price_dollars ? `${parseFloat(o.yes_price_dollars) * 100}¢` : `${o.yes_price || '?'}¢`;
23
+ const side = o.side === 'yes' ? `${utils_js_1.c.green}YES${utils_js_1.c.reset}` : `${utils_js_1.c.red}NO${utils_js_1.c.reset}`;
24
+ const remaining = o.remaining_count_fp || o.remaining_count || '?';
25
+ console.log(` ${(o.ticker || '').padEnd(35)} ${side} ${price.padEnd(8)} qty ${remaining}`);
26
+ }
27
+ console.log(`\n${utils_js_1.c.dim}${result.orders.length} order(s)${utils_js_1.c.reset}`);
28
+ }
@@ -8,10 +8,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
8
8
  exports.publishCommand = publishCommand;
9
9
  exports.unpublishCommand = unpublishCommand;
10
10
  const client_js_1 = require("../client.js");
11
+ /** Convert any string to a URL-safe slug: lowercase, hyphens, strip invalid chars */
12
+ function slugify(input) {
13
+ return input
14
+ .toLowerCase()
15
+ .trim()
16
+ .replace(/[^a-z0-9\s-]/g, '') // remove non-alphanumeric except spaces/hyphens
17
+ .replace(/[\s_]+/g, '-') // spaces/underscores → hyphens
18
+ .replace(/-+/g, '-') // collapse multiple hyphens
19
+ .replace(/^-|-$/g, '') // trim leading/trailing hyphens
20
+ .slice(0, 60); // max 60 chars
21
+ }
11
22
  async function publishCommand(thesisId, opts) {
23
+ const slug = slugify(opts.slug);
24
+ if (slug.length < 3) {
25
+ console.error(`\n Error: slug too short after normalization: "${slug}" (need 3+ chars)\n`);
26
+ process.exit(1);
27
+ }
28
+ if (slug !== opts.slug) {
29
+ console.log(` Slug normalized: "${opts.slug}" → "${slug}"`);
30
+ }
12
31
  const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
13
- await client.publish(thesisId, opts.slug, opts.description);
14
- console.log(`\n ✓ Published: https://simplefunctions.dev/thesis/${opts.slug}\n`);
32
+ await client.publish(thesisId, slug, opts.description);
33
+ console.log(`\n ✓ Published: https://simplefunctions.dev/thesis/${slug}\n`);
15
34
  }
16
35
  async function unpublishCommand(thesisId, opts) {
17
36
  const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
@@ -0,0 +1,5 @@
1
+ export declare function rfqCommand(ticker: string, qty: string, opts: {
2
+ targetCost?: string;
3
+ restRemainder?: boolean;
4
+ json?: boolean;
5
+ }): Promise<void>;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.rfqCommand = rfqCommand;
4
+ const kalshi_js_1 = require("../kalshi.js");
5
+ const config_js_1 = require("../config.js");
6
+ const utils_js_1 = require("../utils.js");
7
+ async function rfqCommand(ticker, qty, opts) {
8
+ (0, config_js_1.requireTrading)();
9
+ const quantity = parseInt(qty);
10
+ if (isNaN(quantity) || quantity <= 0)
11
+ throw new Error('Quantity must be a positive integer');
12
+ console.log();
13
+ console.log(` ${utils_js_1.c.bold}${utils_js_1.c.cyan}Request for Quote${utils_js_1.c.reset}`);
14
+ console.log(` ${utils_js_1.c.dim}${'─'.repeat(30)}${utils_js_1.c.reset}`);
15
+ console.log(` Market: ${ticker}`);
16
+ console.log(` Contracts: ${quantity}`);
17
+ if (opts.targetCost)
18
+ console.log(` Target cost: ${opts.targetCost}¢/contract`);
19
+ console.log(` Rest remainder: ${opts.restRemainder ? 'yes' : 'no'}`);
20
+ console.log();
21
+ try {
22
+ const result = await (0, kalshi_js_1.createRFQ)({
23
+ market_ticker: ticker,
24
+ contracts: quantity,
25
+ rest_remainder: opts.restRemainder || false,
26
+ ...(opts.targetCost ? { target_cost_centi_cents: parseInt(opts.targetCost) * 100 } : {}),
27
+ });
28
+ console.log(` ${utils_js_1.c.green}✓${utils_js_1.c.reset} RFQ created: ${result.id || result.rfq_id || 'OK'}`);
29
+ console.log();
30
+ }
31
+ catch (err) {
32
+ console.error(`\n ${utils_js_1.c.red}✗${utils_js_1.c.reset} ${err.message}\n`);
33
+ process.exit(1);
34
+ }
35
+ }
@@ -0,0 +1,3 @@
1
+ export declare function scheduleCommand(opts: {
2
+ json?: boolean;
3
+ }): Promise<void>;