@spfunctions/cli 1.7.8 → 1.7.11
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/context.js +107 -53
- package/dist/commands/delta.d.ts +16 -0
- package/dist/commands/delta.js +115 -0
- package/dist/commands/edges.d.ts +4 -0
- package/dist/commands/edges.js +38 -12
- package/dist/commands/query.js +4 -2
- package/dist/commands/scan.js +28 -59
- package/dist/commands/trade.js +14 -3
- package/dist/index.js +25 -3
- package/package.json +1 -1
package/dist/commands/context.js
CHANGED
|
@@ -4,10 +4,11 @@ exports.contextCommand = contextCommand;
|
|
|
4
4
|
const client_js_1 = require("../client.js");
|
|
5
5
|
const kalshi_js_1 = require("../kalshi.js");
|
|
6
6
|
const utils_js_1 = require("../utils.js");
|
|
7
|
+
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
7
8
|
async function contextCommand(id, opts) {
|
|
8
|
-
// No thesis ID → global market
|
|
9
|
+
// ── Mode 1: No thesis ID → global market intelligence ─────────────────────
|
|
9
10
|
if (!id) {
|
|
10
|
-
const res = await fetch(
|
|
11
|
+
const res = await fetch(`${opts.apiUrl || SF_API_URL}/api/public/context`);
|
|
11
12
|
if (!res.ok) {
|
|
12
13
|
console.error(` Error: ${res.status} ${await res.text()}`);
|
|
13
14
|
return;
|
|
@@ -17,63 +18,103 @@ async function contextCommand(id, opts) {
|
|
|
17
18
|
console.log(JSON.stringify(data, null, 2));
|
|
18
19
|
return;
|
|
19
20
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
const scanTime = data.scannedAt ? (0, utils_js_1.shortDate)(data.scannedAt) : 'no scan yet';
|
|
22
|
+
console.log(`\n${utils_js_1.c.bold}Markets${utils_js_1.c.reset} ${utils_js_1.c.dim}scan: ${scanTime}${utils_js_1.c.reset}\n`);
|
|
23
|
+
// ── Traditional markets ticker bar ──────────────────────────────────────
|
|
24
|
+
const trad = data.traditional || [];
|
|
25
|
+
if (trad.length > 0) {
|
|
26
|
+
const line = trad.map((m) => {
|
|
27
|
+
const ch = m.changePct || 0;
|
|
28
|
+
const color = ch > 0 ? utils_js_1.c.green : ch < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
29
|
+
const sign = ch > 0 ? '+' : '';
|
|
30
|
+
return `${utils_js_1.c.bold}${m.symbol}${utils_js_1.c.reset} ${m.price} ${color}${sign}${ch.toFixed(1)}%${utils_js_1.c.reset}`;
|
|
31
|
+
}).join(' ');
|
|
32
|
+
console.log(` ${line}`);
|
|
33
|
+
console.log();
|
|
34
|
+
}
|
|
35
|
+
// ── Movers ─────────────────────────────────────────────────────────────
|
|
36
|
+
const movers = data.movers || [];
|
|
37
|
+
if (movers.length > 0) {
|
|
38
|
+
console.log(`${utils_js_1.c.bold}Movers (24h)${utils_js_1.c.reset}`);
|
|
39
|
+
for (const m of movers.slice(0, 8)) {
|
|
24
40
|
const venue = m.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
|
|
25
|
-
const
|
|
26
|
-
|
|
41
|
+
const ch = m.change24h || 0;
|
|
42
|
+
const chColor = ch > 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
43
|
+
const chStr = `${chColor}${ch > 0 ? '+' : ''}${ch}¢${utils_js_1.c.reset}`;
|
|
44
|
+
const cat = m.category ? `${utils_js_1.c.dim}[${m.category}]${utils_js_1.c.reset}` : '';
|
|
45
|
+
console.log(` ${venue} ${(0, utils_js_1.rpad)(`${m.price}¢`, 5)} ${(0, utils_js_1.rpad)(chStr, 16)} ${(0, utils_js_1.trunc)(m.title, 50)} ${cat}`);
|
|
46
|
+
// Actionable: show command to dig deeper
|
|
47
|
+
if (m.venue === 'kalshi' && m.ticker) {
|
|
48
|
+
console.log(` ${utils_js_1.c.dim}→ sf book ${m.ticker}${utils_js_1.c.reset}`);
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
console.log(` ${utils_js_1.c.dim}→ sf query "${extractQuery(m.title)}"${utils_js_1.c.reset}`);
|
|
52
|
+
}
|
|
27
53
|
}
|
|
28
54
|
console.log();
|
|
29
55
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
56
|
+
// ── Liquid markets ─────────────────────────────────────────────────────
|
|
57
|
+
const liquid = data.liquid || [];
|
|
58
|
+
if (liquid.length > 0) {
|
|
59
|
+
console.log(`${utils_js_1.c.bold}Most Liquid${utils_js_1.c.reset}`);
|
|
60
|
+
for (const m of liquid.slice(0, 6)) {
|
|
33
61
|
const venue = m.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
|
|
34
|
-
|
|
35
|
-
console.log(` ${venue} ${String(m.price).padStart(3)}¢ ${ch.padStart(16)} ${m.title.slice(0, 55)}`);
|
|
62
|
+
console.log(` ${venue} ${(0, utils_js_1.rpad)(`${m.price}¢`, 5)} spread ${m.spread}¢ vol ${(0, utils_js_1.vol)(Math.round(m.volume24h))} ${(0, utils_js_1.trunc)(m.title, 45)}`);
|
|
36
63
|
}
|
|
37
64
|
console.log();
|
|
38
65
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
66
|
+
// ── Categories ─────────────────────────────────────────────────────────
|
|
67
|
+
const cats = data.categories || [];
|
|
68
|
+
if (cats.length > 0) {
|
|
69
|
+
console.log(`${utils_js_1.c.bold}Categories${utils_js_1.c.reset}`);
|
|
70
|
+
for (const cat of cats.slice(0, 8)) {
|
|
71
|
+
const mover = cat.topMover;
|
|
72
|
+
const moverStr = mover
|
|
73
|
+
? `${utils_js_1.c.dim}top: ${(0, utils_js_1.trunc)(mover.title, 30)} ${mover.change24h > 0 ? utils_js_1.c.green + '+' : utils_js_1.c.red}${mover.change24h}¢${utils_js_1.c.reset}`
|
|
74
|
+
: '';
|
|
75
|
+
console.log(` ${(0, utils_js_1.pad)(cat.name, 14)} ${(0, utils_js_1.rpad)(String(cat.marketCount) + ' mkts', 10)} vol ${(0, utils_js_1.vol)(cat.totalVolume24h)} ${moverStr}`);
|
|
43
76
|
}
|
|
44
77
|
console.log();
|
|
45
78
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
79
|
+
// ── Thesis edges (from public theses) ──────────────────────────────────
|
|
80
|
+
const edges = data.edges || [];
|
|
81
|
+
if (edges.length > 0) {
|
|
82
|
+
console.log(`${utils_js_1.c.bold}Thesis Edges${utils_js_1.c.reset}`);
|
|
83
|
+
for (const e of edges.slice(0, 6)) {
|
|
84
|
+
const venue = e.venue === 'kalshi' ? `${utils_js_1.c.cyan}K${utils_js_1.c.reset}` : `${utils_js_1.c.magenta}P${utils_js_1.c.reset}`;
|
|
85
|
+
const edgeColor = e.edge > 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
86
|
+
console.log(` ${venue} ${(0, utils_js_1.rpad)(`${e.price}¢`, 5)} ${edgeColor}edge ${e.edge > 0 ? '+' : ''}${e.edge}¢${utils_js_1.c.reset} ${(0, utils_js_1.trunc)(e.title, 40)} ${utils_js_1.c.dim}${e.thesisSlug || ''}${utils_js_1.c.reset}`);
|
|
51
87
|
}
|
|
52
88
|
console.log();
|
|
53
89
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
90
|
+
// ── Recent evaluations ─────────────────────────────────────────────────
|
|
91
|
+
const signals = data.signals || [];
|
|
92
|
+
if (signals.length > 0) {
|
|
93
|
+
console.log(`${utils_js_1.c.bold}Recent Evaluations${utils_js_1.c.reset}`);
|
|
94
|
+
for (const s of signals.slice(0, 5)) {
|
|
95
|
+
const d = s.confidenceDelta;
|
|
96
|
+
const dStr = d > 0 ? `${utils_js_1.c.green}+${d}%${utils_js_1.c.reset}` : d < 0 ? `${utils_js_1.c.red}${d}%${utils_js_1.c.reset}` : `${utils_js_1.c.dim}0%${utils_js_1.c.reset}`;
|
|
97
|
+
const ago = timeAgo(s.evaluatedAt);
|
|
98
|
+
console.log(` ${(0, utils_js_1.rpad)(ago, 5)} ${(0, utils_js_1.rpad)(s.thesisSlug || '?', 18)} ${(0, utils_js_1.rpad)(dStr, 14)} ${(0, utils_js_1.trunc)(s.summary, 50)}`);
|
|
60
99
|
}
|
|
61
100
|
console.log();
|
|
62
101
|
}
|
|
63
|
-
|
|
102
|
+
// ── Actions ────────────────────────────────────────────────────────────
|
|
103
|
+
console.log(`${utils_js_1.c.dim}${'─'.repeat(60)}${utils_js_1.c.reset}`);
|
|
104
|
+
console.log(` ${utils_js_1.c.cyan}sf query${utils_js_1.c.reset} "topic" ${utils_js_1.c.dim}ask anything${utils_js_1.c.reset}`);
|
|
105
|
+
console.log(` ${utils_js_1.c.cyan}sf scan${utils_js_1.c.reset} "keywords" ${utils_js_1.c.dim}search markets${utils_js_1.c.reset}`);
|
|
106
|
+
console.log(` ${utils_js_1.c.cyan}sf explore${utils_js_1.c.reset} ${utils_js_1.c.dim}public theses${utils_js_1.c.reset}`);
|
|
107
|
+
console.log(` ${utils_js_1.c.cyan}sf create${utils_js_1.c.reset} "thesis" ${utils_js_1.c.dim}start monitoring${utils_js_1.c.reset}`);
|
|
108
|
+
console.log();
|
|
64
109
|
return;
|
|
65
110
|
}
|
|
111
|
+
// ── Mode 2: Thesis-specific context (requires API key) ────────────────────
|
|
66
112
|
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
67
113
|
const ctx = await client.getContext(id);
|
|
68
114
|
if (opts.json) {
|
|
69
115
|
console.log(JSON.stringify(ctx, null, 2));
|
|
70
116
|
return;
|
|
71
117
|
}
|
|
72
|
-
// Context API shape:
|
|
73
|
-
// { thesisId, thesis, title, status, confidence (number|null),
|
|
74
|
-
// causalTree: { nodes: FlatNode[] }, edges: Edge[], edgeMeta,
|
|
75
|
-
// lastEvaluation: { summary, confidenceDelta, ... }, updatedAt, ... }
|
|
76
|
-
// Thesis header
|
|
77
118
|
console.log(`\n${utils_js_1.c.bold}Thesis:${utils_js_1.c.reset} ${ctx.thesis || ctx.rawThesis || '(unknown)'}`);
|
|
78
119
|
const confStr = ctx.confidence !== null && ctx.confidence !== undefined ? (0, utils_js_1.pct)(ctx.confidence) : '-';
|
|
79
120
|
const confDelta = ctx.lastEvaluation?.confidenceDelta;
|
|
@@ -81,7 +122,7 @@ async function contextCommand(id, opts) {
|
|
|
81
122
|
console.log(`${utils_js_1.c.bold}Confidence:${utils_js_1.c.reset} ${confStr}${deltaStr}`);
|
|
82
123
|
console.log(`${utils_js_1.c.bold}Status:${utils_js_1.c.reset} ${ctx.status}`);
|
|
83
124
|
console.log(`${utils_js_1.c.bold}Last Updated:${utils_js_1.c.reset} ${(0, utils_js_1.shortDate)(ctx.updatedAt)}`);
|
|
84
|
-
// Causal tree nodes
|
|
125
|
+
// Causal tree nodes
|
|
85
126
|
const nodes = ctx.causalTree?.nodes;
|
|
86
127
|
if (nodes && nodes.length > 0) {
|
|
87
128
|
(0, utils_js_1.header)('Causal Tree');
|
|
@@ -92,53 +133,45 @@ async function contextCommand(id, opts) {
|
|
|
92
133
|
console.log(`${indent}${utils_js_1.c.cyan}${node.id}${utils_js_1.c.reset} ${(0, utils_js_1.pad)(label, 40)} ${(0, utils_js_1.rpad)(prob, 5)}`);
|
|
93
134
|
}
|
|
94
135
|
}
|
|
95
|
-
// Fetch positions if Kalshi is configured
|
|
136
|
+
// Fetch positions if Kalshi is configured
|
|
96
137
|
let positions = null;
|
|
97
138
|
if ((0, kalshi_js_1.isKalshiConfigured)()) {
|
|
98
139
|
try {
|
|
99
140
|
positions = await (0, kalshi_js_1.getPositions)();
|
|
100
141
|
}
|
|
101
|
-
catch {
|
|
102
|
-
// silently skip — positions are optional
|
|
103
|
-
}
|
|
142
|
+
catch { /* optional */ }
|
|
104
143
|
}
|
|
105
144
|
const posMap = new Map();
|
|
106
145
|
if (positions) {
|
|
107
|
-
for (const p of positions)
|
|
146
|
+
for (const p of positions)
|
|
108
147
|
posMap.set(p.ticker, p);
|
|
109
|
-
}
|
|
110
148
|
}
|
|
111
|
-
// Top edges
|
|
149
|
+
// Top edges
|
|
112
150
|
const edges = ctx.edges;
|
|
113
151
|
if (edges && edges.length > 0) {
|
|
114
|
-
(0, utils_js_1.header)('Top Edges
|
|
152
|
+
(0, utils_js_1.header)('Top Edges');
|
|
115
153
|
const sorted = [...edges].sort((a, b) => Math.abs(b.edge ?? b.edgeSize ?? 0) - Math.abs(a.edge ?? a.edgeSize ?? 0));
|
|
116
154
|
for (const edge of sorted.slice(0, 10)) {
|
|
117
155
|
const edgeSize = edge.edge ?? edge.edgeSize ?? 0;
|
|
118
156
|
const edgeColor = edgeSize > 10 ? utils_js_1.c.green : edgeSize > 0 ? utils_js_1.c.yellow : utils_js_1.c.red;
|
|
119
157
|
const mktPrice = edge.marketPrice ?? edge.currentPrice ?? 0;
|
|
120
158
|
const title = edge.market || edge.marketTitle || edge.marketId || '?';
|
|
121
|
-
// Orderbook info (if enriched by rescan)
|
|
122
159
|
const ob = edge.orderbook;
|
|
123
160
|
const obStr = ob ? ` ${utils_js_1.c.dim}spread ${ob.spread}¢ ${ob.liquidityScore}${utils_js_1.c.reset}` : '';
|
|
124
|
-
// Position overlay (if user has Kalshi positions)
|
|
125
161
|
const pos = posMap.get(edge.marketId);
|
|
126
162
|
let posStr = '';
|
|
127
163
|
if (pos) {
|
|
128
164
|
const pnl = pos.unrealized_pnl || 0;
|
|
129
165
|
const pnlColor = pnl > 0 ? utils_js_1.c.green : pnl < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
130
166
|
const pnlFmt = pnl >= 0 ? `+$${(pnl / 100).toFixed(0)}` : `-$${(Math.abs(pnl) / 100).toFixed(0)}`;
|
|
131
|
-
posStr = ` ${utils_js_1.c.cyan}← ${pos.quantity}
|
|
167
|
+
posStr = ` ${utils_js_1.c.cyan}← ${pos.quantity}@${pos.average_price_paid}¢ ${pnlColor}${pnlFmt}${utils_js_1.c.reset}`;
|
|
132
168
|
}
|
|
133
|
-
console.log(` ${(0, utils_js_1.pad)(title, 35)}` +
|
|
134
|
-
` ${(0, utils_js_1.rpad)(mktPrice.toFixed(0) + '¢', 5)}` +
|
|
169
|
+
console.log(` ${(0, utils_js_1.pad)(title, 35)} ${(0, utils_js_1.rpad)(mktPrice.toFixed(0) + '¢', 5)}` +
|
|
135
170
|
` ${edgeColor}edge ${edgeSize > 0 ? '+' : ''}${edgeSize.toFixed(1)}${utils_js_1.c.reset}` +
|
|
136
|
-
` ${utils_js_1.c.dim}${edge.venue || ''}${utils_js_1.c.reset}` +
|
|
137
|
-
obStr +
|
|
138
|
-
posStr);
|
|
171
|
+
` ${utils_js_1.c.dim}${edge.venue || ''}${utils_js_1.c.reset}` + obStr + posStr);
|
|
139
172
|
}
|
|
140
173
|
}
|
|
141
|
-
// Last evaluation
|
|
174
|
+
// Last evaluation
|
|
142
175
|
if (ctx.lastEvaluation?.summary) {
|
|
143
176
|
(0, utils_js_1.header)('Last Evaluation');
|
|
144
177
|
console.log(` ${utils_js_1.c.dim}${(0, utils_js_1.shortDate)(ctx.lastEvaluation.evaluatedAt)} | model: ${ctx.lastEvaluation.model || ''}${utils_js_1.c.reset}`);
|
|
@@ -151,9 +184,30 @@ async function contextCommand(id, opts) {
|
|
|
151
184
|
}
|
|
152
185
|
}
|
|
153
186
|
}
|
|
154
|
-
// Edge meta (last rescan time)
|
|
155
187
|
if (ctx.edgeMeta?.lastRescanAt) {
|
|
156
188
|
console.log(`\n${utils_js_1.c.dim}Last rescan: ${(0, utils_js_1.shortDate)(ctx.edgeMeta.lastRescanAt)}${utils_js_1.c.reset}`);
|
|
157
189
|
}
|
|
158
190
|
console.log('');
|
|
159
191
|
}
|
|
192
|
+
/** Extract a short search query from a market title */
|
|
193
|
+
function extractQuery(title) {
|
|
194
|
+
return title
|
|
195
|
+
.replace(/^Will\s+/i, '')
|
|
196
|
+
.replace(/\?.*$/, '')
|
|
197
|
+
.replace(/by\s+\w+\s+\d{1,2}.*$/i, '')
|
|
198
|
+
.replace(/\*\*/g, '')
|
|
199
|
+
.trim()
|
|
200
|
+
.slice(0, 40)
|
|
201
|
+
.trim();
|
|
202
|
+
}
|
|
203
|
+
/** Human-readable time ago */
|
|
204
|
+
function timeAgo(iso) {
|
|
205
|
+
const ms = Date.now() - new Date(iso).getTime();
|
|
206
|
+
const min = Math.round(ms / 60000);
|
|
207
|
+
if (min < 60)
|
|
208
|
+
return `${min}m`;
|
|
209
|
+
const hr = Math.round(min / 60);
|
|
210
|
+
if (hr < 24)
|
|
211
|
+
return `${hr}h`;
|
|
212
|
+
return `${Math.round(hr / 24)}d`;
|
|
213
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sf delta <thesisId> — Changes since a timestamp.
|
|
3
|
+
*
|
|
4
|
+
* Calls GET /api/thesis/:id/changes?since=<ts>
|
|
5
|
+
* Shows: confidence delta, new signals, node probability changes, edge movements.
|
|
6
|
+
*/
|
|
7
|
+
interface DeltaOpts {
|
|
8
|
+
since?: string;
|
|
9
|
+
hours?: string;
|
|
10
|
+
json?: boolean;
|
|
11
|
+
watch?: boolean;
|
|
12
|
+
apiKey?: string;
|
|
13
|
+
apiUrl?: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function deltaCommand(thesisId: string, opts: DeltaOpts): Promise<void>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* sf delta <thesisId> — Changes since a timestamp.
|
|
4
|
+
*
|
|
5
|
+
* Calls GET /api/thesis/:id/changes?since=<ts>
|
|
6
|
+
* Shows: confidence delta, new signals, node probability changes, edge movements.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.deltaCommand = deltaCommand;
|
|
10
|
+
const client_js_1 = require("../client.js");
|
|
11
|
+
const utils_js_1 = require("../utils.js");
|
|
12
|
+
async function deltaCommand(thesisId, opts) {
|
|
13
|
+
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
14
|
+
// Resolve "since" — either explicit timestamp or --hours offset
|
|
15
|
+
const sinceDate = opts.since
|
|
16
|
+
? new Date(opts.since)
|
|
17
|
+
: new Date(Date.now() - (parseInt(opts.hours || '6') * 3600000));
|
|
18
|
+
if (isNaN(sinceDate.getTime())) {
|
|
19
|
+
throw new Error(`Invalid timestamp: "${opts.since}". Use ISO 8601 format (e.g., 2026-03-28T14:00:00Z)`);
|
|
20
|
+
}
|
|
21
|
+
const run = async () => {
|
|
22
|
+
const since = sinceDate.toISOString();
|
|
23
|
+
const data = await client.getChanges(thesisId, since);
|
|
24
|
+
if (opts.json) {
|
|
25
|
+
console.log(JSON.stringify(data, null, 2));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (!data.changed) {
|
|
29
|
+
console.log(`${utils_js_1.c.dim}No changes since ${sinceDate.toLocaleString()}.${utils_js_1.c.reset}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
// Header
|
|
33
|
+
const id = (0, utils_js_1.shortId)(thesisId);
|
|
34
|
+
console.log();
|
|
35
|
+
console.log(`${utils_js_1.c.bold}${utils_js_1.c.cyan}Delta: ${id}${utils_js_1.c.reset}${utils_js_1.c.dim} — since ${sinceDate.toLocaleString()}${utils_js_1.c.reset}`);
|
|
36
|
+
console.log(`${utils_js_1.c.dim}${'─'.repeat(65)}${utils_js_1.c.reset}`);
|
|
37
|
+
// Confidence change
|
|
38
|
+
if (data.confidence !== undefined) {
|
|
39
|
+
const conf = Math.round(data.confidence * 100);
|
|
40
|
+
const prev = data.previousConfidence !== undefined ? Math.round(data.previousConfidence * 100) : null;
|
|
41
|
+
const delta = prev !== null ? conf - prev : null;
|
|
42
|
+
let deltaStr = '';
|
|
43
|
+
if (delta !== null) {
|
|
44
|
+
deltaStr = delta > 0 ? `${utils_js_1.c.green} (+${delta}%)${utils_js_1.c.reset}` : delta < 0 ? `${utils_js_1.c.red} (${delta}%)${utils_js_1.c.reset}` : `${utils_js_1.c.dim} (0%)${utils_js_1.c.reset}`;
|
|
45
|
+
}
|
|
46
|
+
console.log(` Confidence: ${utils_js_1.c.bold}${conf}%${utils_js_1.c.reset}${deltaStr}`);
|
|
47
|
+
}
|
|
48
|
+
// Evaluation count
|
|
49
|
+
if (data.evaluationCount) {
|
|
50
|
+
console.log(` Evaluations: ${data.evaluationCount} cycle(s)`);
|
|
51
|
+
}
|
|
52
|
+
// Updated nodes
|
|
53
|
+
const nodes = data.updatedNodes || [];
|
|
54
|
+
if (nodes.length > 0) {
|
|
55
|
+
console.log();
|
|
56
|
+
console.log(` ${utils_js_1.c.bold}Node Changes (${nodes.length}):${utils_js_1.c.reset}`);
|
|
57
|
+
for (const n of nodes) {
|
|
58
|
+
const prob = Math.round((n.newProbability ?? n.newProb ?? 0) * 100);
|
|
59
|
+
const prev = n.previousProbability ?? n.prevProb;
|
|
60
|
+
let changeStr = '';
|
|
61
|
+
if (prev !== undefined && prev !== null) {
|
|
62
|
+
const prevPct = Math.round(prev * 100);
|
|
63
|
+
const d = prob - prevPct;
|
|
64
|
+
changeStr = d > 0 ? ` ${utils_js_1.c.green}+${d}%${utils_js_1.c.reset}` : d < 0 ? ` ${utils_js_1.c.red}${d}%${utils_js_1.c.reset}` : '';
|
|
65
|
+
}
|
|
66
|
+
const label = n.label || n.nodeId || '?';
|
|
67
|
+
console.log(` ${label.slice(0, 40).padEnd(40)} ${prob}%${changeStr}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// New signals
|
|
71
|
+
const signals = data.newSignals || [];
|
|
72
|
+
if (signals.length > 0) {
|
|
73
|
+
console.log();
|
|
74
|
+
console.log(` ${utils_js_1.c.bold}New Signals (${signals.length}):${utils_js_1.c.reset}`);
|
|
75
|
+
for (const s of signals) {
|
|
76
|
+
const type = s.type || 'signal';
|
|
77
|
+
const content = (s.content || s.title || '').replace(/\n/g, ' ').slice(0, 70);
|
|
78
|
+
console.log(` ${utils_js_1.c.dim}[${type}]${utils_js_1.c.reset} ${content}`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
// Edge changes
|
|
82
|
+
const edges = data.edgeChanges || data.edges || [];
|
|
83
|
+
if (edges.length > 0) {
|
|
84
|
+
console.log();
|
|
85
|
+
console.log(` ${utils_js_1.c.bold}Edge Movements (${edges.length}):${utils_js_1.c.reset}`);
|
|
86
|
+
for (const e of edges.slice(0, 15)) {
|
|
87
|
+
const market = (e.market || e.marketId || '').slice(0, 35).padEnd(35);
|
|
88
|
+
const edge = e.edge ?? 0;
|
|
89
|
+
const prev = e.previousEdge ?? null;
|
|
90
|
+
const edgeStr = edge > 0 ? `+${edge}` : `${edge}`;
|
|
91
|
+
let changeStr = '';
|
|
92
|
+
if (prev !== null) {
|
|
93
|
+
const d = edge - prev;
|
|
94
|
+
changeStr = d > 0 ? ` ${utils_js_1.c.green}(+${d})${utils_js_1.c.reset}` : d < 0 ? ` ${utils_js_1.c.red}(${d})${utils_js_1.c.reset}` : '';
|
|
95
|
+
}
|
|
96
|
+
const color = edge > 0 ? utils_js_1.c.green : edge < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
97
|
+
console.log(` ${market} ${color}${edgeStr}${utils_js_1.c.reset}${changeStr}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
console.log(`${utils_js_1.c.dim}${'─'.repeat(65)}${utils_js_1.c.reset}`);
|
|
101
|
+
console.log();
|
|
102
|
+
};
|
|
103
|
+
if (opts.watch) {
|
|
104
|
+
console.log(`${utils_js_1.c.dim}Watching for changes every 60s... (Ctrl+C to stop)${utils_js_1.c.reset}`);
|
|
105
|
+
// eslint-disable-next-line no-constant-condition
|
|
106
|
+
while (true) {
|
|
107
|
+
await run();
|
|
108
|
+
await new Promise(r => setTimeout(r, 60_000));
|
|
109
|
+
console.clear();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
await run();
|
|
114
|
+
}
|
|
115
|
+
}
|
package/dist/commands/edges.d.ts
CHANGED
package/dist/commands/edges.js
CHANGED
|
@@ -20,17 +20,30 @@ const utils_js_1 = require("../utils.js");
|
|
|
20
20
|
async function edgesCommand(opts) {
|
|
21
21
|
const client = new client_js_1.SFClient(opts.apiKey, opts.apiUrl);
|
|
22
22
|
const limit = parseInt(opts.limit || '20');
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
23
|
+
const minEdge = opts.minEdge ? parseInt(opts.minEdge) : 0;
|
|
24
|
+
const minLiq = opts.minLiquidity?.toLowerCase() || '';
|
|
25
|
+
const sortBy = opts.sort || 'edge';
|
|
26
|
+
// Support both letter grades (A/B/C/D) and word grades (high/medium/low)
|
|
27
|
+
const liqRank = { a: 4, high: 4, b: 3, medium: 3, c: 2, low: 2, d: 1 };
|
|
28
|
+
// ── Step 1: Fetch theses (or single thesis) ────────────────────────────────
|
|
29
|
+
let theses;
|
|
30
|
+
if (opts.thesis) {
|
|
31
|
+
// Single thesis mode — skip listing, fetch context directly
|
|
32
|
+
console.log(`${utils_js_1.c.dim}Fetching edges for thesis ${opts.thesis}...${utils_js_1.c.reset}`);
|
|
33
|
+
theses = [{ id: opts.thesis }];
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
console.log(`${utils_js_1.c.dim}Fetching theses...${utils_js_1.c.reset}`);
|
|
37
|
+
const data = await client.listTheses();
|
|
38
|
+
const rawTheses = data.theses || data;
|
|
39
|
+
theses = (Array.isArray(rawTheses) ? rawTheses : []).filter((t) => t.status === 'active');
|
|
40
|
+
}
|
|
28
41
|
if (theses.length === 0) {
|
|
29
42
|
console.log(`${utils_js_1.c.yellow}No active theses found.${utils_js_1.c.reset} Create one: sf create "your thesis"`);
|
|
30
43
|
return;
|
|
31
44
|
}
|
|
32
45
|
// ── Step 2: Fetch context for each thesis (parallel) ───────────────────────
|
|
33
|
-
console.log(`${utils_js_1.c.dim}Fetching edges from ${theses.length} theses...${utils_js_1.c.reset}`);
|
|
46
|
+
console.log(`${utils_js_1.c.dim}Fetching edges from ${theses.length} ${theses.length === 1 ? 'thesis' : 'theses'}...${utils_js_1.c.reset}`);
|
|
34
47
|
const allEdges = [];
|
|
35
48
|
const contextPromises = theses.map(async (t) => {
|
|
36
49
|
try {
|
|
@@ -76,6 +89,13 @@ async function edgesCommand(opts) {
|
|
|
76
89
|
}
|
|
77
90
|
}
|
|
78
91
|
let merged = Array.from(deduped.values());
|
|
92
|
+
// ── Step 3b: Apply filters ─────────────────────────────────────────────────
|
|
93
|
+
if (minEdge > 0) {
|
|
94
|
+
merged = merged.filter(e => Math.abs(e.edge) >= minEdge);
|
|
95
|
+
}
|
|
96
|
+
if (minLiq && liqRank[minLiq]) {
|
|
97
|
+
merged = merged.filter(e => e.liquidityScore && (liqRank[e.liquidityScore.toLowerCase()] || 0) >= liqRank[minLiq]);
|
|
98
|
+
}
|
|
79
99
|
// ── Step 4: Fetch positions (optional) ─────────────────────────────────────
|
|
80
100
|
let positions = null;
|
|
81
101
|
if ((0, kalshi_js_1.isKalshiConfigured)()) {
|
|
@@ -107,12 +127,18 @@ async function edgesCommand(opts) {
|
|
|
107
127
|
}
|
|
108
128
|
}
|
|
109
129
|
}
|
|
110
|
-
// ── Step 5: Sort
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
130
|
+
// ── Step 5: Sort ────────────────────────────────────────────────────────────
|
|
131
|
+
if (sortBy === 'spread') {
|
|
132
|
+
merged.sort((a, b) => (a.spread ?? 999) - (b.spread ?? 999));
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Default: sort by executableEdge (or edge) descending
|
|
136
|
+
merged.sort((a, b) => {
|
|
137
|
+
const aVal = a.executableEdge !== null ? a.executableEdge : a.edge;
|
|
138
|
+
const bVal = b.executableEdge !== null ? b.executableEdge : b.edge;
|
|
139
|
+
return Math.abs(bVal) - Math.abs(aVal);
|
|
140
|
+
});
|
|
141
|
+
}
|
|
116
142
|
// Apply limit
|
|
117
143
|
const display = merged.slice(0, limit);
|
|
118
144
|
// ── Step 6: JSON output ────────────────────────────────────────────────────
|
package/dist/commands/query.js
CHANGED
|
@@ -55,13 +55,15 @@ async function queryCommand(q, opts) {
|
|
|
55
55
|
console.log(' \x1b[1mMarkets\x1b[22m');
|
|
56
56
|
for (const m of kalshiList.slice(0, 8)) {
|
|
57
57
|
const vol = fmtVol(m.volume);
|
|
58
|
-
|
|
58
|
+
const id = m.ticker ? ` \x1b[2m${m.ticker}\x1b[22m` : '';
|
|
59
|
+
console.log(` \x1b[36mK\x1b[39m ${String(m.price).padStart(3)}¢ vol ${vol.padStart(6)} ${m.title.slice(0, 55)}${id}`);
|
|
59
60
|
}
|
|
60
61
|
if (kalshiList.length > 0 && polyList.length > 0)
|
|
61
62
|
console.log();
|
|
62
63
|
for (const m of polyList.slice(0, 8)) {
|
|
63
64
|
const vol = fmtVol(m.volume);
|
|
64
|
-
|
|
65
|
+
const id = m.slug ? ` \x1b[2m${m.slug}\x1b[22m` : '';
|
|
66
|
+
console.log(` \x1b[35mP\x1b[39m ${String(m.price).padStart(3)}¢ vol ${vol.padStart(6)} ${m.title.slice(0, 55)}${id}`);
|
|
65
67
|
}
|
|
66
68
|
console.log();
|
|
67
69
|
}
|
package/dist/commands/scan.js
CHANGED
|
@@ -4,6 +4,8 @@ exports.scanCommand = scanCommand;
|
|
|
4
4
|
const client_js_1 = require("../client.js");
|
|
5
5
|
const polymarket_js_1 = require("../polymarket.js");
|
|
6
6
|
const utils_js_1 = require("../utils.js");
|
|
7
|
+
// SF API base — scan goes through the server which holds the proxy key
|
|
8
|
+
const SF_API_URL = process.env.SF_API_URL || 'https://simplefunctions.dev';
|
|
7
9
|
async function scanCommand(query, opts) {
|
|
8
10
|
// Mode 1: --market TICKER — single market detail
|
|
9
11
|
if (opts.market) {
|
|
@@ -79,7 +81,6 @@ async function showSeries(seriesTicker, json) {
|
|
|
79
81
|
}
|
|
80
82
|
}
|
|
81
83
|
async function keywordScan(query, json, venue = 'all') {
|
|
82
|
-
const terms = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
83
84
|
// ── Polymarket search (runs in parallel with Kalshi) ───────────────────────
|
|
84
85
|
const polyPromise = (venue === 'kalshi')
|
|
85
86
|
? Promise.resolve([])
|
|
@@ -90,68 +91,36 @@ async function keywordScan(query, json, venue = 'all') {
|
|
|
90
91
|
if (venue !== 'kalshi') {
|
|
91
92
|
console.log(`${utils_js_1.c.dim}Scanning Polymarket for: "${query}"...${utils_js_1.c.reset}`);
|
|
92
93
|
}
|
|
93
|
-
|
|
94
|
-
// Score each series
|
|
95
|
-
const thesisKeywords = [
|
|
96
|
-
'oil', 'wti', 'gas', 'recession', 'gdp', 'fed', 'inflation',
|
|
97
|
-
'unemployment', 'cpi', 'interest rate', 'congress', 'election',
|
|
98
|
-
'iran', 'hormuz', 'war', 'tariff', 'trade', 'rate',
|
|
99
|
-
];
|
|
100
|
-
const matches = [];
|
|
101
|
-
for (const s of allSeries) {
|
|
102
|
-
const text = `${s.title} ${s.ticker} ${(s.tags || []).join(' ')} ${s.category}`.toLowerCase();
|
|
103
|
-
let score = 0;
|
|
104
|
-
for (const term of terms) {
|
|
105
|
-
if (text.includes(term))
|
|
106
|
-
score += 10;
|
|
107
|
-
}
|
|
108
|
-
for (const kw of thesisKeywords) {
|
|
109
|
-
if (text.includes(kw))
|
|
110
|
-
score += 5;
|
|
111
|
-
}
|
|
112
|
-
const v = parseFloat(s.volume_fp || '0');
|
|
113
|
-
if (v > 1_000_000)
|
|
114
|
-
score += 3;
|
|
115
|
-
else if (v > 100_000)
|
|
116
|
-
score += 1;
|
|
117
|
-
if (score > 0 && v > 1000)
|
|
118
|
-
matches.push({ series: s, score, volume: v });
|
|
119
|
-
}
|
|
120
|
-
// Sort by score first, then by volume as tiebreaker
|
|
121
|
-
matches.sort((a, b) => b.score - a.score || b.volume - a.volume);
|
|
122
|
-
const topSeries = matches.slice(0, 15);
|
|
123
|
-
console.log(`\n${utils_js_1.c.bold}Found ${matches.length} relevant series. Top ${topSeries.length}:${utils_js_1.c.reset}\n`);
|
|
124
|
-
for (const { series: s, volume } of topSeries) {
|
|
125
|
-
const volStr = volume >= 1_000_000 ? `$${(volume / 1_000_000).toFixed(1)}M` : volume >= 1000 ? `$${(volume / 1000).toFixed(0)}k` : `$${volume.toFixed(0)}`;
|
|
126
|
-
console.log(` ${(0, utils_js_1.rpad)(volStr, 10)} ${(0, utils_js_1.pad)(s.ticker, 25)} ${s.title}`);
|
|
127
|
-
}
|
|
128
|
-
// Fetch live markets for top 10
|
|
129
|
-
console.log(`\n${utils_js_1.c.dim}Fetching live markets...${utils_js_1.c.reset}\n`);
|
|
94
|
+
// ── Kalshi via SF API /scan (proxies to CF worker, key stays server-side)
|
|
130
95
|
const allMarkets = [];
|
|
131
|
-
|
|
96
|
+
if (venue !== 'polymarket') {
|
|
132
97
|
try {
|
|
133
|
-
const
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
98
|
+
const scanUrl = `${SF_API_URL}/api/public/scan?q=${encodeURIComponent(query)}&limit=30`;
|
|
99
|
+
const res = await fetch(scanUrl);
|
|
100
|
+
if (!res.ok)
|
|
101
|
+
throw new Error(`Proxy ${res.status}`);
|
|
102
|
+
const data = await res.json();
|
|
103
|
+
const matchedSeries = data.matchedSeries || [];
|
|
104
|
+
const markets = data.markets || [];
|
|
105
|
+
console.log(`\n${utils_js_1.c.bold}Matched ${matchedSeries.length} series via proxy:${utils_js_1.c.reset}\n`);
|
|
106
|
+
for (const s of matchedSeries.slice(0, 15)) {
|
|
107
|
+
console.log(` ${(0, utils_js_1.pad)(s.ticker, 25)} score=${s.score} ${s.title || ''}`);
|
|
108
|
+
}
|
|
109
|
+
for (const m of markets) {
|
|
110
|
+
allMarkets.push({
|
|
111
|
+
venue: 'kalshi',
|
|
112
|
+
seriesTicker: m.series_ticker,
|
|
113
|
+
ticker: m.ticker,
|
|
114
|
+
title: m.title || m.exchange_title || '',
|
|
115
|
+
yesAsk: parseFloat(m.yes_ask_dollars || '0'),
|
|
116
|
+
lastPrice: parseFloat(m.last_price_dollars || '0'),
|
|
117
|
+
volume24h: parseFloat(m.volume_24h_fp || '0'),
|
|
118
|
+
liquidity: parseFloat(m.open_interest_fp || m.liquidity_dollars || '0'),
|
|
119
|
+
});
|
|
150
120
|
}
|
|
151
|
-
await new Promise(r => setTimeout(r, 150));
|
|
152
121
|
}
|
|
153
|
-
catch {
|
|
154
|
-
|
|
122
|
+
catch (err) {
|
|
123
|
+
console.warn(`${utils_js_1.c.dim}Kalshi scan failed: ${err}${utils_js_1.c.reset}`);
|
|
155
124
|
}
|
|
156
125
|
}
|
|
157
126
|
allMarkets.sort((a, b) => b.liquidity - a.liquidity);
|
package/dist/commands/trade.js
CHANGED
|
@@ -21,11 +21,22 @@ async function executeOrder(ticker, qty, action, opts) {
|
|
|
21
21
|
if (isNaN(quantity) || quantity <= 0)
|
|
22
22
|
throw new Error('Quantity must be a positive integer');
|
|
23
23
|
const side = (opts.side || 'yes');
|
|
24
|
+
if (!opts.market && !opts.price) {
|
|
25
|
+
throw new Error('Limit order requires --price <cents>. Example: sf buy TICKER 10 --price 45\nFor market orders use: sf buy TICKER 10 --market --price 99');
|
|
26
|
+
}
|
|
27
|
+
// Kalshi API always requires yes_price — even for "market" orders.
|
|
28
|
+
// For market orders without --price: auto-fetch current market price.
|
|
24
29
|
const orderType = opts.market ? 'market' : 'limit';
|
|
25
|
-
|
|
26
|
-
|
|
30
|
+
let priceCents = opts.price ? parseInt(opts.price) : undefined;
|
|
31
|
+
if (opts.market && !priceCents) {
|
|
32
|
+
console.log(`${utils_js_1.c.dim}Fetching current market price for ${ticker}...${utils_js_1.c.reset}`);
|
|
33
|
+
const livePrice = await (0, kalshi_js_1.getMarketPrice)(ticker.toUpperCase());
|
|
34
|
+
if (!livePrice) {
|
|
35
|
+
throw new Error(`Could not fetch market price for ${ticker}. Use --price <cents> to set manually.`);
|
|
36
|
+
}
|
|
37
|
+
priceCents = livePrice;
|
|
38
|
+
console.log(`${utils_js_1.c.dim}Using market price: ${livePrice}¢${utils_js_1.c.reset}`);
|
|
27
39
|
}
|
|
28
|
-
const priceCents = opts.price ? parseInt(opts.price) : undefined;
|
|
29
40
|
if (priceCents !== undefined && (priceCents < 1 || priceCents > 99)) {
|
|
30
41
|
throw new Error('Price must be 1-99 cents.');
|
|
31
42
|
}
|
package/dist/index.js
CHANGED
|
@@ -57,6 +57,7 @@ const book_js_1 = require("./commands/book.js");
|
|
|
57
57
|
const prompt_js_1 = require("./commands/prompt.js");
|
|
58
58
|
const augment_js_1 = require("./commands/augment.js");
|
|
59
59
|
const telegram_js_1 = require("./commands/telegram.js");
|
|
60
|
+
const delta_js_1 = require("./commands/delta.js");
|
|
60
61
|
const query_js_1 = require("./commands/query.js");
|
|
61
62
|
const markets_js_1 = require("./commands/markets.js");
|
|
62
63
|
const x_js_1 = require("./commands/x.js");
|
|
@@ -128,6 +129,7 @@ const GROUPED_HELP = `
|
|
|
128
129
|
|
|
129
130
|
\x1b[1mInfo\x1b[22m
|
|
130
131
|
\x1b[36mfeed\x1b[39m Evaluation history stream
|
|
132
|
+
\x1b[36mdelta\x1b[39m <id> Changes since timestamp
|
|
131
133
|
\x1b[36mmilestones\x1b[39m Upcoming Kalshi events
|
|
132
134
|
\x1b[36mschedule\x1b[39m Exchange status
|
|
133
135
|
\x1b[36mannouncements\x1b[39m Exchange announcements
|
|
@@ -359,11 +361,19 @@ program
|
|
|
359
361
|
.description('Top edges across all theses — what to trade now')
|
|
360
362
|
.option('--json', 'JSON output for agents')
|
|
361
363
|
.option('--limit <n>', 'Max edges to show', '20')
|
|
364
|
+
.option('--thesis <id>', 'Filter to a single thesis')
|
|
365
|
+
.option('--min-edge <cents>', 'Minimum absolute edge size in cents')
|
|
366
|
+
.option('--min-liquidity <grade>', 'Minimum liquidity (high/medium/low or A/B/C)')
|
|
367
|
+
.option('--sort <by>', 'Sort by: edge (default), spread')
|
|
362
368
|
.action(async (opts, cmd) => {
|
|
363
369
|
const g = cmd.optsWithGlobals();
|
|
364
370
|
await run(() => (0, edges_js_1.edgesCommand)({
|
|
365
371
|
json: opts.json,
|
|
366
372
|
limit: opts.limit,
|
|
373
|
+
thesis: opts.thesis,
|
|
374
|
+
minEdge: opts.minEdge,
|
|
375
|
+
minLiquidity: opts.minLiquidity,
|
|
376
|
+
sort: opts.sort,
|
|
367
377
|
apiKey: g.apiKey,
|
|
368
378
|
apiUrl: g.apiUrl,
|
|
369
379
|
}));
|
|
@@ -434,8 +444,8 @@ program
|
|
|
434
444
|
// ── sf milestones ────────────────────────────────────────────────────────────
|
|
435
445
|
program
|
|
436
446
|
.command('milestones')
|
|
437
|
-
.description('Upcoming events from Kalshi calendar')
|
|
438
|
-
.option('--category <cat>', 'Filter by category')
|
|
447
|
+
.description('Upcoming events from Kalshi calendar. Use --category to filter (e.g., Economics, Politics, Financials, Climate)')
|
|
448
|
+
.option('--category <cat>', 'Filter by category (e.g., Economics, Politics, Financials, Climate, Sports)')
|
|
439
449
|
.option('--thesis <id>', 'Show milestones matching thesis edges')
|
|
440
450
|
.option('--hours <n>', 'Hours ahead (default 168)', '168')
|
|
441
451
|
.option('--json', 'JSON output')
|
|
@@ -446,7 +456,7 @@ program
|
|
|
446
456
|
// ── sf forecast <eventTicker> ────────────────────────────────────────────────
|
|
447
457
|
program
|
|
448
458
|
.command('forecast <eventTicker>')
|
|
449
|
-
.description('Market distribution forecast (P50/P75/P90
|
|
459
|
+
.description('Market distribution forecast (P50/P75/P90). Use EVENT ticker (e.g., KXWTIMAX-26DEC31), not series ticker.')
|
|
450
460
|
.option('--days <n>', 'Days of history (default 7)', '7')
|
|
451
461
|
.option('--json', 'JSON output')
|
|
452
462
|
.action(async (eventTicker, opts) => {
|
|
@@ -499,6 +509,18 @@ program
|
|
|
499
509
|
const g = cmd.optsWithGlobals();
|
|
500
510
|
await run(() => (0, feed_js_1.feedCommand)({ ...opts, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
501
511
|
});
|
|
512
|
+
// ── sf delta <thesisId> ───────────────────────────────────────────────────────
|
|
513
|
+
program
|
|
514
|
+
.command('delta <thesisId>')
|
|
515
|
+
.description('Changes since a timestamp — confidence, nodes, signals, edges')
|
|
516
|
+
.option('--since <timestamp>', 'ISO 8601 start time (e.g., 2026-03-28T14:00:00Z)')
|
|
517
|
+
.option('--hours <n>', 'Hours to look back (default 6)', '6')
|
|
518
|
+
.option('--watch', 'Continuously poll every 60s')
|
|
519
|
+
.option('--json', 'JSON output')
|
|
520
|
+
.action(async (thesisId, opts, cmd) => {
|
|
521
|
+
const g = cmd.optsWithGlobals();
|
|
522
|
+
await run(() => (0, delta_js_1.deltaCommand)(thesisId, { ...opts, apiKey: g.apiKey, apiUrl: g.apiUrl }));
|
|
523
|
+
});
|
|
502
524
|
// ── sf whatif <thesisId> ──────────────────────────────────────────────────────
|
|
503
525
|
program
|
|
504
526
|
.command('whatif <thesisId>')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.11",
|
|
4
4
|
"description": "Prediction market intelligence CLI. Causal thesis model, 24/7 Kalshi/Polymarket scan, live orderbook, edge detection. Interactive agent mode with tool calling.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"sf": "./dist/index.js"
|