@spfunctions/cli 1.4.1 → 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.
@@ -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: KXWTIMAX-26DEC31-T135 -> T135 */
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
- return parts[parts.length - 1] || ticker;
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) {
@@ -155,12 +181,12 @@ async function performanceCommand(opts) {
155
181
  const config = (0, config_js_1.loadConfig)();
156
182
  const client = new client_js_1.SFClient(config.apiKey, config.apiUrl);
157
183
  const feedData = await client.getFeed(720);
158
- const feedItems = feedData?.items || feedData?.events || feedData || [];
184
+ const feedItems = feedData?.feed || feedData?.items || feedData?.events || feedData || [];
159
185
  if (Array.isArray(feedItems)) {
160
186
  for (const item of feedItems) {
161
- const confDelta = item.confidenceDelta ?? item.confidence_delta ?? 0;
162
- if (Math.abs(confDelta) > 0.03) {
163
- 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 || '';
164
190
  if (itemDate) {
165
191
  events.push({
166
192
  date: dateKey(new Date(itemDate)),
@@ -211,7 +237,7 @@ async function performanceCommand(opts) {
211
237
  console.log();
212
238
  // Column headers
213
239
  const abbrevs = tickers.map(t => abbrevTicker(t.ticker));
214
- const colWidth = 9;
240
+ const colWidth = 11;
215
241
  const dateCol = 'Date'.padEnd(12);
216
242
  const headerCols = abbrevs.map(a => (0, utils_js_1.rpad)(a, colWidth)).join('');
217
243
  const totalCol = (0, utils_js_1.rpad)('Total', colWidth);
@@ -239,6 +265,21 @@ async function performanceCommand(opts) {
239
265
  const totalColor = row.total > 0 ? utils_js_1.c.green : row.total < 0 ? utils_js_1.c.red : utils_js_1.c.dim;
240
266
  console.log(` ${dateStr}${cols}${totalColor}${(0, utils_js_1.rpad)(totalStr, colWidth)}${utils_js_1.c.reset}`);
241
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
+ }
242
283
  // Events
243
284
  if (events.length > 0) {
244
285
  console.log();
@@ -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
- matches.sort((a, b) => b.score - a.score);
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, score } of topSeries) {
114
- console.log(` ${utils_js_1.c.dim}[${score}]${utils_js_1.c.reset} ${(0, utils_js_1.pad)(s.ticker, 25)} ${s.title}`);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spfunctions/cli",
3
- "version": "1.4.1",
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"