@spfunctions/cli 1.4.5 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agent.js +349 -39
- package/dist/commands/book.d.ts +17 -0
- package/dist/commands/book.js +220 -0
- package/dist/commands/dashboard.js +30 -1
- package/dist/commands/liquidity.d.ts +2 -0
- package/dist/commands/liquidity.js +128 -43
- package/dist/commands/positions.js +50 -0
- package/dist/commands/scan.d.ts +1 -0
- package/dist/commands/scan.js +66 -15
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +71 -6
- package/dist/config.d.ts +2 -0
- package/dist/config.js +8 -0
- package/dist/index.js +92 -8
- package/dist/polymarket.d.ts +237 -0
- package/dist/polymarket.js +353 -0
- package/dist/polymarket.test.d.ts +1 -0
- package/dist/polymarket.test.js +424 -0
- package/dist/topics.d.ts +3 -0
- package/dist/topics.js +65 -7
- package/dist/topics.test.js +83 -6
- package/dist/tui/dashboard.js +65 -30
- package/dist/tui/widgets/edges.js +5 -4
- package/dist/tui/widgets/portfolio.js +3 -2
- package/package.json +1 -1
package/dist/commands/scan.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.scanCommand = scanCommand;
|
|
4
4
|
const client_js_1 = require("../client.js");
|
|
5
|
+
const polymarket_js_1 = require("../polymarket.js");
|
|
5
6
|
const utils_js_1 = require("../utils.js");
|
|
6
7
|
async function scanCommand(query, opts) {
|
|
7
8
|
// Mode 1: --market TICKER — single market detail
|
|
@@ -14,8 +15,8 @@ async function scanCommand(query, opts) {
|
|
|
14
15
|
await showSeries(opts.series.toUpperCase(), opts.json);
|
|
15
16
|
return;
|
|
16
17
|
}
|
|
17
|
-
// Mode 3: keyword scan across all series
|
|
18
|
-
await keywordScan(query, opts.json);
|
|
18
|
+
// Mode 3: keyword scan across all series + Polymarket
|
|
19
|
+
await keywordScan(query, opts.json, opts.venue || 'all');
|
|
19
20
|
}
|
|
20
21
|
async function showMarket(ticker, json) {
|
|
21
22
|
console.log(`${utils_js_1.c.dim}Fetching market ${ticker}...${utils_js_1.c.reset}`);
|
|
@@ -77,10 +78,19 @@ async function showSeries(seriesTicker, json) {
|
|
|
77
78
|
printMarketsTable(markets);
|
|
78
79
|
}
|
|
79
80
|
}
|
|
80
|
-
async function keywordScan(query, json) {
|
|
81
|
+
async function keywordScan(query, json, venue = 'all') {
|
|
81
82
|
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
82
|
-
|
|
83
|
-
const
|
|
83
|
+
// ── Polymarket search (runs in parallel with Kalshi) ───────────────────────
|
|
84
|
+
const polyPromise = (venue === 'kalshi')
|
|
85
|
+
? Promise.resolve([])
|
|
86
|
+
: (0, polymarket_js_1.polymarketSearch)(query, 10).catch(() => []);
|
|
87
|
+
if (venue !== 'polymarket') {
|
|
88
|
+
console.log(`${utils_js_1.c.dim}Scanning Kalshi for: "${query}"...${utils_js_1.c.reset}`);
|
|
89
|
+
}
|
|
90
|
+
if (venue !== 'kalshi') {
|
|
91
|
+
console.log(`${utils_js_1.c.dim}Scanning Polymarket for: "${query}"...${utils_js_1.c.reset}`);
|
|
92
|
+
}
|
|
93
|
+
const allSeries = venue === 'polymarket' ? [] : await (0, client_js_1.kalshiFetchAllSeries)();
|
|
84
94
|
// Score each series
|
|
85
95
|
const thesisKeywords = [
|
|
86
96
|
'oil', 'wti', 'gas', 'recession', 'gdp', 'fed', 'inflation',
|
|
@@ -126,13 +136,14 @@ async function keywordScan(query, json) {
|
|
|
126
136
|
for (const m of markets) {
|
|
127
137
|
if (m.status === 'open' || m.status === 'active') {
|
|
128
138
|
allMarkets.push({
|
|
139
|
+
venue: 'kalshi',
|
|
129
140
|
seriesTicker: s.ticker,
|
|
130
141
|
ticker: m.ticker,
|
|
131
142
|
title: m.title || m.subtitle || '',
|
|
132
143
|
yesAsk: parseFloat(m.yes_ask_dollars || '0'),
|
|
133
144
|
lastPrice: parseFloat(m.last_price_dollars || '0'),
|
|
134
145
|
volume24h: parseFloat(m.volume_24h_fp || '0'),
|
|
135
|
-
liquidity: parseFloat(m.liquidity_dollars || '0'),
|
|
146
|
+
liquidity: parseFloat(m.open_interest_fp || m.liquidity_dollars || '0'),
|
|
136
147
|
});
|
|
137
148
|
}
|
|
138
149
|
}
|
|
@@ -144,27 +155,67 @@ async function keywordScan(query, json) {
|
|
|
144
155
|
}
|
|
145
156
|
}
|
|
146
157
|
allMarkets.sort((a, b) => b.liquidity - a.liquidity);
|
|
158
|
+
// ── Polymarket results ──────────────────────────────────────────────────────
|
|
159
|
+
const polyEvents = await polyPromise;
|
|
160
|
+
const polyMarkets = [];
|
|
161
|
+
for (const event of polyEvents) {
|
|
162
|
+
for (const m of (event.markets || [])) {
|
|
163
|
+
if (!m.active || m.closed)
|
|
164
|
+
continue;
|
|
165
|
+
const prices = (0, polymarket_js_1.parseOutcomePrices)(m.outcomePrices);
|
|
166
|
+
const yesPrice = prices[0] || 0;
|
|
167
|
+
polyMarkets.push({
|
|
168
|
+
venue: 'polymarket',
|
|
169
|
+
ticker: m.conditionId?.slice(0, 16) || m.id,
|
|
170
|
+
title: m.groupItemTitle
|
|
171
|
+
? `${event.title}: ${m.groupItemTitle}`
|
|
172
|
+
: m.question || event.title,
|
|
173
|
+
yesAsk: yesPrice,
|
|
174
|
+
lastPrice: m.lastTradePrice || yesPrice,
|
|
175
|
+
volume24h: m.volume24hr || 0,
|
|
176
|
+
liquidity: m.liquidityNum || 0,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ── Merge & output ─────────────────────────────────────────────────────────
|
|
181
|
+
// Tag Kalshi markets with venue
|
|
182
|
+
for (const m of allMarkets) {
|
|
183
|
+
if (!m.venue)
|
|
184
|
+
m.venue = 'kalshi';
|
|
185
|
+
}
|
|
186
|
+
const combined = [...allMarkets, ...polyMarkets];
|
|
187
|
+
combined.sort((a, b) => b.liquidity - a.liquidity);
|
|
147
188
|
if (json) {
|
|
148
|
-
console.log(JSON.stringify(
|
|
189
|
+
console.log(JSON.stringify(combined, null, 2));
|
|
149
190
|
return;
|
|
150
191
|
}
|
|
151
|
-
(0, utils_js_1.header)(`${
|
|
192
|
+
(0, utils_js_1.header)(`${combined.length} Live Markets (${allMarkets.length} Kalshi + ${polyMarkets.length} Polymarket)`);
|
|
152
193
|
console.log(utils_js_1.c.bold +
|
|
153
|
-
(0, utils_js_1.pad)('
|
|
194
|
+
(0, utils_js_1.pad)('', 5) +
|
|
195
|
+
(0, utils_js_1.pad)('Ticker', 30) +
|
|
154
196
|
(0, utils_js_1.rpad)('Yes', 6) +
|
|
155
197
|
(0, utils_js_1.rpad)('Last', 6) +
|
|
156
198
|
(0, utils_js_1.rpad)('Vol24h', 10) +
|
|
157
199
|
(0, utils_js_1.rpad)('Liq', 10) +
|
|
158
200
|
' Title' +
|
|
159
201
|
utils_js_1.c.reset);
|
|
160
|
-
(0, utils_js_1.hr)(
|
|
161
|
-
for (const m of
|
|
162
|
-
|
|
202
|
+
(0, utils_js_1.hr)(120);
|
|
203
|
+
for (const m of combined.slice(0, 60)) {
|
|
204
|
+
// Venue tag: fixed 5-char column, color applied AFTER padding
|
|
205
|
+
const venuePad = m.venue === 'polymarket' ? 'POLY ' : 'KLSH ';
|
|
206
|
+
const venueColored = m.venue === 'polymarket'
|
|
207
|
+
? utils_js_1.c.blue + venuePad + utils_js_1.c.reset
|
|
208
|
+
: utils_js_1.c.cyan + venuePad + utils_js_1.c.reset;
|
|
209
|
+
const tickerStr = m.venue === 'polymarket'
|
|
210
|
+
? (m.ticker || '').slice(0, 28)
|
|
211
|
+
: (m.ticker || '').slice(0, 28);
|
|
212
|
+
console.log(venueColored +
|
|
213
|
+
(0, utils_js_1.pad)(tickerStr, 30) +
|
|
163
214
|
(0, utils_js_1.rpad)(`${Math.round(m.yesAsk * 100)}¢`, 6) +
|
|
164
215
|
(0, utils_js_1.rpad)(`${Math.round(m.lastPrice * 100)}¢`, 6) +
|
|
165
|
-
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.volume24h), 10) +
|
|
166
|
-
(0, utils_js_1.rpad)((0, utils_js_1.vol)(m.liquidity), 10) +
|
|
167
|
-
` ${m.title.slice(0, 55)}`);
|
|
216
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(Math.round(m.volume24h)), 10) +
|
|
217
|
+
(0, utils_js_1.rpad)((0, utils_js_1.vol)(Math.round(m.liquidity)), 10) +
|
|
218
|
+
` ${(m.title || '').slice(0, 55)}`);
|
|
168
219
|
}
|
|
169
220
|
console.log('');
|
|
170
221
|
}
|
package/dist/commands/setup.d.ts
CHANGED
package/dist/commands/setup.js
CHANGED
|
@@ -165,6 +165,17 @@ async function setupCommand(opts) {
|
|
|
165
165
|
blank();
|
|
166
166
|
return;
|
|
167
167
|
}
|
|
168
|
+
// ── sf setup --polymarket (reconfigure Polymarket credentials) ──────────
|
|
169
|
+
if (opts.polymarket) {
|
|
170
|
+
const existing = (0, config_js_1.loadFileConfig)();
|
|
171
|
+
blank();
|
|
172
|
+
console.log(` ${bold('Reconfigure Polymarket Credentials')}`);
|
|
173
|
+
blank();
|
|
174
|
+
await promptForPolymarket(existing);
|
|
175
|
+
(0, config_js_1.saveConfig)(existing);
|
|
176
|
+
blank();
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
168
179
|
// ── sf setup --enable-trading / --disable-trading ────────────────────────
|
|
169
180
|
if (opts.enableTrading) {
|
|
170
181
|
const existing = (0, config_js_1.loadFileConfig)();
|
|
@@ -211,6 +222,13 @@ async function showCheck() {
|
|
|
211
222
|
else {
|
|
212
223
|
info(`${dim('○')} KALSHI ${dim('skipped')}`);
|
|
213
224
|
}
|
|
225
|
+
// Polymarket
|
|
226
|
+
if (config.polymarketWalletAddress) {
|
|
227
|
+
ok(`POLYMARKET ${dim(mask(config.polymarketWalletAddress))}`);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
info(`${dim('○')} POLYMARKET ${dim('skipped')}`);
|
|
231
|
+
}
|
|
214
232
|
// Tavily
|
|
215
233
|
if (config.tavilyKey) {
|
|
216
234
|
ok(`TAVILY ${dim(mask(config.tavilyKey))}`);
|
|
@@ -318,9 +336,27 @@ async function runWizard() {
|
|
|
318
336
|
}
|
|
319
337
|
(0, config_js_1.saveConfig)(config);
|
|
320
338
|
// ════════════════════════════════════════════════════════════════════════════
|
|
321
|
-
// Step 4:
|
|
339
|
+
// Step 4: Polymarket
|
|
340
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
341
|
+
console.log(` ${bold('Step 4: Polymarket (optional)')}`);
|
|
342
|
+
blank();
|
|
343
|
+
const existingPolyWallet = process.env.POLYMARKET_WALLET_ADDRESS || config.polymarketWalletAddress;
|
|
344
|
+
if (existingPolyWallet) {
|
|
345
|
+
ok(`Detected wallet — ${dim(mask(existingPolyWallet))}`);
|
|
346
|
+
info(dim('Skipping.'));
|
|
347
|
+
config.polymarketWalletAddress = existingPolyWallet;
|
|
348
|
+
blank();
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
await promptForPolymarket(config);
|
|
352
|
+
}
|
|
353
|
+
(0, config_js_1.saveConfig)(config);
|
|
354
|
+
if (config.polymarketWalletAddress)
|
|
355
|
+
process.env.POLYMARKET_WALLET_ADDRESS = config.polymarketWalletAddress;
|
|
356
|
+
// ════════════════════════════════════════════════════════════════════════════
|
|
357
|
+
// Step 5: Tavily
|
|
322
358
|
// ════════════════════════════════════════════════════════════════════════════
|
|
323
|
-
console.log(` ${bold('Step
|
|
359
|
+
console.log(` ${bold('Step 5: News Search (optional)')}`);
|
|
324
360
|
blank();
|
|
325
361
|
const existingTavily = process.env.TAVILY_API_KEY || config.tavilyKey;
|
|
326
362
|
if (existingTavily) {
|
|
@@ -346,7 +382,7 @@ async function runWizard() {
|
|
|
346
382
|
// Step 5: Trading
|
|
347
383
|
// ════════════════════════════════════════════════════════════════════════════
|
|
348
384
|
if (config.kalshiKeyId) {
|
|
349
|
-
console.log(` ${bold('Step
|
|
385
|
+
console.log(` ${bold('Step 6: Trading (optional)')}`);
|
|
350
386
|
blank();
|
|
351
387
|
info('Warning: enabling this unlocks sf buy / sf sell / sf cancel.');
|
|
352
388
|
info('Your Kalshi API key must have read+write permissions.');
|
|
@@ -493,7 +529,36 @@ async function promptForTavily() {
|
|
|
493
529
|
blank();
|
|
494
530
|
return answer;
|
|
495
531
|
}
|
|
496
|
-
|
|
532
|
+
async function promptForPolymarket(config) {
|
|
533
|
+
info('Connect Polymarket to view positions and scan orderbooks.');
|
|
534
|
+
info('Your Polygon wallet address is needed (starts with 0x...).');
|
|
535
|
+
info(`Find it at ${cyan('https://polymarket.com')} → Settings → Profile.`);
|
|
536
|
+
info('Press Enter to skip:');
|
|
537
|
+
blank();
|
|
538
|
+
const walletAddress = await prompt(' Wallet address > ');
|
|
539
|
+
if (!walletAddress) {
|
|
540
|
+
info(dim('Skipped.'));
|
|
541
|
+
blank();
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
if (!walletAddress.startsWith('0x') || walletAddress.length < 40) {
|
|
545
|
+
fail('Invalid wallet address (must start with 0x and be 42 characters)');
|
|
546
|
+
info(dim('Saved anyway. You can fix it later with sf setup.'));
|
|
547
|
+
}
|
|
548
|
+
else {
|
|
549
|
+
ok(`Wallet: ${mask(walletAddress)}`);
|
|
550
|
+
}
|
|
551
|
+
config.polymarketWalletAddress = walletAddress;
|
|
552
|
+
// Optionally configure private key for future trading
|
|
553
|
+
info(dim('Private key (for future trading) — press Enter to skip:'));
|
|
554
|
+
const keyPath = await prompt(' Key path > ');
|
|
555
|
+
if (keyPath) {
|
|
556
|
+
config.polymarketPrivateKeyPath = keyPath;
|
|
557
|
+
ok(`Private key path: ${dim(keyPath)}`);
|
|
558
|
+
}
|
|
559
|
+
blank();
|
|
560
|
+
}
|
|
561
|
+
// ─── Step 7: Thesis ──────────────────────────────────────────────────────────
|
|
497
562
|
async function handleThesisStep(config) {
|
|
498
563
|
try {
|
|
499
564
|
const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
|
|
@@ -501,7 +566,7 @@ async function handleThesisStep(config) {
|
|
|
501
566
|
const theses = data.theses || [];
|
|
502
567
|
const activeTheses = theses.filter((t) => t.status === 'active');
|
|
503
568
|
if (activeTheses.length > 0) {
|
|
504
|
-
console.log(` ${bold('Step
|
|
569
|
+
console.log(` ${bold('Step 7: Theses')}`);
|
|
505
570
|
blank();
|
|
506
571
|
ok(`Found ${activeTheses.length} active thesis(es):`);
|
|
507
572
|
for (const t of activeTheses.slice(0, 5)) {
|
|
@@ -542,7 +607,7 @@ async function handleThesisStep(config) {
|
|
|
542
607
|
return;
|
|
543
608
|
}
|
|
544
609
|
// No theses — offer to create one
|
|
545
|
-
console.log(` ${bold('Step
|
|
610
|
+
console.log(` ${bold('Step 7: Create Your First Thesis')}`);
|
|
546
611
|
blank();
|
|
547
612
|
info('A thesis is your core market conviction. The system builds a causal model');
|
|
548
613
|
info('from it, then continuously scans prediction markets for mispriced contracts.');
|
package/dist/config.d.ts
CHANGED
package/dist/config.js
CHANGED
|
@@ -50,6 +50,8 @@ function loadConfig() {
|
|
|
50
50
|
openrouterKey: process.env.OPENROUTER_API_KEY || file.openrouterKey,
|
|
51
51
|
kalshiKeyId: process.env.KALSHI_API_KEY_ID || file.kalshiKeyId,
|
|
52
52
|
kalshiPrivateKeyPath: process.env.KALSHI_PRIVATE_KEY_PATH || file.kalshiPrivateKeyPath,
|
|
53
|
+
polymarketWalletAddress: process.env.POLYMARKET_WALLET_ADDRESS || file.polymarketWalletAddress,
|
|
54
|
+
polymarketPrivateKeyPath: process.env.POLYMARKET_PRIVATE_KEY_PATH || file.polymarketPrivateKeyPath,
|
|
53
55
|
tavilyKey: process.env.TAVILY_API_KEY || file.tavilyKey,
|
|
54
56
|
model: process.env.SF_MODEL || file.model || DEFAULT_MODEL,
|
|
55
57
|
tradingEnabled: file.tradingEnabled || false,
|
|
@@ -101,6 +103,12 @@ function applyConfig() {
|
|
|
101
103
|
if (!process.env.KALSHI_PRIVATE_KEY_PATH && file.kalshiPrivateKeyPath) {
|
|
102
104
|
process.env.KALSHI_PRIVATE_KEY_PATH = file.kalshiPrivateKeyPath;
|
|
103
105
|
}
|
|
106
|
+
if (!process.env.POLYMARKET_WALLET_ADDRESS && file.polymarketWalletAddress) {
|
|
107
|
+
process.env.POLYMARKET_WALLET_ADDRESS = file.polymarketWalletAddress;
|
|
108
|
+
}
|
|
109
|
+
if (!process.env.POLYMARKET_PRIVATE_KEY_PATH && file.polymarketPrivateKeyPath) {
|
|
110
|
+
process.env.POLYMARKET_PRIVATE_KEY_PATH = file.polymarketPrivateKeyPath;
|
|
111
|
+
}
|
|
104
112
|
if (!process.env.TAVILY_API_KEY && file.tavilyKey) {
|
|
105
113
|
process.env.TAVILY_API_KEY = file.tavilyKey;
|
|
106
114
|
}
|
package/dist/index.js
CHANGED
|
@@ -52,18 +52,84 @@ const announcements_js_1 = require("./commands/announcements.js");
|
|
|
52
52
|
const history_js_1 = require("./commands/history.js");
|
|
53
53
|
const performance_js_1 = require("./commands/performance.js");
|
|
54
54
|
const liquidity_js_1 = require("./commands/liquidity.js");
|
|
55
|
+
const book_js_1 = require("./commands/book.js");
|
|
55
56
|
const telegram_js_1 = require("./commands/telegram.js");
|
|
56
57
|
const utils_js_1 = require("./utils.js");
|
|
57
58
|
// ── Apply ~/.sf/config.json to process.env BEFORE any command ────────────────
|
|
58
59
|
// This means client.ts, kalshi.ts, agent.ts keep reading process.env and just work.
|
|
59
60
|
(0, config_js_1.applyConfig)();
|
|
60
61
|
const program = new commander_1.Command();
|
|
62
|
+
const GROUPED_HELP = `
|
|
63
|
+
\x1b[1mSimpleFunctions CLI\x1b[22m — prediction market thesis agent
|
|
64
|
+
|
|
65
|
+
\x1b[1mUsage:\x1b[22m sf <command> [options]
|
|
66
|
+
sf <command> --help for detailed options
|
|
67
|
+
|
|
68
|
+
\x1b[1mSetup\x1b[22m
|
|
69
|
+
\x1b[36msetup\x1b[39m Interactive config wizard
|
|
70
|
+
\x1b[36msetup --check\x1b[39m Show config status
|
|
71
|
+
\x1b[36msetup --polymarket\x1b[39m Configure Polymarket wallet
|
|
72
|
+
|
|
73
|
+
\x1b[1mThesis\x1b[22m
|
|
74
|
+
\x1b[36mlist\x1b[39m List all theses
|
|
75
|
+
\x1b[36mget\x1b[39m <id> Full thesis details
|
|
76
|
+
\x1b[36mcontext\x1b[39m <id> [--json] Thesis snapshot \x1b[2m(primary for agents)\x1b[22m
|
|
77
|
+
\x1b[36mcreate\x1b[39m "thesis" Create a new thesis
|
|
78
|
+
\x1b[36msignal\x1b[39m <id> "content" Inject a signal
|
|
79
|
+
\x1b[36mevaluate\x1b[39m <id> Trigger deep evaluation
|
|
80
|
+
\x1b[36mpublish\x1b[39m / \x1b[36munpublish\x1b[39m <id> Manage public visibility
|
|
81
|
+
|
|
82
|
+
\x1b[1mMarkets\x1b[22m
|
|
83
|
+
\x1b[36mscan\x1b[39m "keywords" Search Kalshi + Polymarket
|
|
84
|
+
\x1b[36mscan\x1b[39m --series TICKER Browse a Kalshi series
|
|
85
|
+
\x1b[36medges\x1b[39m [--json] Top edges across all theses
|
|
86
|
+
\x1b[36mwhatif\x1b[39m <id> What-if scenario analysis
|
|
87
|
+
\x1b[36mliquidity\x1b[39m [topic] Orderbook liquidity scanner
|
|
88
|
+
\x1b[36mbook\x1b[39m <ticker> [ticker2...] Orderbook depth for specific markets
|
|
89
|
+
\x1b[36mexplore\x1b[39m [slug] Browse public theses
|
|
90
|
+
\x1b[36mforecast\x1b[39m <event> Market distribution (P50/P75/P90)
|
|
91
|
+
|
|
92
|
+
\x1b[1mPortfolio\x1b[22m
|
|
93
|
+
\x1b[36mpositions\x1b[39m Kalshi + Polymarket positions
|
|
94
|
+
\x1b[36mbalance\x1b[39m Account balance
|
|
95
|
+
\x1b[36morders\x1b[39m Resting orders
|
|
96
|
+
\x1b[36mfills\x1b[39m Recent trade fills
|
|
97
|
+
\x1b[36msettlements\x1b[39m Settled contracts with P&L
|
|
98
|
+
\x1b[36mperformance\x1b[39m P&L over time
|
|
99
|
+
\x1b[36mdashboard\x1b[39m Interactive TUI overview
|
|
100
|
+
|
|
101
|
+
\x1b[1mTrading\x1b[22m \x1b[2m(requires sf setup --enable-trading)\x1b[22m
|
|
102
|
+
\x1b[36mbuy\x1b[39m <ticker> <qty> Buy contracts
|
|
103
|
+
\x1b[36msell\x1b[39m <ticker> <qty> Sell contracts
|
|
104
|
+
\x1b[36mcancel\x1b[39m [orderId] Cancel order(s)
|
|
105
|
+
\x1b[36mrfq\x1b[39m <ticker> <qty> Request for quote
|
|
106
|
+
|
|
107
|
+
\x1b[1mInteractive\x1b[22m
|
|
108
|
+
\x1b[36magent\x1b[39m [id] Agent with natural language + tools
|
|
109
|
+
\x1b[36mtelegram\x1b[39m Telegram bot for monitoring
|
|
110
|
+
|
|
111
|
+
\x1b[1mInfo\x1b[22m
|
|
112
|
+
\x1b[36mfeed\x1b[39m Evaluation history stream
|
|
113
|
+
\x1b[36mmilestones\x1b[39m Upcoming Kalshi events
|
|
114
|
+
\x1b[36mschedule\x1b[39m Exchange status
|
|
115
|
+
\x1b[36mannouncements\x1b[39m Exchange announcements
|
|
116
|
+
\x1b[36mhistory\x1b[39m <ticker> Historical market data
|
|
117
|
+
`;
|
|
61
118
|
program
|
|
62
119
|
.name('sf')
|
|
63
120
|
.description('SimpleFunctions CLI — prediction market thesis agent')
|
|
64
121
|
.version('0.1.0')
|
|
65
122
|
.option('--api-key <key>', 'API key (or set SF_API_KEY env var)')
|
|
66
|
-
.option('--api-url <url>', 'API base URL (or set SF_API_URL env var)')
|
|
123
|
+
.option('--api-url <url>', 'API base URL (or set SF_API_URL env var)')
|
|
124
|
+
.configureHelp({
|
|
125
|
+
formatHelp: (cmd, helper) => {
|
|
126
|
+
// For subcommands, use default help
|
|
127
|
+
if (cmd.parent)
|
|
128
|
+
return helper.formatHelp(cmd, helper);
|
|
129
|
+
// For main program, show grouped help
|
|
130
|
+
return GROUPED_HELP;
|
|
131
|
+
},
|
|
132
|
+
});
|
|
67
133
|
// ── Pre-action guard: check configuration ────────────────────────────────────
|
|
68
134
|
const NO_CONFIG_COMMANDS = new Set(['setup', 'help', 'scan', 'explore', 'milestones', 'forecast', 'settlements', 'balance', 'orders', 'fills', 'schedule', 'announcements', 'history', 'liquidity']);
|
|
69
135
|
program.hook('preAction', (thisCommand, actionCommand) => {
|
|
@@ -91,8 +157,9 @@ program
|
|
|
91
157
|
.option('--enable-trading', 'Enable trading (sf buy/sell/cancel)')
|
|
92
158
|
.option('--disable-trading', 'Disable trading')
|
|
93
159
|
.option('--kalshi', 'Reconfigure Kalshi API credentials')
|
|
160
|
+
.option('--polymarket', 'Reconfigure Polymarket wallet address')
|
|
94
161
|
.action(async (opts) => {
|
|
95
|
-
await run(() => (0, setup_js_1.setupCommand)({ check: opts.check, reset: opts.reset, key: opts.key, enableTrading: opts.enableTrading, disableTrading: opts.disableTrading, kalshi: opts.kalshi }));
|
|
162
|
+
await run(() => (0, setup_js_1.setupCommand)({ check: opts.check, reset: opts.reset, key: opts.key, enableTrading: opts.enableTrading, disableTrading: opts.disableTrading, kalshi: opts.kalshi, polymarket: opts.polymarket }));
|
|
96
163
|
});
|
|
97
164
|
// ── sf list ──────────────────────────────────────────────────────────────────
|
|
98
165
|
program
|
|
@@ -149,9 +216,10 @@ program
|
|
|
149
216
|
// ── sf scan [query] ───────────────────────────────────────────────────────────
|
|
150
217
|
program
|
|
151
218
|
.command('scan [query]')
|
|
152
|
-
.description('Explore Kalshi
|
|
219
|
+
.description('Explore Kalshi + Polymarket markets')
|
|
153
220
|
.option('--series <ticker>', 'List events + markets for a series (e.g. KXWTIMAX)')
|
|
154
221
|
.option('--market <ticker>', 'Get single market detail (e.g. KXWTIMAX-26DEC31-T140)')
|
|
222
|
+
.option('--venue <venue>', 'Filter by venue: kalshi, polymarket, or all (default: all)')
|
|
155
223
|
.option('--json', 'Output raw JSON')
|
|
156
224
|
.action(async (query, opts, cmd) => {
|
|
157
225
|
const g = cmd.optsWithGlobals();
|
|
@@ -163,6 +231,7 @@ program
|
|
|
163
231
|
await run(() => (0, scan_js_1.scanCommand)(q, {
|
|
164
232
|
series: opts.series,
|
|
165
233
|
market: opts.market,
|
|
234
|
+
venue: opts.venue,
|
|
166
235
|
json: opts.json,
|
|
167
236
|
apiKey: g.apiKey,
|
|
168
237
|
apiUrl: g.apiUrl,
|
|
@@ -402,14 +471,29 @@ program
|
|
|
402
471
|
});
|
|
403
472
|
// ── sf liquidity ─────────────────────────────────────────────────────────────
|
|
404
473
|
program
|
|
405
|
-
.command('liquidity')
|
|
406
|
-
.description('Market liquidity scanner
|
|
407
|
-
.option('--
|
|
474
|
+
.command('liquidity [topic]')
|
|
475
|
+
.description('Market liquidity scanner — run without args to see topics')
|
|
476
|
+
.option('--all', 'Scan all topics')
|
|
477
|
+
.option('--venue <venue>', 'Filter venue: kalshi, polymarket, all (default: all)')
|
|
408
478
|
.option('--horizon <horizon>', 'Filter horizon (weekly, monthly, long-term)')
|
|
409
479
|
.option('--min-depth <depth>', 'Minimum bid+ask depth', parseInt)
|
|
410
480
|
.option('--json', 'JSON output')
|
|
411
|
-
.action(async (opts) => {
|
|
412
|
-
await run(() => (0, liquidity_js_1.liquidityCommand)(opts));
|
|
481
|
+
.action(async (topic, opts) => {
|
|
482
|
+
await run(() => (0, liquidity_js_1.liquidityCommand)({ ...opts, topic }));
|
|
483
|
+
});
|
|
484
|
+
// ── sf book ──────────────────────────────────────────────────────────────────
|
|
485
|
+
program
|
|
486
|
+
.command('book [tickers...]')
|
|
487
|
+
.description('Orderbook depth, spread, and liquidity for specific markets')
|
|
488
|
+
.option('--poly <query>', 'Search Polymarket markets by keyword')
|
|
489
|
+
.option('--history', 'Include 7-day price history sparkline')
|
|
490
|
+
.option('--json', 'JSON output')
|
|
491
|
+
.action(async (tickers, opts) => {
|
|
492
|
+
if (tickers.length === 0 && !opts.poly) {
|
|
493
|
+
console.error('Usage: sf book <ticker> [ticker2...] OR sf book --poly "oil price"');
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
await run(() => (0, book_js_1.bookCommand)(tickers, opts));
|
|
413
497
|
});
|
|
414
498
|
// ── sf telegram ──────────────────────────────────────────────────────────────
|
|
415
499
|
program
|