-
Notifications
You must be signed in to change notification settings - Fork 64
v2.1.0 #27
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
v2.1.0 #27
Changes from all commits
edcc8e4
67bbcf5
75c2fdf
6c1b686
08ee9d8
22c3dea
fc5bb5a
457d291
e78314c
e0fabd1
8c6edd3
589d3be
3736344
9c90979
40ab646
ce964c8
3c7b071
ebe8634
bd369da
b2921d1
465d262
9ce36a2
0d5ddc0
63af512
1bf4bab
87fc0c4
ebb427b
8f70b72
69785d6
cab6352
81af43a
dc49f30
7887b4b
594e988
3baa764
3be9338
6a4e5e9
ba5f1cd
59c1074
680114a
47520d8
de32831
4e4192e
041c542
97d37d9
fd11c1a
9d1a054
5d689e7
bd5af36
e11e4d9
05021dc
9854e25
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,10 +1,11 @@ | ||||||||||||||||||||
| { | ||||||||||||||||||||
| "name": "kalshi-deep-trading-bot", | ||||||||||||||||||||
| "version": "2.0.22", | ||||||||||||||||||||
| "description": "Kalshi Deep Trading Bot - AI-powered prediction market terminal.", | ||||||||||||||||||||
| "name": "kalshi-trading-bot-cli", | ||||||||||||||||||||
| "version": "2.1.0", | ||||||||||||||||||||
| "description": "Kalshi Trading Bot CLI - AI-powered prediction market terminal.", | ||||||||||||||||||||
| "type": "module", | ||||||||||||||||||||
| "main": "src/index.tsx", | ||||||||||||||||||||
| "bin": { | ||||||||||||||||||||
| "kalshi-trading-bot-cli": "./src/index.tsx", | ||||||||||||||||||||
| "kalshi-deep-trading-bot": "./src/index.tsx" | ||||||||||||||||||||
| }, | ||||||||||||||||||||
|
Comment on lines
7
to
10
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Expose the binary name your help output advertises. The CLI help still shows commands as One possible fix "bin": {
+ "kalshi": "./src/index.tsx",
"kalshi-trading-bot-cli": "./src/index.tsx",
"kalshi-deep-trading-bot": "./src/index.tsx"
},📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||
| "scripts": { | ||||||||||||||||||||
|
|
||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,172 @@ | ||
| import type { Database } from 'bun:sqlite'; | ||
| import { callKalshiApi } from '../tools/kalshi/api.js'; | ||
| import type { KalshiMarket } from '../tools/kalshi/types.js'; | ||
|
|
||
| const CONCURRENCY = 10; | ||
|
|
||
| export interface SettledMarket { | ||
| ticker: string; | ||
| event_ticker: string; | ||
| result: 'yes' | 'no'; | ||
| close_time: string; | ||
| series_category: string; | ||
| last_price: number; // last traded price (0-1) | ||
| volume: number; // lifetime trading volume (used for tradeability gate) | ||
| } | ||
|
|
||
| export interface OpenMarket { | ||
| ticker: string; | ||
| event_ticker: string; | ||
| market_prob: number; // current trading price (0-1) | ||
| close_time: string; | ||
| series_category: string; | ||
| volume: number; // lifetime trading volume (tradeability gate) | ||
| volume_24h: number; // 24-hour volume (liquidity-now gate) | ||
| } | ||
|
|
||
| /** Parse market price from Kalshi response (handles both cents and dollars formats). */ | ||
| function parsePrice(m: KalshiMarket): number { | ||
| const dollars = parseFloat(m.last_price_dollars ?? ''); | ||
| if (Number.isFinite(dollars)) return dollars; | ||
| return typeof m.last_price === 'number' ? m.last_price / 100 : 0; | ||
| } | ||
|
|
||
| /** Parse lifetime volume (prefers volume_fp string from new API). */ | ||
| function parseVolume(m: KalshiMarket): number { | ||
| const fp = parseFloat(m.volume_fp ?? ''); | ||
| if (Number.isFinite(fp)) return fp; | ||
| return typeof m.volume === 'number' ? m.volume : 0; | ||
| } | ||
|
|
||
| /** Parse 24h volume (prefers volume_24h_fp string from new API). */ | ||
| function parseVolume24h(m: KalshiMarket): number { | ||
| const fp = parseFloat(m.volume_24h_fp ?? ''); | ||
| if (Number.isFinite(fp)) return fp; | ||
| return typeof m.volume_24h === 'number' ? m.volume_24h : 0; | ||
| } | ||
|
|
||
| /** Fetch event markets from Kalshi, returning empty array on error. */ | ||
| async function fetchEventMarkets(eventTicker: string): Promise<KalshiMarket[]> { | ||
| try { | ||
| const response = await callKalshiApi('GET', `/events/${eventTicker}`, { | ||
| params: { with_nested_markets: true }, | ||
| }); | ||
| if (!response || typeof response !== 'object') return []; | ||
| const obj = response as Record<string, unknown>; | ||
| const event = (obj.event ?? obj) as Record<string, unknown>; | ||
| const markets = event.markets; | ||
| return Array.isArray(markets) ? markets as KalshiMarket[] : []; | ||
| } catch { | ||
| return []; | ||
| } | ||
|
Comment on lines
+48
to
+61
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don’t hide Kalshi fetch failures as empty events. Lines 42-43 make an outage or rate-limit indistinguishable from “this event has no nested markets”. That silently drops data from both discovery paths, so downstream callers can return a successful but incomplete backtest instead of surfacing that discovery was partial. 🤖 Prompt for AI Agents |
||
| } | ||
|
|
||
| /** Build the event discovery query with optional category filter and extra WHERE clauses. */ | ||
| function buildEventQuery( | ||
| extraWhere: string, | ||
| category?: string, | ||
| ): { query: string; params: Record<string, string> } { | ||
| let query = `SELECT event_ticker, MAX(series_category) as category | ||
| FROM octagon_reports r WHERE variant_used = 'events-api'${extraWhere}`; | ||
| const params: Record<string, string> = {}; | ||
| if (category) { | ||
| query += ' AND LOWER(series_category) LIKE $cat'; | ||
| params.$cat = `%${category.toLowerCase()}%`; | ||
| } | ||
| query += ' GROUP BY event_ticker'; | ||
| return { query, params }; | ||
| } | ||
|
|
||
| /** Process items in parallel batches of `concurrency`. */ | ||
| async function parallelMap<T, R>( | ||
| items: T[], | ||
| fn: (item: T) => Promise<R>, | ||
| concurrency: number, | ||
| ): Promise<R[]> { | ||
| const results: R[] = []; | ||
| for (let i = 0; i < items.length; i += concurrency) { | ||
| const batch = items.slice(i, i + concurrency); | ||
| const batchResults = await Promise.all(batch.map(fn)); | ||
| results.push(...batchResults); | ||
| } | ||
| return results; | ||
| } | ||
|
|
||
| /** | ||
| * Discover settled Kalshi markets that have Octagon coverage with history. | ||
| */ | ||
| export async function discoverSettledMarkets( | ||
| db: Database, | ||
| opts?: { category?: string; days?: number }, | ||
| ): Promise<SettledMarket[]> { | ||
| // Filter to events whose close_time falls within the lookback window. | ||
| // We deliberately do NOT gate on has_history: the flag is set at prefetch | ||
| // time and lags reality, so we rely on empty-data fallthrough later. | ||
| let extraWhere = ''; | ||
| if (opts?.days) { | ||
| const cutoff = new Date(Date.now() - opts.days * 24 * 60 * 60 * 1000).toISOString(); | ||
| extraWhere += ` AND (close_time IS NULL OR close_time >= '${cutoff}')`; | ||
| } | ||
| const { query, params } = buildEventQuery(extraWhere, opts?.category); | ||
| const events = db.query(query).all(params) as Array<{ event_ticker: string; category: string | null }>; | ||
|
|
||
| const batchResults = await parallelMap(events, async ({ event_ticker, category: cat }) => { | ||
| const markets = await fetchEventMarkets(event_ticker); | ||
| const settled: SettledMarket[] = []; | ||
|
|
||
| for (const m of markets) { | ||
| const result = (m.result ?? '').toLowerCase(); | ||
| if (result !== 'yes' && result !== 'no') continue; | ||
|
|
||
| settled.push({ | ||
| ticker: m.ticker, | ||
| event_ticker, | ||
| result: result as 'yes' | 'no', | ||
| close_time: m.close_time ?? '', | ||
| series_category: cat ?? '', | ||
| last_price: parsePrice(m), | ||
| volume: parseVolume(m), | ||
| }); | ||
| } | ||
| return settled; | ||
| }, CONCURRENCY); | ||
|
|
||
| return batchResults.flat(); | ||
| } | ||
|
|
||
| /** | ||
| * Discover open Kalshi markets that have Octagon coverage. | ||
| */ | ||
| export async function discoverOpenMarkets( | ||
| db: Database, | ||
| opts?: { category?: string }, | ||
| ): Promise<OpenMarket[]> { | ||
| const { query: q2, params: p2 } = buildEventQuery('', opts?.category); | ||
| const events2 = db.query(q2).all(p2) as Array<{ event_ticker: string; category: string | null }>; | ||
|
|
||
| const batchResults = await parallelMap(events2, async ({ event_ticker, category: cat }) => { | ||
| const markets = await fetchEventMarkets(event_ticker); | ||
| const open: OpenMarket[] = []; | ||
|
|
||
| for (const m of markets) { | ||
| const status = (m.status ?? '').toLowerCase(); | ||
| if (status !== 'open' && status !== 'active') continue; | ||
|
|
||
| const marketProb = parsePrice(m); | ||
| if (marketProb <= 0) continue; | ||
|
|
||
| open.push({ | ||
| ticker: m.ticker, | ||
| event_ticker, | ||
| market_prob: marketProb, | ||
| close_time: m.close_time ?? '', | ||
| series_category: cat ?? '', | ||
| volume: parseVolume(m), | ||
| volume_24h: parseVolume24h(m), | ||
| }); | ||
| } | ||
| return open; | ||
| }, CONCURRENCY); | ||
|
|
||
| return batchResults.flat(); | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clarify workflow precedence for README changes.
Line 13 mandates README updates, but repo guidance says docs should not be created/updated unless explicitly requested. Please resolve this conflict to avoid inconsistent contributor behavior.
Based on learnings "Do not create README or documentation files unless explicitly asked".
🤖 Prompt for AI Agents