@spfunctions/cli 1.7.10 → 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/index.js +22 -0
- 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/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
|
}));
|
|
@@ -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"
|