kalshi-trading-bot-cli 2.1.3 → 2.1.4
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 +181 -5
- package/data/themes_seo.json +159 -0
- package/env.example +5 -2
- package/package.json +1 -1
- package/src/backtest/renderer.ts +0 -2
- package/src/cli.ts +88 -0
- package/src/commands/basket.ts +646 -0
- package/src/commands/catalysts.ts +121 -0
- package/src/commands/clusters.ts +153 -0
- package/src/commands/correlate.ts +112 -0
- package/src/commands/dispatch.ts +238 -7
- package/src/commands/editorial-themes.ts +494 -0
- package/src/commands/events.ts +140 -0
- package/src/commands/help.ts +299 -14
- package/src/commands/index.ts +137 -6
- package/src/commands/parse-args.ts +204 -1
- package/src/commands/peers.ts +87 -0
- package/src/commands/search-remote.ts +90 -0
- package/src/commands/series.ts +386 -0
- package/src/commands/similar.ts +97 -0
- package/src/components/intro.ts +9 -0
- package/src/db/editorial-themes.ts +111 -0
- package/src/db/event-index.ts +109 -31
- package/src/db/schema.ts +18 -0
- package/src/gateway/commands/handler.ts +6 -2
- package/src/scan/octagon-events-api.ts +55 -0
- package/src/scan/octagon-kalshi-api.ts +564 -0
package/src/commands/help.ts
CHANGED
|
@@ -10,20 +10,32 @@ function prefix(ctx: HelpContext): string {
|
|
|
10
10
|
function buildTopics(ctx: HelpContext): Record<string, string> {
|
|
11
11
|
const p = prefix(ctx);
|
|
12
12
|
return {
|
|
13
|
-
search: `**${p}search** — Discovery
|
|
13
|
+
search: `**${p}search** — Discovery (Octagon-powered when OCTAGON_API_KEY is set)
|
|
14
14
|
|
|
15
|
-
${p}search [theme|ticker|query]
|
|
15
|
+
${p}search [theme|ticker|query] Full-text market search (server-side when key is set, else local index)
|
|
16
16
|
${p}search themes List all available themes and subcategories
|
|
17
|
-
${p}search edge
|
|
17
|
+
${p}search edge Edge ranking from latest Octagon run (server-side) or local cache
|
|
18
18
|
${p}search edge --min-edge 30 Markets with ≥30pp edge
|
|
19
19
|
${p}search edge --limit 50 Top 50 results
|
|
20
20
|
${p}search edge --category crypto Filter by category
|
|
21
|
+
${p}search edge --sort-by total_volume Sort: edge_pp | expected_return | total_volume | model_probability
|
|
22
|
+
|
|
23
|
+
Search flags (server-side path):
|
|
24
|
+
--category <name> Filter by category
|
|
25
|
+
--series <ticker> Filter to a series
|
|
26
|
+
--min-volume <n> Floor on 24h volume
|
|
27
|
+
--close-before <iso> Only markets closing before
|
|
28
|
+
--limit <n> Page size (default 30)
|
|
29
|
+
--sort-by <key> volume_24h | close_time | last_price (client-side sort)
|
|
30
|
+
--aggregate-by series Roll up results by series (calls series rollup)
|
|
31
|
+
--active-only Drop non-active markets (defensive; the live universe is active by default)
|
|
21
32
|
|
|
22
33
|
Examples:
|
|
23
34
|
${p}search crypto
|
|
24
|
-
${p}search
|
|
25
|
-
${p}search
|
|
26
|
-
|
|
35
|
+
${p}search "bitcoin price" --min-volume 10000
|
|
36
|
+
${p}search edge --min-edge 30 --category crypto
|
|
37
|
+
|
|
38
|
+
Tip: ${p}similar gives semantic match (catches "Bitcoin pierce six figures" ↔ "BTC > $100k").`,
|
|
27
39
|
|
|
28
40
|
portfolio: `**${p}portfolio** — Account state
|
|
29
41
|
|
|
@@ -125,6 +137,219 @@ ${p}init Launch the TUI with the setup wizard open
|
|
|
125
137
|
|
|
126
138
|
${p}help Show all commands
|
|
127
139
|
${p}help <command> Show detailed help for a command`,
|
|
140
|
+
|
|
141
|
+
similar: `**${p}similar** — Semantic market search (Octagon-powered)
|
|
142
|
+
|
|
143
|
+
${p}similar <ticker> Markets near this ticker by embedding distance
|
|
144
|
+
${p}similar -q "free-text query" Markets matching free-text intent (server-side embed)
|
|
145
|
+
${p}similar <ticker> --top-k 25 Return top-25 nearest neighbors
|
|
146
|
+
${p}similar -q "..." --category crypto --min-volume 10000 --close-before 2026-08-19T00:00:00Z
|
|
147
|
+
|
|
148
|
+
Flags:
|
|
149
|
+
--top-k <n> Number of neighbors (default 25, max 100)
|
|
150
|
+
--category <name> Restrict to a Kalshi category
|
|
151
|
+
--min-volume <n> Floor on 24h volume
|
|
152
|
+
--close-before <iso> Only markets closing before this timestamp
|
|
153
|
+
--json JSON output
|
|
154
|
+
|
|
155
|
+
Catches matches keyword search misses — "Will Bitcoin pierce six figures" ↔ "BTC over $100k".`,
|
|
156
|
+
|
|
157
|
+
clusters: `**${p}clusters** — Browse Octagon clusters (thematic + behavioral)
|
|
158
|
+
|
|
159
|
+
${p}clusters List thematic clusters with sample titles
|
|
160
|
+
${p}clusters --label fed Filter by label substring
|
|
161
|
+
${p}clusters <id> Show markets in cluster
|
|
162
|
+
${p}clusters --behavioral List behavioral clusters (mean return + vol)
|
|
163
|
+
${p}clusters <id> --behavioral Members of a behavioral cluster
|
|
164
|
+
${p}clusters --ranked Rank clusters by historical basket return
|
|
165
|
+
${p}clusters --ranked --timeframe 1y --min-return 0.20 --top-k 5
|
|
166
|
+
|
|
167
|
+
Flags:
|
|
168
|
+
--behavioral Use behavioral clustering (30-day return vectors)
|
|
169
|
+
--label <substring> Case-insensitive label filter
|
|
170
|
+
--ranked Score clusters by equal-weight basket return
|
|
171
|
+
--timeframe <1w|1m|3m|6m|1y> Window for --ranked (default 1y)
|
|
172
|
+
--min-return <n> Minimum total_return to include (e.g. 0.20)
|
|
173
|
+
--top-k <n> Basket size per cluster for --ranked (default 5)
|
|
174
|
+
--limit <n> Max clusters to evaluate
|
|
175
|
+
--json JSON output`,
|
|
176
|
+
|
|
177
|
+
peers: `**${p}peers** — Find markets in the same cluster as a ticker
|
|
178
|
+
|
|
179
|
+
${p}peers <ticker> Thematic cluster peers (default)
|
|
180
|
+
${p}peers <ticker> --behavioral Behavioral cluster peers
|
|
181
|
+
${p}peers <ticker> --limit 50 Up to 50 peers (excluding anchor)
|
|
182
|
+
${p}peers <ticker> --show-cluster Just show which clusters this ticker belongs to
|
|
183
|
+
|
|
184
|
+
Flags:
|
|
185
|
+
--behavioral Use behavioral clusters instead of thematic
|
|
186
|
+
--limit <n> Number of peers to return (default 50)
|
|
187
|
+
--show-cluster Print cluster membership only (no peer list)
|
|
188
|
+
--json JSON output`,
|
|
189
|
+
|
|
190
|
+
correlate: `**${p}correlate** — Pairwise correlation matrix over close-price candles
|
|
191
|
+
|
|
192
|
+
${p}correlate <ticker1> <ticker2> [...] Pearson correlation across 2-100 tickers
|
|
193
|
+
${p}correlate --tickers KX-A,KX-B,KX-C Same, comma-separated
|
|
194
|
+
${p}correlate KX-A KX-B --window-days 90 90-day lookback
|
|
195
|
+
${p}correlate KX-A KX-B --sides yes,no Side-aware: corr(YES_A, NO_B) flips sign
|
|
196
|
+
${p}correlate KX-A KX-B --cells Per-cell detail (overlap_count + reason)
|
|
197
|
+
|
|
198
|
+
Flags:
|
|
199
|
+
--window-days <n> Lookback (1-730, default 30; auto interval picks 1d if >=90)
|
|
200
|
+
--correlation-interval <1h|1d> Override bin size
|
|
201
|
+
--tickers <csv> Alternative to positional args
|
|
202
|
+
--sides yes,no,yes Per-ticker side (same length as tickers); default all yes
|
|
203
|
+
--cells Include per-cell detail (overlap_count, reason)
|
|
204
|
+
--json JSON output (matrix + ranked_pairs + cells_detail)
|
|
205
|
+
|
|
206
|
+
Output ranks pairs ascending by correlation — most-uncorrelated first.`,
|
|
207
|
+
|
|
208
|
+
events: `**${p}events** — Octagon event rollups (event ↔ outcome ladder)
|
|
209
|
+
|
|
210
|
+
${p}events List events sorted by total_volume
|
|
211
|
+
${p}events --category Politics Filter by series_category
|
|
212
|
+
${p}events --min-volume 10000 Volume floor
|
|
213
|
+
${p}events --limit 25 Page size (default 50)
|
|
214
|
+
${p}events KXFEDCHAIRNOM-29 Drill into one event: outcome probabilities + per-contract edge
|
|
215
|
+
|
|
216
|
+
Flags:
|
|
217
|
+
--category <name> Filter by series_category (case-insensitive substring)
|
|
218
|
+
--min-volume <n> Floor on total_volume
|
|
219
|
+
--limit <n> Page size (default 50)
|
|
220
|
+
--json JSON envelope output
|
|
221
|
+
|
|
222
|
+
Each event is a multi-market Kalshi question (e.g. "Who will Trump nominate as Fed Chair?")
|
|
223
|
+
with one binary sub-market per outcome (Kevin Warsh, Judy Shelton, ...).
|
|
224
|
+
Octagon supplies a model_probability per outcome so you can rank contracts by edge.`,
|
|
225
|
+
|
|
226
|
+
series: `**${p}series** — Series-level rollups over the Kalshi universe
|
|
227
|
+
|
|
228
|
+
${p}series List series with 24h vol, market count, dominant category
|
|
229
|
+
${p}series list --min-volume 10000 Liquidity filter
|
|
230
|
+
${p}series list --category Crypto Filter by category
|
|
231
|
+
${p}series KXBTCD Drill in: all sub-markets sorted by volume
|
|
232
|
+
${p}series search "bitcoin" Keyword search → rolled up by series
|
|
233
|
+
${p}series candles KXBTCD --timeframe 3m Series NAV = equal-weight basket of top sub-markets
|
|
234
|
+
${p}series events KXIPO List events in a series (e.g. KXIPO → KXIPO-26)
|
|
235
|
+
|
|
236
|
+
Flags:
|
|
237
|
+
--min-volume <n> Floor on 24h volume per series
|
|
238
|
+
--category <name> Filter by category
|
|
239
|
+
--limit <n> Page size (default 50)
|
|
240
|
+
--timeframe <1w|1m|3m|6m|1y> Candle window (default 1y; for "series candles")
|
|
241
|
+
--top-k <n> Sub-markets to include in series NAV basket (default 20)
|
|
242
|
+
--series <prefix> Filter list by series-ticker prefix (e.g. KXBTC)
|
|
243
|
+
--json JSON output
|
|
244
|
+
|
|
245
|
+
A series is the Kalshi grouping above individual markets — KXBTCD is the BTC
|
|
246
|
+
strike ladder, with hundreds of sub-markets like KXBTCD-26DEC31-T100000.
|
|
247
|
+
Series list is now a single server-side call (was 25 paginated calls).`,
|
|
248
|
+
|
|
249
|
+
catalysts: `**${p}catalysts** — Upcoming Kalshi market closes grouped by week
|
|
250
|
+
|
|
251
|
+
${p}catalysts upcoming Next 30 days
|
|
252
|
+
${p}catalysts upcoming --days 7 Next week
|
|
253
|
+
${p}catalysts upcoming --days 14 --min-volume 5000 --category Politics
|
|
254
|
+
${p}catalysts upcoming --limit 10 Up to 10 markets per week shown
|
|
255
|
+
|
|
256
|
+
Flags:
|
|
257
|
+
--days <n> Lookback window (default 30)
|
|
258
|
+
--min-volume <n> Floor on 24h volume
|
|
259
|
+
--category <name> Filter by category
|
|
260
|
+
--limit <n> Top-N markets per week (default 8)
|
|
261
|
+
--json JSON output
|
|
262
|
+
|
|
263
|
+
Use for catalyst-calendar planning: see which weeks have major Kalshi
|
|
264
|
+
resolutions cluster up so you can position before catalyst risk.`,
|
|
265
|
+
|
|
266
|
+
themes: `**${p}themes** — Editorial narrative registry (curated theme buckets)
|
|
267
|
+
|
|
268
|
+
${p}themes List registered editorial themes
|
|
269
|
+
${p}themes import Seed from data/themes_seo.json (25 starter themes)
|
|
270
|
+
${p}themes import <path> Import from a custom JSON file
|
|
271
|
+
${p}themes export <path> Export current registry
|
|
272
|
+
${p}themes show "Iran Escalation" Drill into one theme
|
|
273
|
+
${p}themes create "My Theme" --tickers KXA,KXB --label "..." [--min-volume N]
|
|
274
|
+
${p}themes delete "My Theme"
|
|
275
|
+
${p}themes add-series "My Theme" KXBTCD,KXETHD
|
|
276
|
+
${p}themes remove-series "My Theme" KXBTCD
|
|
277
|
+
${p}themes set-search-volume "My Theme" 100000
|
|
278
|
+
${p}themes report Dashboard: 25-theme grid with SEO + liquidity
|
|
279
|
+
${p}themes audit Flag dead themes (high SEO + zero volume)
|
|
280
|
+
${p}themes overlap Cross-theme dedupe report
|
|
281
|
+
|
|
282
|
+
Editorial themes are narrative buckets you curate (e.g. "AI Race Milestones",
|
|
283
|
+
"Iran Escalation") — distinct from Octagon's ML clusters. Each theme maps to a
|
|
284
|
+
list of Kalshi series and an optional monthly search-volume estimate.
|
|
285
|
+
|
|
286
|
+
Flags:
|
|
287
|
+
--label <desc> Set description on create
|
|
288
|
+
--min-volume <n> Set search_volume on create (poorly named — improve later)
|
|
289
|
+
--tickers <csv> Comma-separated series on create
|
|
290
|
+
--json JSON output
|
|
291
|
+
|
|
292
|
+
Legacy: ${p}search themes still lists Kalshi category labels (the pre-registry view).`,
|
|
293
|
+
|
|
294
|
+
basket: `**${p}basket** — Build, backtest, and size diversified baskets
|
|
295
|
+
|
|
296
|
+
${p}basket build [universe filters] [-n N] [--max-per-cluster M] [--max-corr X] [--bankroll $ --kelly K --probs ...]
|
|
297
|
+
${p}basket backtest --tickers KX-A,KX-B --weights 0.6,0.4 --timeframe 1y
|
|
298
|
+
${p}basket candles --tickers KX-A,KX-B --timeframe 6m
|
|
299
|
+
${p}basket size --bankroll 1000 --kelly 0.25 --probs KX-A:0.62,KX-B:0.55 [--side yes|no]
|
|
300
|
+
|
|
301
|
+
Validate flags:
|
|
302
|
+
--tickers KX-A,KX-B Validate explicit tickers (equal-stake split)
|
|
303
|
+
--probs KX-A:yes:170,KX-B:no:160 Per-leg ticker:side:stake
|
|
304
|
+
--theme <name> Resolve from editorial registry
|
|
305
|
+
--bankroll <usd> Used to compute max_leg_pct + warnings
|
|
306
|
+
--window-days <n> Correlation lookback (default 30)
|
|
307
|
+
--max-corr <-1..1> Soft threshold for correlation warning
|
|
308
|
+
|
|
309
|
+
Build flags (universe + diversification):
|
|
310
|
+
--category <name> Restrict candidate pool by category
|
|
311
|
+
--series <ticker> Restrict to a series
|
|
312
|
+
--min-volume <n> Volume floor for candidates
|
|
313
|
+
--close-before <iso> Only markets closing before
|
|
314
|
+
--label <csv> Restrict cluster labels (substring match, comma-separated)
|
|
315
|
+
-q "<text>" Anchor candidate pool by free-text intent (semantic)
|
|
316
|
+
--ticker <ticker> Anchor candidate pool by ticker (semantic)
|
|
317
|
+
--tickers KX-A,KX-B Explicit candidate pool (universe.market_tickers)
|
|
318
|
+
--theme <name> Resolve theme registry → explicit candidate pool
|
|
319
|
+
--auto-probs Auto-fetch model probabilities (markets/edge)
|
|
320
|
+
and use Kelly sizing
|
|
321
|
+
-n <n> Number of legs (1-20)
|
|
322
|
+
--max-per-cluster <n> Cap legs per thematic cluster
|
|
323
|
+
--max-corr <x> Pairwise correlation cap (-1 to 1)
|
|
324
|
+
--limit <n> Candidate pool size (2-200)
|
|
325
|
+
--window-days <n> Correlation window (7-365)
|
|
326
|
+
|
|
327
|
+
Sizing flags (build & size):
|
|
328
|
+
--bankroll <usd> Required for Kelly sizing
|
|
329
|
+
--kelly <fraction> Kelly multiplier 0-1 (default 0.25)
|
|
330
|
+
--probs KX-A:0.62,KX-B:0.55 Model probabilities per ticker (manual)
|
|
331
|
+
--auto-probs --tickers KX-A,KX-B Auto-fetch via POST /markets/edge
|
|
332
|
+
--auto-probs --theme <name> Resolve theme + auto-fetch probabilities
|
|
333
|
+
--side <yes|no> Default leg side for "basket size" (default yes)
|
|
334
|
+
|
|
335
|
+
Backtest/candles flags:
|
|
336
|
+
--tickers <csv> Tickers (required if --theme is absent)
|
|
337
|
+
--theme <name> Resolve from editorial registry (top market per series)
|
|
338
|
+
--weights <csv> Optional weights, same length as tickers
|
|
339
|
+
--timeframe <1w|1m|3m|6m|1y> Window/bin size
|
|
340
|
+
|
|
341
|
+
Common:
|
|
342
|
+
--json JSON output
|
|
343
|
+
|
|
344
|
+
Recipes:
|
|
345
|
+
${p}basket build --category crypto --min-volume 10000 -n 8 --max-per-cluster 2 --max-corr 0.6
|
|
346
|
+
${p}basket build --label fed,cpi,fomc,gdp,jobs -n 5 --max-per-cluster 1 --max-corr 0.4
|
|
347
|
+
${p}basket build --tickers KX-A,KX-B,KX-C -n 2 --max-corr 0.5 # explicit candidate pool
|
|
348
|
+
${p}basket build --theme "Iran Escalation" -n 3 --max-per-cluster 1 --auto-probs --bankroll 1000
|
|
349
|
+
${p}basket backtest --tickers KX-A,KX-B,KX-C --weights 0.4,0.4,0.2 --timeframe 1y
|
|
350
|
+
${p}basket size --auto-probs --theme "Iran Escalation" --bankroll 1000 --kelly 0.25
|
|
351
|
+
${p}basket validate --theme "Iran Escalation" --bankroll 1000 # sanity-check before placing
|
|
352
|
+
${p}basket validate --tickers KX-A,KX-B --bankroll 1000 --max-corr 0.5`,
|
|
128
353
|
};
|
|
129
354
|
}
|
|
130
355
|
|
|
@@ -139,14 +364,44 @@ Quick start:
|
|
|
139
364
|
kalshi watch --theme crypto Continuous scan across a theme
|
|
140
365
|
|
|
141
366
|
Discovery:
|
|
142
|
-
search [theme|ticker|query] Find markets
|
|
143
|
-
search --
|
|
144
|
-
search
|
|
145
|
-
search
|
|
367
|
+
search [theme|ticker|query] Find markets (Octagon when key set, else local)
|
|
368
|
+
search --sort-by volume_24h Top-N by liquidity
|
|
369
|
+
search --aggregate-by series Roll up results to series level
|
|
370
|
+
search themes (Legacy) Kalshi category labels
|
|
371
|
+
search edge [--min-edge N] Edge ranking (Octagon when key set, else local)
|
|
372
|
+
similar <ticker> Semantic neighbors (embedding distance)
|
|
373
|
+
similar -q "free text" Semantic search by natural-language query
|
|
374
|
+
clusters [--label X] Browse thematic clusters
|
|
375
|
+
clusters <id> List markets in a cluster
|
|
376
|
+
clusters --behavioral Behavioral clusters (30-day return vectors)
|
|
377
|
+
clusters --ranked Rank clusters by historical basket return
|
|
378
|
+
peers <ticker> Find markets in the same cluster
|
|
379
|
+
events Octagon events (event ↔ outcome ladder)
|
|
380
|
+
events <event_ticker> Drill into one event's outcome probabilities
|
|
381
|
+
series Series rollup with 24h vol, market count
|
|
382
|
+
series <SERIES> Sub-markets in one series
|
|
383
|
+
series candles <SERIES> Series NAV (basket of top sub-markets)
|
|
384
|
+
catalysts upcoming --days 30 Markets closing soon, grouped by week
|
|
146
385
|
watch <ticker> Live price/orderbook feed
|
|
147
386
|
watch --theme <theme> Continuous theme scan (Ctrl+C to stop)
|
|
148
387
|
watch --refresh Force index rebuild before watching
|
|
149
388
|
|
|
389
|
+
Editorial themes (narrative registry):
|
|
390
|
+
themes List registered editorial themes
|
|
391
|
+
themes import Seed from data/themes_seo.json (25 starter themes)
|
|
392
|
+
themes show <name> Drill into one theme
|
|
393
|
+
themes report 25-theme dashboard with SEO + liquidity
|
|
394
|
+
themes audit Flag dead themes (high SEO + zero volume)
|
|
395
|
+
themes overlap Cross-theme dedupe report
|
|
396
|
+
themes create/delete/add-series/remove-series/set-search-volume/export
|
|
397
|
+
|
|
398
|
+
Portfolio construction:
|
|
399
|
+
correlate <t1> <t2> [...] Pairwise Pearson correlation matrix
|
|
400
|
+
basket build [filters] -n N Diversified basket with cluster + correlation caps
|
|
401
|
+
basket backtest --tickers ... NAV summary with Sharpe, max DD, win rate
|
|
402
|
+
basket size --bankroll $ --probs ... Fractional Kelly sizing for picked legs
|
|
403
|
+
basket candles --tickers ... OHLC bars for a weighted basket NAV
|
|
404
|
+
|
|
150
405
|
Analysis & Trading:
|
|
151
406
|
analyze <ticker> Full report: edge, drivers, Kelly sizing
|
|
152
407
|
analyze <ticker> --refresh Force fresh Octagon report
|
|
@@ -185,14 +440,44 @@ Quick start:
|
|
|
185
440
|
/watch --theme crypto Continuous scan across a theme
|
|
186
441
|
|
|
187
442
|
Discovery:
|
|
188
|
-
/search [theme|ticker|query] Find markets
|
|
189
|
-
/search --
|
|
190
|
-
/search
|
|
191
|
-
/search
|
|
443
|
+
/search [theme|ticker|query] Find markets (Octagon when key set, else local)
|
|
444
|
+
/search --sort-by volume_24h Top-N by liquidity
|
|
445
|
+
/search --aggregate-by series Roll up results to series level
|
|
446
|
+
/search themes (Legacy) Kalshi category labels
|
|
447
|
+
/search edge [--min-edge N] Edge ranking (Octagon when key set, else local)
|
|
448
|
+
/similar <ticker> Semantic neighbors (embedding distance)
|
|
449
|
+
/similar -q "free text" Semantic search by natural-language query
|
|
450
|
+
/clusters [--label X] Browse thematic clusters
|
|
451
|
+
/clusters <id> List markets in a cluster
|
|
452
|
+
/clusters --behavioral Behavioral clusters (30-day return vectors)
|
|
453
|
+
/clusters --ranked Rank clusters by historical basket return
|
|
454
|
+
/peers <ticker> Find markets in the same cluster
|
|
455
|
+
/events Octagon events (event ↔ outcome ladder)
|
|
456
|
+
/events <event_ticker> Drill into one event's outcome probabilities
|
|
457
|
+
/series Series rollup with 24h vol, market count
|
|
458
|
+
/series <SERIES> Sub-markets in one series
|
|
459
|
+
/series candles <SERIES> Series NAV (basket of top sub-markets)
|
|
460
|
+
/catalysts upcoming --days 30 Markets closing soon, grouped by week
|
|
192
461
|
/watch <ticker> Live price/orderbook feed
|
|
193
462
|
/watch --theme <theme> Continuous theme scan (Esc to stop)
|
|
194
463
|
/watch --refresh Force index rebuild before watching
|
|
195
464
|
|
|
465
|
+
Editorial themes (narrative registry):
|
|
466
|
+
/themes List registered editorial themes
|
|
467
|
+
/themes import Seed from data/themes_seo.json (25 starter themes)
|
|
468
|
+
/themes show <name> Drill into one theme
|
|
469
|
+
/themes report 25-theme dashboard with SEO + liquidity
|
|
470
|
+
/themes audit Flag dead themes (high SEO + zero volume)
|
|
471
|
+
/themes overlap Cross-theme dedupe report
|
|
472
|
+
/themes create/delete/add-series/remove-series/set-search-volume/export
|
|
473
|
+
|
|
474
|
+
Portfolio construction:
|
|
475
|
+
/correlate <t1> <t2> [...] Pairwise Pearson correlation matrix
|
|
476
|
+
/basket build [filters] -n N Diversified basket with cluster + correlation caps
|
|
477
|
+
/basket backtest --tickers ... NAV summary with Sharpe, max DD, win rate
|
|
478
|
+
/basket size --bankroll $ --probs ... Fractional Kelly sizing for picked legs
|
|
479
|
+
/basket candles --tickers ... OHLC bars for a weighted basket NAV
|
|
480
|
+
|
|
196
481
|
Analysis:
|
|
197
482
|
/backtest Model accuracy scorecard + live edge scanner
|
|
198
483
|
/analyze <ticker> Full report: edge, drivers, Kelly sizing
|
package/src/commands/index.ts
CHANGED
|
@@ -16,7 +16,10 @@ function defaultArgs(overrides: Partial<ParsedArgs>): ParsedArgs {
|
|
|
16
16
|
subcommand: 'chat', positionalArgs: [], json: false,
|
|
17
17
|
live: false, refresh: false, report: false, dryRun: false,
|
|
18
18
|
verbose: false, performance: false, resolved: false,
|
|
19
|
-
unresolved: false,
|
|
19
|
+
unresolved: false,
|
|
20
|
+
behavioral: false, ranked: false, showCluster: false,
|
|
21
|
+
activeOnly: false, cells: false, autoProbs: false,
|
|
22
|
+
parseErrors: [],
|
|
20
23
|
...overrides,
|
|
21
24
|
};
|
|
22
25
|
}
|
|
@@ -27,6 +30,16 @@ import { reviewPortfolio, formatReviewHuman } from './review.js';
|
|
|
27
30
|
import { buildHelp, validateTradeArgs } from './help.js';
|
|
28
31
|
import { fetchMarketQuote } from './helpers.js';
|
|
29
32
|
import { trackEvent } from '../utils/telemetry.js';
|
|
33
|
+
import { parseArgs } from './parse-args.js';
|
|
34
|
+
import { handleSimilar, formatSimilarHuman } from './similar.js';
|
|
35
|
+
import { handleClusters, formatClustersHuman } from './clusters.js';
|
|
36
|
+
import { handlePeers, formatPeersHuman } from './peers.js';
|
|
37
|
+
import { handleCorrelate, formatCorrelationHuman } from './correlate.js';
|
|
38
|
+
import { handleBasket, formatBasketHuman } from './basket.js';
|
|
39
|
+
import { handleEvents, formatEventsHuman } from './events.js';
|
|
40
|
+
import { handleSeries, formatSeriesHuman } from './series.js';
|
|
41
|
+
import { handleEditorialThemes, formatEditorialThemesHuman } from './editorial-themes.js';
|
|
42
|
+
import { handleCatalysts, formatCatalystsHuman } from './catalysts.js';
|
|
30
43
|
|
|
31
44
|
export interface CommandResult {
|
|
32
45
|
output: string;
|
|
@@ -49,7 +62,28 @@ export async function handleSlashCommand(input: string): Promise<CommandResult |
|
|
|
49
62
|
const parts = trimmed.slice(1).trim().split(/\s+/);
|
|
50
63
|
const command = parts[0]?.toLowerCase();
|
|
51
64
|
const args = parts.slice(1);
|
|
52
|
-
|
|
65
|
+
// Enrich Octagon-Kalshi commands with subview/mode flags so analytics can
|
|
66
|
+
// distinguish e.g. "basket build" vs "basket backtest", or thematic vs
|
|
67
|
+
// behavioral clusters. Outer command name is always tracked.
|
|
68
|
+
const slashMeta: Record<string, string | boolean> = { command: command ?? '' };
|
|
69
|
+
if (command === 'basket') {
|
|
70
|
+
const sub = args[0]?.toLowerCase();
|
|
71
|
+
if (sub === 'build' || sub === 'backtest' || sub === 'size' || sub === 'candles') {
|
|
72
|
+
slashMeta.subview = sub;
|
|
73
|
+
}
|
|
74
|
+
slashMeta.kelly_sizing = args.includes('--bankroll');
|
|
75
|
+
} else if (command === 'clusters') {
|
|
76
|
+
slashMeta.behavioral = args.includes('--behavioral');
|
|
77
|
+
slashMeta.ranked = args.includes('--ranked');
|
|
78
|
+
} else if (command === 'peers') {
|
|
79
|
+
slashMeta.behavioral = args.includes('--behavioral');
|
|
80
|
+
slashMeta.show_cluster = args.includes('--show-cluster');
|
|
81
|
+
} else if (command === 'similar') {
|
|
82
|
+
slashMeta.anchor = args.includes('-q') || args.includes('--query') ? 'query' : 'ticker';
|
|
83
|
+
} else if (command === 'search') {
|
|
84
|
+
slashMeta.remote = !!process.env.OCTAGON_API_KEY;
|
|
85
|
+
}
|
|
86
|
+
trackEvent('slash_command', slashMeta);
|
|
53
87
|
|
|
54
88
|
switch (command) {
|
|
55
89
|
case 'help': {
|
|
@@ -79,11 +113,24 @@ export async function handleSlashCommand(input: string): Promise<CommandResult |
|
|
|
79
113
|
case 'cancel':
|
|
80
114
|
return handleCancel(args[0]);
|
|
81
115
|
|
|
82
|
-
// ─── /
|
|
83
|
-
//
|
|
116
|
+
// ─── /themes (editorial registry) ────────────────────────────────
|
|
117
|
+
// The bare /themes call now hits the editorial-themes registry. Legacy
|
|
118
|
+
// "Kalshi category labels" is still reachable via /search themes.
|
|
84
119
|
case 'themes': {
|
|
85
|
-
const
|
|
86
|
-
|
|
120
|
+
const parsed = parseArgs(['themes', ...args]);
|
|
121
|
+
const sub = parsed.positionalArgs[0]?.toLowerCase();
|
|
122
|
+
const isAsync = sub === 'report' || sub === 'audit';
|
|
123
|
+
if (!isAsync) {
|
|
124
|
+
const resp = await handleEditorialThemes(parsed);
|
|
125
|
+
return { output: resp.ok ? formatEditorialThemesHuman(resp.data) : (resp.error?.message ?? 'themes failed') };
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
output: `Building themes ${sub} (this pulls the full Kalshi universe)...`,
|
|
129
|
+
asyncFollowUp: async () => {
|
|
130
|
+
const resp = await handleEditorialThemes(parsed);
|
|
131
|
+
return resp.ok ? formatEditorialThemesHuman(resp.data) : (resp.error?.message ?? 'themes failed');
|
|
132
|
+
},
|
|
133
|
+
};
|
|
87
134
|
}
|
|
88
135
|
|
|
89
136
|
// ─── /analyze ────────────────────────────────────────────────────
|
|
@@ -126,6 +173,90 @@ export async function handleSlashCommand(input: string): Promise<CommandResult |
|
|
|
126
173
|
};
|
|
127
174
|
}
|
|
128
175
|
|
|
176
|
+
// ─── Octagon Kalshi search/clusters/basket ───────────────────────
|
|
177
|
+
case 'similar': {
|
|
178
|
+
const parsed = parseArgs(['similar', ...args]);
|
|
179
|
+
return {
|
|
180
|
+
output: 'Querying Octagon for similar markets...',
|
|
181
|
+
asyncFollowUp: async () => {
|
|
182
|
+
const resp = await handleSimilar(parsed);
|
|
183
|
+
return resp.ok ? formatSimilarHuman(resp.data) : (resp.error?.message ?? 'similar failed');
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
case 'clusters': {
|
|
188
|
+
const parsed = parseArgs(['clusters', ...args]);
|
|
189
|
+
return {
|
|
190
|
+
output: 'Querying Octagon for clusters...',
|
|
191
|
+
asyncFollowUp: async () => {
|
|
192
|
+
const resp = await handleClusters(parsed);
|
|
193
|
+
return resp.ok ? formatClustersHuman(resp.data) : (resp.error?.message ?? 'clusters failed');
|
|
194
|
+
},
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
case 'peers': {
|
|
198
|
+
const parsed = parseArgs(['peers', ...args]);
|
|
199
|
+
return {
|
|
200
|
+
output: 'Querying Octagon for cluster peers...',
|
|
201
|
+
asyncFollowUp: async () => {
|
|
202
|
+
const resp = await handlePeers(parsed);
|
|
203
|
+
return resp.ok ? formatPeersHuman(resp.data) : (resp.error?.message ?? 'peers failed');
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
case 'correlate': {
|
|
208
|
+
const parsed = parseArgs(['correlate', ...args]);
|
|
209
|
+
return {
|
|
210
|
+
output: 'Computing correlation matrix...',
|
|
211
|
+
asyncFollowUp: async () => {
|
|
212
|
+
const resp = await handleCorrelate(parsed);
|
|
213
|
+
return resp.ok ? formatCorrelationHuman(resp.data) : (resp.error?.message ?? 'correlate failed');
|
|
214
|
+
},
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
case 'basket': {
|
|
218
|
+
const parsed = parseArgs(['basket', ...args]);
|
|
219
|
+
const sub = parsed.positionalArgs[0] ?? '';
|
|
220
|
+
return {
|
|
221
|
+
output: `Running basket ${sub || '(no subcommand)'}...`,
|
|
222
|
+
asyncFollowUp: async () => {
|
|
223
|
+
const resp = await handleBasket(parsed);
|
|
224
|
+
return resp.ok ? formatBasketHuman(resp.data) : (resp.error?.message ?? 'basket failed');
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
case 'events': {
|
|
229
|
+
const parsed = parseArgs(['events', ...args]);
|
|
230
|
+
return {
|
|
231
|
+
output: 'Querying Octagon events...',
|
|
232
|
+
asyncFollowUp: async () => {
|
|
233
|
+
const resp = await handleEvents(parsed);
|
|
234
|
+
return resp.ok ? formatEventsHuman(resp.data) : (resp.error?.message ?? 'events failed');
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
case 'series': {
|
|
239
|
+
const parsed = parseArgs(['series', ...args]);
|
|
240
|
+
const sub = parsed.positionalArgs[0]?.toLowerCase();
|
|
241
|
+
return {
|
|
242
|
+
output: sub === 'candles' ? 'Building series NAV...' : 'Rolling up Kalshi series...',
|
|
243
|
+
asyncFollowUp: async () => {
|
|
244
|
+
const resp = await handleSeries(parsed);
|
|
245
|
+
return resp.ok ? formatSeriesHuman(resp.data) : (resp.error?.message ?? 'series failed');
|
|
246
|
+
},
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
case 'catalysts': {
|
|
250
|
+
const parsed = parseArgs(['catalysts', ...args]);
|
|
251
|
+
return {
|
|
252
|
+
output: 'Loading upcoming catalysts...',
|
|
253
|
+
asyncFollowUp: async () => {
|
|
254
|
+
const resp = await handleCatalysts(parsed);
|
|
255
|
+
return resp.ok ? formatCatalystsHuman(resp.data) : (resp.error?.message ?? 'catalysts failed');
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
|
|
129
260
|
case 'config':
|
|
130
261
|
// Fall through to agent — better handled by the LLM
|
|
131
262
|
return null;
|