@spfunctions/cli 1.4.4 → 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/README.md +205 -48
- package/dist/cache.d.ts +6 -0
- package/dist/cache.js +31 -0
- package/dist/cache.test.d.ts +1 -0
- package/dist/cache.test.js +73 -0
- package/dist/client.test.d.ts +1 -0
- package/dist/client.test.js +89 -0
- package/dist/commands/agent.js +594 -106
- package/dist/commands/book.d.ts +17 -0
- package/dist/commands/book.js +220 -0
- package/dist/commands/dashboard.d.ts +6 -3
- package/dist/commands/dashboard.js +53 -22
- package/dist/commands/liquidity.d.ts +2 -0
- package/dist/commands/liquidity.js +128 -43
- package/dist/commands/performance.js +9 -2
- 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/commands/telegram.d.ts +15 -0
- package/dist/commands/telegram.js +125 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.js +9 -0
- package/dist/config.test.d.ts +1 -0
- package/dist/config.test.js +138 -0
- package/dist/index.js +107 -9
- 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/telegram/agent-bridge.d.ts +15 -0
- package/dist/telegram/agent-bridge.js +368 -0
- package/dist/telegram/bot.d.ts +10 -0
- package/dist/telegram/bot.js +297 -0
- package/dist/telegram/commands.d.ts +11 -0
- package/dist/telegram/commands.js +120 -0
- package/dist/telegram/format.d.ts +11 -0
- package/dist/telegram/format.js +51 -0
- package/dist/telegram/format.test.d.ts +1 -0
- package/dist/telegram/format.test.js +73 -0
- package/dist/telegram/poller.d.ts +6 -0
- package/dist/telegram/poller.js +32 -0
- package/dist/topics.d.ts +3 -0
- package/dist/topics.js +65 -7
- package/dist/topics.test.d.ts +1 -0
- package/dist/topics.test.js +131 -0
- package/dist/tui/border.d.ts +33 -0
- package/dist/tui/border.js +87 -0
- package/dist/tui/chart.d.ts +19 -0
- package/dist/tui/chart.js +117 -0
- package/dist/tui/dashboard.d.ts +9 -0
- package/dist/tui/dashboard.js +814 -0
- package/dist/tui/layout.d.ts +16 -0
- package/dist/tui/layout.js +41 -0
- package/dist/tui/screen.d.ts +33 -0
- package/dist/tui/screen.js +102 -0
- package/dist/tui/state.d.ts +40 -0
- package/dist/tui/state.js +36 -0
- package/dist/tui/widgets/commandbar.d.ts +8 -0
- package/dist/tui/widgets/commandbar.js +82 -0
- package/dist/tui/widgets/detail.d.ts +9 -0
- package/dist/tui/widgets/detail.js +151 -0
- package/dist/tui/widgets/edges.d.ts +4 -0
- package/dist/tui/widgets/edges.js +34 -0
- package/dist/tui/widgets/liquidity.d.ts +9 -0
- package/dist/tui/widgets/liquidity.js +142 -0
- package/dist/tui/widgets/orders.d.ts +4 -0
- package/dist/tui/widgets/orders.js +37 -0
- package/dist/tui/widgets/portfolio.d.ts +4 -0
- package/dist/tui/widgets/portfolio.js +59 -0
- package/dist/tui/widgets/signals.d.ts +4 -0
- package/dist/tui/widgets/signals.js +31 -0
- package/dist/tui/widgets/statusbar.d.ts +8 -0
- package/dist/tui/widgets/statusbar.js +72 -0
- package/dist/tui/widgets/thesis.d.ts +4 -0
- package/dist/tui/widgets/thesis.js +66 -0
- package/dist/tui/widgets/trade.d.ts +9 -0
- package/dist/tui/widgets/trade.js +117 -0
- package/dist/tui/widgets/upcoming.d.ts +4 -0
- package/dist/tui/widgets/upcoming.js +41 -0
- package/dist/tui/widgets/whatif.d.ts +7 -0
- package/dist/tui/widgets/whatif.js +113 -0
- package/dist/utils.test.d.ts +1 -0
- package/dist/utils.test.js +111 -0
- package/package.json +6 -2
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sf book — Orderbook depth, price history, and liquidity for individual markets
|
|
3
|
+
*
|
|
4
|
+
* Usage:
|
|
5
|
+
* sf book KXWTIMAX-26DEC31-T135 Single Kalshi market
|
|
6
|
+
* sf book KXWTI-T135 KXCPI-26MAY Multiple markets
|
|
7
|
+
* sf book --poly "oil price" Polymarket search
|
|
8
|
+
* sf book KXWTIMAX-26DEC31-T135 --history With 7d price history
|
|
9
|
+
* sf book KXWTIMAX-26DEC31-T135 --json JSON output
|
|
10
|
+
*/
|
|
11
|
+
interface BookOpts {
|
|
12
|
+
poly?: string;
|
|
13
|
+
history?: boolean;
|
|
14
|
+
json?: boolean;
|
|
15
|
+
}
|
|
16
|
+
export declare function bookCommand(tickers: string[], opts: BookOpts): Promise<void>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* sf book — Orderbook depth, price history, and liquidity for individual markets
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* sf book KXWTIMAX-26DEC31-T135 Single Kalshi market
|
|
7
|
+
* sf book KXWTI-T135 KXCPI-26MAY Multiple markets
|
|
8
|
+
* sf book --poly "oil price" Polymarket search
|
|
9
|
+
* sf book KXWTIMAX-26DEC31-T135 --history With 7d price history
|
|
10
|
+
* sf book KXWTIMAX-26DEC31-T135 --json JSON output
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.bookCommand = bookCommand;
|
|
14
|
+
const client_js_1 = require("../client.js");
|
|
15
|
+
const kalshi_js_1 = require("../kalshi.js");
|
|
16
|
+
const polymarket_js_1 = require("../polymarket.js");
|
|
17
|
+
const utils_js_1 = require("../utils.js");
|
|
18
|
+
async function bookCommand(tickers, opts) {
|
|
19
|
+
const results = [];
|
|
20
|
+
// ── Polymarket search mode ──
|
|
21
|
+
if (opts.poly) {
|
|
22
|
+
console.log(`${utils_js_1.c.dim}Searching Polymarket for "${opts.poly}"...${utils_js_1.c.reset}`);
|
|
23
|
+
const events = await (0, polymarket_js_1.polymarketSearch)(opts.poly, 10);
|
|
24
|
+
for (const event of events) {
|
|
25
|
+
for (const m of (event.markets || []).slice(0, 5)) {
|
|
26
|
+
if (!m.active || m.closed || !m.clobTokenIds)
|
|
27
|
+
continue;
|
|
28
|
+
const ids = (0, polymarket_js_1.parseClobTokenIds)(m.clobTokenIds);
|
|
29
|
+
if (!ids)
|
|
30
|
+
continue;
|
|
31
|
+
const depth = await (0, polymarket_js_1.polymarketGetOrderbookWithDepth)(ids[0]);
|
|
32
|
+
if (!depth)
|
|
33
|
+
continue;
|
|
34
|
+
const prices = (0, polymarket_js_1.parseOutcomePrices)(m.outcomePrices);
|
|
35
|
+
const book = {
|
|
36
|
+
venue: 'polymarket',
|
|
37
|
+
ticker: m.conditionId?.slice(0, 16) || m.id,
|
|
38
|
+
title: m.groupItemTitle
|
|
39
|
+
? `${event.title}: ${m.groupItemTitle}`
|
|
40
|
+
: m.question || event.title,
|
|
41
|
+
bestBid: depth.bestBid,
|
|
42
|
+
bestAsk: depth.bestAsk,
|
|
43
|
+
spread: depth.spread,
|
|
44
|
+
bidDepth: depth.totalBidDepth,
|
|
45
|
+
askDepth: depth.totalAskDepth,
|
|
46
|
+
liquidityScore: depth.liquidityScore,
|
|
47
|
+
volume24h: m.volume24hr || 0,
|
|
48
|
+
openInterest: m.liquidityNum || 0,
|
|
49
|
+
lastPrice: prices[0] ? (0, polymarket_js_1.toCents)(prices[0]) : 0,
|
|
50
|
+
expiry: m.endDateIso || null,
|
|
51
|
+
bidLevels: depth.levels.bids.slice(0, 5).map(l => ({ price: Math.round(parseFloat(l.price) * 100), size: Math.round(parseFloat(l.size)) })),
|
|
52
|
+
askLevels: depth.levels.asks.slice(0, 5).map(l => ({ price: Math.round(parseFloat(l.price) * 100), size: Math.round(parseFloat(l.size)) })),
|
|
53
|
+
};
|
|
54
|
+
// Price history sparkline
|
|
55
|
+
if (opts.history) {
|
|
56
|
+
try {
|
|
57
|
+
const hist = await (0, polymarket_js_1.polymarketGetPriceHistory)({ tokenId: ids[0], interval: '1w', fidelity: 360 });
|
|
58
|
+
if (hist.length > 0) {
|
|
59
|
+
book.sparkline = makeSparkline(hist.map(h => h.p * 100));
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch { /* skip */ }
|
|
63
|
+
}
|
|
64
|
+
results.push(book);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
// ── Kalshi tickers ──
|
|
69
|
+
for (const ticker of tickers) {
|
|
70
|
+
console.log(`${utils_js_1.c.dim}Fetching ${ticker}...${utils_js_1.c.reset}`);
|
|
71
|
+
try {
|
|
72
|
+
const market = await (0, client_js_1.kalshiFetchMarket)(ticker);
|
|
73
|
+
const ob = await (0, kalshi_js_1.getPublicOrderbook)(ticker);
|
|
74
|
+
const yesBids = (ob?.yes_dollars || [])
|
|
75
|
+
.map(([p, q]) => ({ price: Math.round(parseFloat(p) * 100), size: Math.round(parseFloat(q)) }))
|
|
76
|
+
.filter((l) => l.price > 0)
|
|
77
|
+
.sort((a, b) => b.price - a.price);
|
|
78
|
+
const noAsks = (ob?.no_dollars || [])
|
|
79
|
+
.map(([p, q]) => ({ price: Math.round(parseFloat(p) * 100), size: Math.round(parseFloat(q)) }))
|
|
80
|
+
.filter((l) => l.price > 0)
|
|
81
|
+
.sort((a, b) => b.price - a.price);
|
|
82
|
+
const bestBid = yesBids[0]?.price || 0;
|
|
83
|
+
const bestAsk = noAsks.length > 0 ? (100 - noAsks[0].price) : 100;
|
|
84
|
+
const spread = bestAsk - bestBid;
|
|
85
|
+
const bidDepth = yesBids.reduce((s, l) => s + l.size, 0);
|
|
86
|
+
const askDepth = noAsks.reduce((s, l) => s + l.size, 0);
|
|
87
|
+
const totalDepth = yesBids.slice(0, 3).reduce((s, l) => s + l.size, 0) +
|
|
88
|
+
noAsks.slice(0, 3).reduce((s, l) => s + l.size, 0);
|
|
89
|
+
const liq = spread <= 2 && totalDepth >= 500 ? 'high' : spread <= 5 && totalDepth >= 100 ? 'medium' : 'low';
|
|
90
|
+
const lastPrice = parseFloat(market.last_price_dollars || '0') * 100;
|
|
91
|
+
const book = {
|
|
92
|
+
venue: 'kalshi',
|
|
93
|
+
ticker: market.ticker || ticker,
|
|
94
|
+
title: market.title || market.subtitle || ticker,
|
|
95
|
+
bestBid,
|
|
96
|
+
bestAsk,
|
|
97
|
+
spread,
|
|
98
|
+
bidDepth,
|
|
99
|
+
askDepth,
|
|
100
|
+
liquidityScore: liq,
|
|
101
|
+
volume24h: parseFloat(market.volume_24h_fp || '0'),
|
|
102
|
+
openInterest: parseFloat(market.open_interest_fp || '0'),
|
|
103
|
+
lastPrice: Math.round(lastPrice),
|
|
104
|
+
expiry: market.close_time || market.expiration_time || null,
|
|
105
|
+
bidLevels: yesBids.slice(0, 5),
|
|
106
|
+
askLevels: noAsks.slice(0, 5).map((l) => ({ price: 100 - l.price, size: l.size })),
|
|
107
|
+
};
|
|
108
|
+
// Kalshi price history
|
|
109
|
+
if (opts.history) {
|
|
110
|
+
// Try authenticated candlestick API first (requires Kalshi keys)
|
|
111
|
+
if ((0, kalshi_js_1.isKalshiConfigured)()) {
|
|
112
|
+
try {
|
|
113
|
+
const now = Math.floor(Date.now() / 1000);
|
|
114
|
+
const weekAgo = now - 7 * 86400;
|
|
115
|
+
const candleResults = await (0, kalshi_js_1.getBatchCandlesticks)({
|
|
116
|
+
tickers: [ticker],
|
|
117
|
+
startTs: weekAgo,
|
|
118
|
+
endTs: now,
|
|
119
|
+
periodInterval: 1440,
|
|
120
|
+
});
|
|
121
|
+
const mktEntry = candleResults.find((e) => e.market_ticker === ticker) || candleResults[0];
|
|
122
|
+
const mktCandles = mktEntry?.candlesticks || [];
|
|
123
|
+
if (Array.isArray(mktCandles) && mktCandles.length > 0) {
|
|
124
|
+
const prices = mktCandles.map((cd) => {
|
|
125
|
+
// Kalshi nests prices: cd.price.close_dollars or cd.yes_ask.close_dollars
|
|
126
|
+
const p = cd.price?.close_dollars ?? cd.yes_ask?.close_dollars ?? cd.yes_bid?.close_dollars ?? cd.close ?? cd.price;
|
|
127
|
+
const v = typeof p === 'string' ? parseFloat(p) * 100 : (typeof p === 'number' ? p : 0);
|
|
128
|
+
return Math.round(v);
|
|
129
|
+
}).filter((p) => p > 0);
|
|
130
|
+
if (prices.length >= 2) {
|
|
131
|
+
book.sparkline = makeSparkline(prices);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch { /* skip */ }
|
|
136
|
+
}
|
|
137
|
+
// Fallback: show previous vs current from market data
|
|
138
|
+
if (!book.sparkline) {
|
|
139
|
+
const prev = parseFloat(market.previous_price_dollars || '0') * 100;
|
|
140
|
+
if (prev > 0 && Math.abs(prev - book.lastPrice) > 0) {
|
|
141
|
+
const delta = book.lastPrice - Math.round(prev);
|
|
142
|
+
const deltaColor = delta >= 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
143
|
+
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
144
|
+
book.sparkline = `prev ${Math.round(prev)}¢ → now ${book.lastPrice}¢ ${deltaColor}${deltaStr}${utils_js_1.c.reset}`;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
results.push(book);
|
|
149
|
+
}
|
|
150
|
+
catch (err) {
|
|
151
|
+
console.error(`${utils_js_1.c.red}Failed to fetch ${ticker}: ${err.message}${utils_js_1.c.reset}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (results.length === 0) {
|
|
155
|
+
console.log(`${utils_js_1.c.dim}No markets found.${utils_js_1.c.reset}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
// ── JSON output ──
|
|
159
|
+
if (opts.json) {
|
|
160
|
+
console.log(JSON.stringify(results, null, 2));
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// ── Formatted output ──
|
|
164
|
+
for (const book of results) {
|
|
165
|
+
const venueTag = book.venue === 'polymarket' ? `${utils_js_1.c.blue}POLY${utils_js_1.c.reset}` : `${utils_js_1.c.cyan}KLSH${utils_js_1.c.reset}`;
|
|
166
|
+
console.log();
|
|
167
|
+
console.log(`${utils_js_1.c.bold}${venueTag} ${book.title}${utils_js_1.c.reset}`);
|
|
168
|
+
console.log(`${utils_js_1.c.dim}${book.ticker}${utils_js_1.c.reset}`);
|
|
169
|
+
console.log();
|
|
170
|
+
// Summary line
|
|
171
|
+
const liqColor = book.liquidityScore === 'high' ? utils_js_1.c.green : book.liquidityScore === 'medium' ? utils_js_1.c.yellow : utils_js_1.c.red;
|
|
172
|
+
console.log(` Last ${utils_js_1.c.bold}${book.lastPrice}¢${utils_js_1.c.reset} ` +
|
|
173
|
+
`Bid ${utils_js_1.c.green}${book.bestBid}¢${utils_js_1.c.reset} ` +
|
|
174
|
+
`Ask ${utils_js_1.c.red}${book.bestAsk}¢${utils_js_1.c.reset} ` +
|
|
175
|
+
`Spread ${liqColor}${book.spread}¢${utils_js_1.c.reset} ` +
|
|
176
|
+
`Liq ${liqColor}${book.liquidityScore}${utils_js_1.c.reset}`);
|
|
177
|
+
console.log(` Vol24h ${(0, utils_js_1.vol)(book.volume24h)} ` +
|
|
178
|
+
`OI ${(0, utils_js_1.vol)(book.openInterest)}` +
|
|
179
|
+
(book.expiry ? ` Expires ${book.expiry.slice(0, 10)}` : ''));
|
|
180
|
+
// Sparkline
|
|
181
|
+
if (book.sparkline) {
|
|
182
|
+
console.log(` 7d ${book.sparkline}`);
|
|
183
|
+
}
|
|
184
|
+
// Orderbook depth
|
|
185
|
+
if (book.bidLevels && book.askLevels) {
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(` ${utils_js_1.c.dim}${(0, utils_js_1.pad)('BID', 18)} ${(0, utils_js_1.pad)('ASK', 18)}${utils_js_1.c.reset}`);
|
|
188
|
+
console.log(` ${utils_js_1.c.dim}${'─'.repeat(38)}${utils_js_1.c.reset}`);
|
|
189
|
+
const maxLevels = Math.max(book.bidLevels.length, book.askLevels.length);
|
|
190
|
+
for (let i = 0; i < Math.min(maxLevels, 5); i++) {
|
|
191
|
+
const bid = book.bidLevels[i];
|
|
192
|
+
const ask = book.askLevels[i];
|
|
193
|
+
const bidStr = bid ? `${utils_js_1.c.green}${(0, utils_js_1.rpad)(`${bid.price}¢`, 5)}${utils_js_1.c.reset} ${(0, utils_js_1.rpad)(String(bid.size), 8)}` : (0, utils_js_1.pad)('', 18);
|
|
194
|
+
const askStr = ask ? `${utils_js_1.c.red}${(0, utils_js_1.rpad)(`${ask.price}¢`, 5)}${utils_js_1.c.reset} ${(0, utils_js_1.rpad)(String(ask.size), 8)}` : '';
|
|
195
|
+
console.log(` ${bidStr} ${askStr}`);
|
|
196
|
+
}
|
|
197
|
+
console.log(` ${utils_js_1.c.dim}depth: ${book.bidDepth} bid / ${book.askDepth} ask${utils_js_1.c.reset}`);
|
|
198
|
+
}
|
|
199
|
+
console.log();
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// ── Sparkline helper ──
|
|
203
|
+
function makeSparkline(values) {
|
|
204
|
+
if (values.length < 2)
|
|
205
|
+
return '';
|
|
206
|
+
const min = Math.min(...values);
|
|
207
|
+
const max = Math.max(...values);
|
|
208
|
+
const range = max - min || 1;
|
|
209
|
+
const blocks = '▁▂▃▄▅▆▇█';
|
|
210
|
+
const line = values.map(v => {
|
|
211
|
+
const idx = Math.round(((v - min) / range) * 7);
|
|
212
|
+
return blocks[idx];
|
|
213
|
+
}).join('');
|
|
214
|
+
const startPrice = Math.round(values[0]);
|
|
215
|
+
const endPrice = Math.round(values[values.length - 1]);
|
|
216
|
+
const delta = endPrice - startPrice;
|
|
217
|
+
const deltaColor = delta >= 0 ? utils_js_1.c.green : utils_js_1.c.red;
|
|
218
|
+
const deltaStr = delta >= 0 ? `+${delta}` : `${delta}`;
|
|
219
|
+
return `${line} ${startPrice}¢→${endPrice}¢ ${deltaColor}${deltaStr}${utils_js_1.c.reset}`;
|
|
220
|
+
}
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* sf dashboard —
|
|
2
|
+
* sf dashboard — Commander entry point
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Three modes:
|
|
5
|
+
* --json → dump current state as JSON
|
|
6
|
+
* --once → one-time formatted print (no interactive TUI)
|
|
7
|
+
* default → launch interactive TUI dashboard
|
|
6
8
|
*/
|
|
7
9
|
export declare function dashboardCommand(opts?: {
|
|
8
10
|
json?: boolean;
|
|
11
|
+
once?: boolean;
|
|
9
12
|
apiKey?: string;
|
|
10
13
|
apiUrl?: string;
|
|
11
14
|
}): Promise<void>;
|
|
@@ -1,17 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* sf dashboard —
|
|
3
|
+
* sf dashboard — Commander entry point
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* Three modes:
|
|
6
|
+
* --json → dump current state as JSON
|
|
7
|
+
* --once → one-time formatted print (no interactive TUI)
|
|
8
|
+
* default → launch interactive TUI dashboard
|
|
7
9
|
*/
|
|
8
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
11
|
exports.dashboardCommand = dashboardCommand;
|
|
10
12
|
const client_js_1 = require("../client.js");
|
|
11
13
|
const kalshi_js_1 = require("../kalshi.js");
|
|
14
|
+
const polymarket_js_1 = require("../polymarket.js");
|
|
15
|
+
const config_js_1 = require("../config.js");
|
|
12
16
|
const topics_js_1 = require("../topics.js");
|
|
17
|
+
const dashboard_js_1 = require("../tui/dashboard.js");
|
|
13
18
|
function categorize(ticker) {
|
|
14
|
-
// Match longest prefix first
|
|
15
19
|
const sorted = Object.keys(topics_js_1.RISK_CATEGORIES).sort((a, b) => b.length - a.length);
|
|
16
20
|
for (const prefix of sorted) {
|
|
17
21
|
if (ticker.startsWith(prefix))
|
|
@@ -31,8 +35,13 @@ function timeAgo(dateStr) {
|
|
|
31
35
|
return `${days}d ago`;
|
|
32
36
|
}
|
|
33
37
|
async function dashboardCommand(opts) {
|
|
38
|
+
// ── Default: interactive TUI ──
|
|
39
|
+
if (!opts?.json && !opts?.once) {
|
|
40
|
+
await (0, dashboard_js_1.startDashboard)();
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
// ── JSON or one-time print modes (legacy behavior) ──
|
|
34
44
|
const client = new client_js_1.SFClient(opts?.apiKey, opts?.apiUrl);
|
|
35
|
-
// ── Fetch data in parallel ─────────────────────────────────────────────────
|
|
36
45
|
const [thesesResult, positions] = await Promise.all([
|
|
37
46
|
client.listTheses(),
|
|
38
47
|
(0, kalshi_js_1.getPositions)().catch(() => null),
|
|
@@ -59,7 +68,7 @@ async function dashboardCommand(opts) {
|
|
|
59
68
|
}
|
|
60
69
|
}
|
|
61
70
|
}
|
|
62
|
-
//
|
|
71
|
+
// Collect all edges across all theses
|
|
63
72
|
const allEdges = [];
|
|
64
73
|
for (const ctx of contexts) {
|
|
65
74
|
if (!ctx?.edges)
|
|
@@ -78,26 +87,49 @@ async function dashboardCommand(opts) {
|
|
|
78
87
|
}
|
|
79
88
|
// Find positioned tickers
|
|
80
89
|
const positionedTickers = new Set(positions?.map((p) => p.ticker) || []);
|
|
81
|
-
// Unpositioned edges
|
|
90
|
+
// Unpositioned edges
|
|
82
91
|
const unpositionedEdges = [...edgeMap.values()]
|
|
83
92
|
.filter(e => !positionedTickers.has(e.marketId))
|
|
84
93
|
.sort((a, b) => Math.abs(b.edge) - Math.abs(a.edge))
|
|
85
94
|
.slice(0, 10);
|
|
86
|
-
//
|
|
95
|
+
// Fetch additional data for JSON mode
|
|
96
|
+
const [orders, balance, polyPositions] = await Promise.all([
|
|
97
|
+
opts?.json ? (0, kalshi_js_1.getOrders)({ status: 'resting' }).catch(() => []) : Promise.resolve([]),
|
|
98
|
+
opts?.json ? (0, kalshi_js_1.getBalance)().catch(() => null) : Promise.resolve(null),
|
|
99
|
+
opts?.json && (0, config_js_1.loadConfig)().polymarketWalletAddress
|
|
100
|
+
? (0, polymarket_js_1.polymarketGetPositions)((0, config_js_1.loadConfig)().polymarketWalletAddress).catch(() => [])
|
|
101
|
+
: Promise.resolve([]),
|
|
102
|
+
]);
|
|
103
|
+
// Fetch feed for recent evaluations
|
|
104
|
+
let feed = [];
|
|
105
|
+
if (opts?.json) {
|
|
106
|
+
try {
|
|
107
|
+
feed = (await client.getFeed(24, 20)).evaluations || [];
|
|
108
|
+
}
|
|
109
|
+
catch { /* skip */ }
|
|
110
|
+
}
|
|
111
|
+
// ── JSON output ──
|
|
87
112
|
if (opts?.json) {
|
|
88
113
|
console.log(JSON.stringify({
|
|
89
114
|
theses,
|
|
90
|
-
positions,
|
|
115
|
+
positions: positions || [],
|
|
116
|
+
polymarketPositions: polyPositions,
|
|
117
|
+
orders,
|
|
118
|
+
balance,
|
|
91
119
|
unpositionedEdges,
|
|
120
|
+
feed,
|
|
121
|
+
kalshiConfigured: (0, kalshi_js_1.isKalshiConfigured)(),
|
|
122
|
+
polymarketConfigured: !!(0, config_js_1.loadConfig)().polymarketWalletAddress,
|
|
123
|
+
timestamp: new Date().toISOString(),
|
|
92
124
|
}, null, 2));
|
|
93
125
|
return;
|
|
94
126
|
}
|
|
95
|
-
// ──
|
|
127
|
+
// ── One-time formatted output ──
|
|
96
128
|
console.log();
|
|
97
129
|
console.log(' SimpleFunctions Dashboard');
|
|
98
|
-
console.log(' ' + '
|
|
130
|
+
console.log(' ' + '\u2500'.repeat(50));
|
|
99
131
|
console.log();
|
|
100
|
-
//
|
|
132
|
+
// Theses
|
|
101
133
|
console.log(' Theses');
|
|
102
134
|
if (theses.length === 0) {
|
|
103
135
|
console.log(' (none)');
|
|
@@ -115,7 +147,7 @@ async function dashboardCommand(opts) {
|
|
|
115
147
|
}
|
|
116
148
|
}
|
|
117
149
|
console.log();
|
|
118
|
-
//
|
|
150
|
+
// Positions
|
|
119
151
|
console.log(' Positions');
|
|
120
152
|
if (!positions || positions.length === 0) {
|
|
121
153
|
console.log(' (no Kalshi positions or Kalshi not configured)');
|
|
@@ -126,8 +158,8 @@ async function dashboardCommand(opts) {
|
|
|
126
158
|
for (const p of positions) {
|
|
127
159
|
const ticker = (p.ticker || '').padEnd(22);
|
|
128
160
|
const qty = String(p.quantity || 0).padStart(5);
|
|
129
|
-
const avg = `${p.average_price_paid || 0}
|
|
130
|
-
const now = typeof p.current_value === 'number' ? `${p.current_value}
|
|
161
|
+
const avg = `${p.average_price_paid || 0}\u00A2`;
|
|
162
|
+
const now = typeof p.current_value === 'number' ? `${p.current_value}\u00A2` : '?\u00A2';
|
|
131
163
|
const pnlCents = p.unrealized_pnl || 0;
|
|
132
164
|
const pnlDollars = (pnlCents / 100).toFixed(2);
|
|
133
165
|
const pnlStr = pnlCents >= 0 ? `+$${pnlDollars}` : `-$${Math.abs(parseFloat(pnlDollars)).toFixed(2)}`;
|
|
@@ -136,14 +168,14 @@ async function dashboardCommand(opts) {
|
|
|
136
168
|
totalPnl += pnlCents;
|
|
137
169
|
console.log(` ${ticker} ${qty} @ ${avg.padEnd(5)} now ${now.padEnd(5)} ${pnlStr}`);
|
|
138
170
|
}
|
|
139
|
-
console.log(' ' + '
|
|
171
|
+
console.log(' ' + '\u2500'.repeat(45));
|
|
140
172
|
const totalCostDollars = (totalCost / 100).toFixed(0);
|
|
141
173
|
const totalPnlDollars = (totalPnl / 100).toFixed(2);
|
|
142
174
|
const pnlDisplay = totalPnl >= 0 ? `+$${totalPnlDollars}` : `-$${Math.abs(parseFloat(totalPnlDollars)).toFixed(2)}`;
|
|
143
175
|
console.log(` Total cost: $${totalCostDollars} | P&L: ${pnlDisplay}`);
|
|
144
176
|
}
|
|
145
177
|
console.log();
|
|
146
|
-
//
|
|
178
|
+
// Risk Exposure
|
|
147
179
|
if (positions && positions.length > 0) {
|
|
148
180
|
console.log(' Risk Exposure');
|
|
149
181
|
const riskGroups = new Map();
|
|
@@ -157,7 +189,6 @@ async function dashboardCommand(opts) {
|
|
|
157
189
|
existing.tickers.push(p.ticker);
|
|
158
190
|
riskGroups.set(cat, existing);
|
|
159
191
|
}
|
|
160
|
-
// Sort by cost descending
|
|
161
192
|
const sorted = [...riskGroups.entries()].sort((a, b) => b[1].cost - a[1].cost);
|
|
162
193
|
for (const [category, data] of sorted) {
|
|
163
194
|
const costDollars = `$${(data.cost / 100).toFixed(0)}`;
|
|
@@ -168,16 +199,16 @@ async function dashboardCommand(opts) {
|
|
|
168
199
|
}
|
|
169
200
|
console.log();
|
|
170
201
|
}
|
|
171
|
-
//
|
|
202
|
+
// Top Unpositioned Edges
|
|
172
203
|
if (unpositionedEdges.length > 0) {
|
|
173
204
|
console.log(' Top Unpositioned Edges');
|
|
174
205
|
for (const e of unpositionedEdges) {
|
|
175
206
|
const name = (e.market || e.marketId || '').slice(0, 25).padEnd(25);
|
|
176
|
-
const mkt = `${e.marketPrice}
|
|
177
|
-
const thesis = `${e.thesisPrice}
|
|
207
|
+
const mkt = `${e.marketPrice}\u00A2`;
|
|
208
|
+
const thesis = `${e.thesisPrice}\u00A2`;
|
|
178
209
|
const edge = e.edge > 0 ? `+${e.edge}` : `${e.edge}`;
|
|
179
210
|
const liq = e.orderbook?.liquidityScore || '?';
|
|
180
|
-
console.log(` ${name} ${mkt.padStart(5)}
|
|
211
|
+
console.log(` ${name} ${mkt.padStart(5)} \u2192 ${thesis.padStart(5)} edge ${edge.padStart(4)} ${liq}`);
|
|
181
212
|
}
|
|
182
213
|
console.log();
|
|
183
214
|
}
|