@spfunctions/cli 1.4.0 → 1.4.2
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 +3 -1
- package/dist/commands/performance.js +55 -10
- package/dist/commands/scan.js +7 -5
- package/dist/kalshi.js +2 -2
- package/package.json +1 -1
package/dist/commands/agent.js
CHANGED
|
@@ -671,6 +671,8 @@ async function agentCommand(thesisId, opts) {
|
|
|
671
671
|
const matched = series
|
|
672
672
|
.filter((s) => keywords.every((kw) => (s.title || '').toLowerCase().includes(kw) ||
|
|
673
673
|
(s.ticker || '').toLowerCase().includes(kw)))
|
|
674
|
+
.filter((s) => parseFloat(s.volume_fp || '0') > 1000)
|
|
675
|
+
.sort((a, b) => parseFloat(b.volume_fp || '0') - parseFloat(a.volume_fp || '0'))
|
|
674
676
|
.slice(0, 15);
|
|
675
677
|
result = matched;
|
|
676
678
|
}
|
|
@@ -2187,7 +2189,7 @@ async function runPlainTextAgent(params) {
|
|
|
2187
2189
|
else if (p.query) {
|
|
2188
2190
|
const series = await (0, client_js_1.kalshiFetchAllSeries)();
|
|
2189
2191
|
const kws = p.query.toLowerCase().split(/\s+/);
|
|
2190
|
-
result = series.filter((s) => kws.every((k) => ((s.title || '') + (s.ticker || '')).toLowerCase().includes(k))).slice(0, 15);
|
|
2192
|
+
result = series.filter((s) => kws.every((k) => ((s.title || '') + (s.ticker || '')).toLowerCase().includes(k))).filter((s) => parseFloat(s.volume_fp || '0') > 1000).sort((a, b) => parseFloat(b.volume_fp || '0') - parseFloat(a.volume_fp || '0')).slice(0, 15);
|
|
2191
2193
|
}
|
|
2192
2194
|
else {
|
|
2193
2195
|
result = { error: 'Provide query, series, or market' };
|
|
@@ -8,10 +8,36 @@ const client_js_1 = require("../client.js");
|
|
|
8
8
|
const kalshi_js_1 = require("../kalshi.js");
|
|
9
9
|
const config_js_1 = require("../config.js");
|
|
10
10
|
const utils_js_1 = require("../utils.js");
|
|
11
|
-
/** Abbreviate ticker
|
|
11
|
+
/** Abbreviate ticker to something readable:
|
|
12
|
+
* KXWTIMAX-26DEC31-T135 → Oil 135
|
|
13
|
+
* KXRECSSNBER-26 → Recsn
|
|
14
|
+
* KXAAAGASM-26MAR31-4.40 → Gas 4.40
|
|
15
|
+
* KXINXY-26DEC31H1600-T4000 → S&P 4000
|
|
16
|
+
*/
|
|
12
17
|
function abbrevTicker(ticker) {
|
|
18
|
+
// Short topic names for performance table
|
|
19
|
+
const SHORT_TOPICS = {
|
|
20
|
+
KXWTIMAX: 'Oil', KXWTI: 'Oil', KXRECSSNBER: 'Recsn',
|
|
21
|
+
KXAAAGASM: 'Gas', KXCPI: 'CPI', KXINXY: 'S&P',
|
|
22
|
+
KXFEDDECISION: 'Fed', KXUNEMPLOYMENT: 'Unemp', KXCLOSEHORMUZ: 'Hormuz',
|
|
23
|
+
};
|
|
24
|
+
const sorted = Object.keys(SHORT_TOPICS).sort((a, b) => b.length - a.length);
|
|
25
|
+
let topic = '';
|
|
26
|
+
for (const prefix of sorted) {
|
|
27
|
+
if (ticker.startsWith(prefix)) {
|
|
28
|
+
topic = SHORT_TOPICS[prefix];
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
// Extract the meaningful suffix (strike/level)
|
|
13
33
|
const parts = ticker.split('-');
|
|
14
|
-
|
|
34
|
+
const last = parts[parts.length - 1] || '';
|
|
35
|
+
const suffix = last.startsWith('T') ? last.slice(1) : '';
|
|
36
|
+
if (topic && suffix)
|
|
37
|
+
return `${topic} ${suffix}`;
|
|
38
|
+
if (topic)
|
|
39
|
+
return topic;
|
|
40
|
+
return last || ticker.slice(0, 8);
|
|
15
41
|
}
|
|
16
42
|
/** Format date as "Mar 01" */
|
|
17
43
|
function fmtDate(d) {
|
|
@@ -48,8 +74,8 @@ async function performanceCommand(opts) {
|
|
|
48
74
|
continue;
|
|
49
75
|
const side = fill.side || 'yes';
|
|
50
76
|
const action = fill.action || 'buy';
|
|
51
|
-
const count = fill.count || 0;
|
|
52
|
-
const yesPrice = fill.
|
|
77
|
+
const count = Math.round(parseFloat(fill.count_fp || fill.count || '0'));
|
|
78
|
+
const yesPrice = Math.round(parseFloat(fill.yes_price_dollars || '0') * 100); // dollars string → cents int
|
|
53
79
|
// Determine direction: buy yes = +count, sell yes = -count
|
|
54
80
|
let delta = count;
|
|
55
81
|
if (action === 'sell')
|
|
@@ -105,7 +131,11 @@ async function performanceCommand(opts) {
|
|
|
105
131
|
const priceByDate = new Map();
|
|
106
132
|
for (const candle of (mc.candlesticks || [])) {
|
|
107
133
|
// close_dollars is a string like "0.4800"
|
|
108
|
-
|
|
134
|
+
// price object may be empty; use midpoint of yes_bid.close and yes_ask.close
|
|
135
|
+
const bidClose = parseFloat(candle.yes_bid?.close_dollars || '0');
|
|
136
|
+
const askClose = parseFloat(candle.yes_ask?.close_dollars || '0');
|
|
137
|
+
const mid = bidClose > 0 && askClose > 0 ? (bidClose + askClose) / 2 : bidClose || askClose;
|
|
138
|
+
const closeDollars = parseFloat(candle.price?.close_dollars || '0') || mid;
|
|
109
139
|
const closeCents = Math.round(closeDollars * 100);
|
|
110
140
|
const ts = candle.end_period_ts || candle.period_end_ts || candle.ts;
|
|
111
141
|
if (ts) {
|
|
@@ -151,12 +181,12 @@ async function performanceCommand(opts) {
|
|
|
151
181
|
const config = (0, config_js_1.loadConfig)();
|
|
152
182
|
const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
|
|
153
183
|
const feedData = await client.getFeed(720);
|
|
154
|
-
const feedItems = feedData?.items || feedData?.events || feedData || [];
|
|
184
|
+
const feedItems = feedData?.feed || feedData?.items || feedData?.events || feedData || [];
|
|
155
185
|
if (Array.isArray(feedItems)) {
|
|
156
186
|
for (const item of feedItems) {
|
|
157
|
-
const confDelta = item.confidenceDelta ?? item.confidence_delta ?? 0;
|
|
158
|
-
if (Math.abs(confDelta)
|
|
159
|
-
const itemDate = item.createdAt || item.created_at || item.timestamp || '';
|
|
187
|
+
const confDelta = item.delta ?? item.confidenceDelta ?? item.confidence_delta ?? 0;
|
|
188
|
+
if (Math.abs(confDelta) >= 0.02) {
|
|
189
|
+
const itemDate = item.evaluatedAt || item.createdAt || item.created_at || item.timestamp || '';
|
|
160
190
|
if (itemDate) {
|
|
161
191
|
events.push({
|
|
162
192
|
date: dateKey(new Date(itemDate)),
|
|
@@ -207,7 +237,7 @@ async function performanceCommand(opts) {
|
|
|
207
237
|
console.log();
|
|
208
238
|
// Column headers
|
|
209
239
|
const abbrevs = tickers.map(t => abbrevTicker(t.ticker));
|
|
210
|
-
const colWidth =
|
|
240
|
+
const colWidth = 11;
|
|
211
241
|
const dateCol = 'Date'.padEnd(12);
|
|
212
242
|
const headerCols = abbrevs.map(a => (0, utils_js_1.rpad)(a, colWidth)).join('');
|
|
213
243
|
const totalCol = (0, utils_js_1.rpad)('Total', colWidth);
|
|
@@ -235,6 +265,21 @@ async function performanceCommand(opts) {
|
|
|
235
265
|
const totalColor = row.total > 0 ? utils_js_1.c.green : row.total < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
|
|
236
266
|
console.log(` ${dateStr}${cols}${totalColor}${(0, utils_js_1.rpad)(totalStr, colWidth)}${utils_js_1.c.reset}`);
|
|
237
267
|
}
|
|
268
|
+
// Sparkline of total P&L
|
|
269
|
+
if (dailyRows.length >= 2) {
|
|
270
|
+
const totals = dailyRows.map(r => r.total);
|
|
271
|
+
const min = Math.min(...totals);
|
|
272
|
+
const max = Math.max(...totals);
|
|
273
|
+
const range = max - min || 1;
|
|
274
|
+
const blocks = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
275
|
+
const spark = totals.map(v => {
|
|
276
|
+
const idx = Math.round(((v - min) / range) * (blocks.length - 1));
|
|
277
|
+
const ch = blocks[idx];
|
|
278
|
+
return v >= 0 ? `${utils_js_1.c.green}${ch}${utils_js_1.c.reset}` : `${utils_js_1.c.red}${ch}${utils_js_1.c.reset}`;
|
|
279
|
+
}).join('');
|
|
280
|
+
console.log();
|
|
281
|
+
console.log(` ${utils_js_1.c.dim}P&L trend:${utils_js_1.c.reset} ${spark}`);
|
|
282
|
+
}
|
|
238
283
|
// Events
|
|
239
284
|
if (events.length > 0) {
|
|
240
285
|
console.log();
|
package/dist/commands/scan.js
CHANGED
|
@@ -104,14 +104,16 @@ async function keywordScan(query, json) {
|
|
|
104
104
|
score += 3;
|
|
105
105
|
else if (v > 100_000)
|
|
106
106
|
score += 1;
|
|
107
|
-
if (score > 0)
|
|
108
|
-
matches.push({ series: s, score });
|
|
107
|
+
if (score > 0 && v > 1000)
|
|
108
|
+
matches.push({ series: s, score, volume: v });
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
// Sort by score first, then by volume as tiebreaker
|
|
111
|
+
matches.sort((a, b) => b.score - a.score || b.volume - a.volume);
|
|
111
112
|
const topSeries = matches.slice(0, 15);
|
|
112
113
|
console.log(`\n${utils_js_1.c.bold}Found ${matches.length} relevant series. Top ${topSeries.length}:${utils_js_1.c.reset}\n`);
|
|
113
|
-
for (const { series: s,
|
|
114
|
-
|
|
114
|
+
for (const { series: s, volume } of topSeries) {
|
|
115
|
+
const volStr = volume >= 1_000_000 ? `$${(volume / 1_000_000).toFixed(1)}M` : volume >= 1000 ? `$${(volume / 1000).toFixed(0)}k` : `$${volume.toFixed(0)}`;
|
|
116
|
+
console.log(` ${(0, utils_js_1.rpad)(volStr, 10)} ${(0, utils_js_1.pad)(s.ticker, 25)} ${s.title}`);
|
|
115
117
|
}
|
|
116
118
|
// Fetch live markets for top 10
|
|
117
119
|
console.log(`\n${utils_js_1.c.dim}Fetching live markets...${utils_js_1.c.reset}\n`);
|
package/dist/kalshi.js
CHANGED
|
@@ -481,12 +481,12 @@ async function getBatchCandlesticks(params) {
|
|
|
481
481
|
return [];
|
|
482
482
|
try {
|
|
483
483
|
const searchParams = new URLSearchParams();
|
|
484
|
-
searchParams.set('
|
|
484
|
+
searchParams.set('market_tickers', params.tickers.join(','));
|
|
485
485
|
searchParams.set('start_ts', params.startTs.toString());
|
|
486
486
|
searchParams.set('end_ts', params.endTs.toString());
|
|
487
487
|
searchParams.set('period_interval', (params.periodInterval ?? 1440).toString());
|
|
488
488
|
const data = await kalshiAuthGet(`/markets/candlesticks?${searchParams.toString()}`);
|
|
489
|
-
return data.
|
|
489
|
+
return data.markets || [];
|
|
490
490
|
}
|
|
491
491
|
catch (err) {
|
|
492
492
|
console.warn('[Kalshi] Failed to fetch candlesticks:', err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@spfunctions/cli",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.2",
|
|
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"
|