Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
edcc8e4
Fix wrong model probability for multi-outcome events
melvinmt Apr 13, 2026
67bbcf5
Fix cacheMiss false positive for multi-outcome markets with per-marke…
melvinmt Apr 13, 2026
75c2fdf
Pre-fetch all Octagon events via REST API on app initialization
melvinmt Apr 13, 2026
6c1b686
Add backtest feature types and metrics modules
melvinmt Apr 13, 2026
08ee9d8
Add Octagon history API fetcher and octagon_history DB table
melvinmt Apr 13, 2026
22c3dea
Add backtest command with resolved scorecard and unresolved edge scanner
melvinmt Apr 13, 2026
fc5bb5a
Use has_history field from events API response instead of second API …
melvinmt Apr 13, 2026
457d291
Fix backtest: filter multi-market events, add category breakdown, sna…
melvinmt Apr 13, 2026
e78314c
Harden prefetch: validate API responses, scope to runtime DB, fix err…
melvinmt Apr 13, 2026
e0fabd1
Fix fetcher type safety, metrics correctness, and cache column preser…
melvinmt Apr 13, 2026
8c6edd3
Fix date filtering, CSV escaping, flag conflicts, timestamp validation
melvinmt Apr 13, 2026
589d3be
Fix type errors: add missing required ParsedArgs fields to helper fac…
melvinmt Apr 13, 2026
3736344
Clean up discovery, harden types, atomic upsert, tighten constraint c…
melvinmt Apr 13, 2026
9c90979
Skip mutually_exclusive events, remove logging, fix nulls and sub-hou…
melvinmt Apr 13, 2026
40ab646
De-duplicate discovery queries by event_ticker using GROUP BY
melvinmt Apr 13, 2026
ce964c8
Fix parsePrice zero-price bug and extract query builder helper
melvinmt Apr 13, 2026
3c7b071
Add backtest to help text and CLI overview
melvinmt Apr 13, 2026
ebe8634
Add /backtest TUI slash command and fix floating point noise
melvinmt Apr 13, 2026
bd369da
Add confidence_score column to octagon_reports and use in backtest
melvinmt Apr 13, 2026
b2921d1
Support per-market probabilities via outcome_probabilities for multi-…
melvinmt Apr 13, 2026
465d262
Narrow error handling, atomic metadata writes, clean up dead code
melvinmt Apr 14, 2026
9ce36a2
Add backtest documentation to README
melvinmt Apr 14, 2026
0d5ddc0
Bump version to 2.0.23
melvinmt Apr 14, 2026
63af512
Rename project from Kalshi Deep Trading Bot to Kalshi Trading Bot CLI
melvinmt Apr 14, 2026
1bf4bab
Add Star History chart to README
melvinmt Apr 14, 2026
87fc0c4
Add /backtest to TUI autocomplete and intro screen
melvinmt Apr 14, 2026
ebb427b
Show progress message immediately when running /backtest in TUI
melvinmt Apr 14, 2026
8f70b72
Handle subscription-required errors for history API gracefully
melvinmt Apr 14, 2026
69785d6
Backtest V2: unified lookback model with --days flag
melvinmt Apr 14, 2026
cab6352
Clarify minEdge scale comment and wrap prefetch INSERT+UPDATE in tran…
melvinmt Apr 14, 2026
81af43a
Add command signature update checklist to CLAUDE.md
melvinmt Apr 14, 2026
dc49f30
Add backward-compat bin alias, validate history API, remove stale fla…
melvinmt Apr 14, 2026
7887b4b
Add search edge: instant edge scanner from cached Octagon data
melvinmt Apr 14, 2026
594e988
Add --limit to README flags table and mention edge scan in intro screen
melvinmt Apr 14, 2026
3baa764
Bump version to 2.1.0
melvinmt Apr 14, 2026
3be9338
Use prefetched events API data to avoid individual Octagon cache calls
melvinmt Apr 14, 2026
6a4e5e9
Fix code block lint, allow unresolved scan with subscription notice, …
melvinmt Apr 14, 2026
ba5f1cd
Use larger braille block spinner frames for more visible animation
melvinmt Apr 14, 2026
59c1074
Animate spinner on AnswerBox during async follow-up commands
melvinmt Apr 14, 2026
680114a
Filter out 0% and 100% market prices from edge scan (no liquidity/set…
melvinmt Apr 14, 2026
47520d8
Add autocomplete for /search edge, /backtest flags, and /help backtest
melvinmt Apr 14, 2026
de32831
Exclude markets where either probability is <1% or >99% from edge scan
melvinmt Apr 14, 2026
4e4192e
Accept --theme as alias for --category in TUI /search edge handler
melvinmt Apr 14, 2026
041c542
Filter resolved events from edge scan using close_time instead of pro…
melvinmt Apr 14, 2026
97d37d9
Add close_time filter to tryFromPrefetch and browse extractAllOutcome…
melvinmt Apr 15, 2026
fd11c1a
Use history API days and exclude_empty_model params for cleaner backt…
melvinmt Apr 15, 2026
9d1a054
Use exclude_empty_model=true on history API, fix days caching
melvinmt Apr 15, 2026
5d689e7
Skip events with 0% model and market probability during prefetch (inc…
melvinmt Apr 15, 2026
bd5af36
Skip events with 0 or null model_probability during prefetch
melvinmt Apr 15, 2026
e11e4d9
Fix backtest accuracy: skip markets without valid historical snapshot
melvinmt Apr 15, 2026
05021dc
Fix backtest coverage, cache freshness, and tradeable filtering
melvinmt Apr 16, 2026
9854e25
Fix volume parsing: prefer new Kalshi volume_fp/volume_24h_fp fields
melvinmt Apr 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
- Skills: `src/skills/` (SKILL.md-based extensible workflows, e.g. DCF valuation)
- Utils: `src/utils/` (env, config, caching, token estimation, markdown tables)
- Evals: `src/evals/` (LangSmith evaluation runner with Ink UI)
- Config: `.kalshi-deep-trading-bot/settings.json` (persisted model/provider selection)
- Config: `.kalshi-trading-bot-cli/settings.json` (persisted model/provider selection)
- Environment: `.env` (API keys; see `env.example`)
- Scripts: `scripts/release.sh`

Expand Down Expand Up @@ -100,5 +100,5 @@
## Security

- API keys stored in `.env` (gitignored). Users can also enter keys interactively via the CLI.
- Config stored in `.kalshi-deep-trading-bot/settings.json` (gitignored).
- Config stored in `.kalshi-trading-bot-cli/settings.json` (gitignored).
- Never commit or expose real API keys, tokens, or credentials.
10 changes: 10 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,13 @@
## Workflow

- Always commit and push after each change.
- When a command's flags or signature changes, update ALL of these:
- `src/commands/parse-args.ts` — flag parsing and `ParsedArgs` interface
- `src/commands/help.ts` — detailed help topic and overview section
- `src/commands/index.ts` — TUI slash command handler and `defaultArgs()`
- `src/commands/dispatch.ts` — CLI dispatch block
- `src/cli.ts` — autocomplete `slashCommands` array
- `src/components/intro.ts` — welcome screen command list
- `README.md` — commands table, flags table, and examples
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

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
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` at line 13, Update the sentence on CLAUDE.md that currently
mandates adding `README.md — commands table, flags table, and examples` (line
mentioning README) so it aligns with repo policy: change it from a hard
requirement to a conditional instruction (e.g., only add or update README.md
when documentation changes are explicitly requested) and/or add a short note
referencing the "Do not create README or documentation files unless explicitly
asked" guidance; ensure the change edits the exact text that mentions `README.md
— commands table, flags table, and examples` so contributors understand README
updates are conditional.

- `src/__tests__/e2e.test.ts` — `makeParsedArgs()` defaults
- `src/gateway/commands/handler.ts` — `makeArgs()` defaults
2 changes: 1 addition & 1 deletion GUIDE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Kalshi Deep Trading Bot — User Guide
# Kalshi Trading Bot CLI — User Guide

AI-powered prediction market terminal for [Kalshi](https://kalshi.com). Ask natural language questions, research markets, and trade — all from your terminal.

Expand Down
73 changes: 69 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ Runs deep fundamental research on every market — independent probability estim

Integrates with the [Octagon Research API](https://app.octagonai.co) for AI-generated probability estimates that power the edge detection engine.

![Kalshi Deep Trading Bot](assets/screenshot.png)
![Kalshi Trading Bot CLI](assets/screenshot.png)

## Quick Start

```bash
git clone https://github.com/OctagonAI/kalshi-deep-trading-bot-cli.git
cd kalshi-deep-trading-bot-cli
git clone https://github.com/OctagonAI/kalshi-trading-bot-cli.git
cd kalshi-trading-bot-cli
bun install
bun start
```
Expand All @@ -26,7 +26,7 @@ The setup wizard runs automatically on first launch — it walks you through API
```
$ bun start

Welcome to Kalshi Deep Trading Bot
Welcome to Kalshi Trading Bot CLI
Type help for commands, or just ask a question.

> search crypto
Expand Down Expand Up @@ -73,12 +73,14 @@ Type help for commands, or just ask a question.
| Command | Description |
|---------|-------------|
| `search [theme\|ticker\|query]` | Find markets by keyword or theme |
| `search edge [--min-edge N]` | Scan all markets by model edge |
| `analyze <ticker>` | Deep analysis: edge, drivers, Kelly sizing |
| `watch <ticker>` | Live price and orderbook feed |
| `watch --theme <theme>` | Continuous theme scan |
| `buy <ticker> <count> [price] [yes\|no]` | Buy contracts |
| `sell <ticker> <count> [price] [yes\|no]` | Sell contracts |
| `cancel <order_id>` | Cancel a resting order |
| `backtest` | Model accuracy scorecard + live edge scanner |
| `portfolio` | Positions, P&L, risk snapshot |
| `setup` | Re-run setup wizard (inside TUI) |
| `init` | Launch setup wizard from CLI (`bun start init`) |
Expand All @@ -97,6 +99,59 @@ Type help for commands, or just ask a question.
| `--min-edge <n>` | Minimum edge threshold |
| `--interval <min>` | Scan interval in minutes (watch) |
| `--live` | Force 15m scan interval (watch) |
| `--days <n>` | Lookback period in days (backtest, default 30) |
| `--max-age <n>` | Reject predictions older than N days (backtest, default = `--days`) |
| `--resolved` | Resolved markets only (backtest) |
| `--unresolved` | Open markets only (backtest) |
| `--category <cat>` | Filter by category (backtest, search edge) |
| `--limit <n>` | Max results to show (search edge, default 20) |
| `--min-volume <n>` | Min lifetime volume for a tradeable contract (backtest, default 1) |
| `--min-price <n>` | Min contract price, 0-100 scale (backtest, default 5) |
| `--max-price <n>` | Max contract price, 0-100 scale (backtest, default 95) |
| `--export <path>` | Export per-market CSV (backtest) |

### Backtesting

Does the model find real edge? Look back N days, compare what the model said then to where the market is now.

- **Resolved** — scored against Kalshi settlement (YES=100%, NO=0%)
- **Unresolved** — mark-to-market vs current Kalshi trading price

```bash
bun start backtest # 30-day lookback (default)
bun start backtest --days 60 # 60-day lookback
bun start backtest --max-age 14 # only score predictions <=14d old
bun start backtest --resolved # resolved only
bun start backtest --unresolved --min-edge 10 # unresolved, 10pp threshold
bun start backtest --category crypto # filter by category
bun start backtest --min-volume 10 --min-price 5 --max-price 95 # tradeable contracts only
bun start backtest --export results.csv # per-market detail
```

```text
Octagon Backtest — 30-day lookback (03/15 – 04/14)
══════════════════════════════════════════════════════════

VERDICT: Model has edge (Skill +12.5% [CI: +4.1%, +20.8%]; ROI +7.8%)

Events 83
Markets 247 (142 resolved, 105 unresolved)
Brier (Octagon) 0.168
Brier (Market) 0.192
Skill Score +12.5% [95% CI: +4.1% to +20.8%]
Hit rate 61.4% [95% CI: 54.2% to 68.1%]
Flat-bet P&L +$14.38 (ROI: +7.8%)

RESOLVED (142 markets)
Ticker Model Mkt Then Outcome Edge P&L
KXBTC-26APR-B95000 72% 58% YES 100% +14pp +$0.42
...

UNRESOLVED (105 markets)
Ticker Model Mkt Then Now Edge M2M
KXBTC-26MAY-B110000 71% 58% 68% +13pp +$0.10
...
```

### Demo Mode

Expand Down Expand Up @@ -268,6 +323,16 @@ TELEMETRY_ENABLED=false bun start

See the [User Guide](GUIDE.md) for detailed usage instructions, examples, and tips.

## Star History

<a href="https://www.star-history.com/?repos=OctagonAI%2Fkalshi-trading-bot-cli&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/chart?repos=OctagonAI/kalshi-trading-bot-cli&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/chart?repos=OctagonAI/kalshi-trading-bot-cli&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/chart?repos=OctagonAI/kalshi-trading-bot-cli&type=date&legend=top-left" />
</picture>
</a>

## License

MIT License — see [LICENSE](LICENSE) for details.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Expose the binary name your help output advertises.

The CLI help still shows commands as kalshi ..., but this package only installs kalshi-trading-bot-cli and kalshi-deep-trading-bot. A global install won't have the executable the built-in help tells users to run.

One possible fix
  "bin": {
+   "kalshi": "./src/index.tsx",
    "kalshi-trading-bot-cli": "./src/index.tsx",
    "kalshi-deep-trading-bot": "./src/index.tsx"
  },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
"bin": {
"kalshi-trading-bot-cli": "./src/index.tsx",
"kalshi-deep-trading-bot": "./src/index.tsx"
},
"bin": {
"kalshi": "./src/index.tsx",
"kalshi-trading-bot-cli": "./src/index.tsx",
"kalshi-deep-trading-bot": "./src/index.tsx"
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@package.json` around lines 7 - 10, The package advertises a `kalshi` CLI but
the package.json "bin" only exposes "kalshi-trading-bot-cli" and
"kalshi-deep-trading-bot", so global installs won't provide the advertised
executable; either add a "kalshi": "./src/index.tsx" entry to the "bin" object
in package.json (or rename an existing bin key to "kalshi") or change the help
text in the CLI entrypoint (index.tsx) to use the actual binaries; update
package.json's "bin" to include the exact executable name your help output
references or align the help text in the CLI code (index.tsx) to the current bin
names.

"scripts": {
Expand Down
4 changes: 4 additions & 0 deletions src/__tests__/e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ function makeParsedArgs(overrides: Partial<ParsedArgs>): ParsedArgs {
dryRun: false,
verbose: false,
performance: false,
resolved: false,
unresolved: false,


parseErrors: [],
...overrides,
};
Expand Down
4 changes: 2 additions & 2 deletions src/agent/prompts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export interface GroupContext {
// Default System Prompt (for backward compatibility)
// ============================================================================

export const DEFAULT_SYSTEM_PROMPT = `You are Kalshi Deep Trading Bot, a prediction market research and trading assistant.
export const DEFAULT_SYSTEM_PROMPT = `You are Kalshi Trading Bot CLI, a prediction market research and trading assistant.

Current date: ${getCurrentDate()}

Expand Down Expand Up @@ -95,7 +95,7 @@ export function buildSystemPrompt(model: string, channel?: string): string {
? `\n## Tables (for comparative/tabular data)\n\n${profile.tables}`
: '';

return `You are Kalshi Deep Trading Bot, an AI-powered prediction market research and trading assistant.
return `You are Kalshi Trading Bot CLI, an AI-powered prediction market research and trading assistant.

Current date: ${getCurrentDate()}

Expand Down
172 changes: 172 additions & 0 deletions src/backtest/discovery.ts
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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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
Verify each finding against the current code and only fix it if needed.

In `@src/backtest/discovery.ts` around lines 31 - 44, fetchEventMarkets currently
swallows all errors from callKalshiApi and returns an empty array, making
outages/rate-limits indistinguishable from "no markets"; update
fetchEventMarkets to surface failures instead of hiding them by removing the
broad try/catch or rethrowing the caught error with context so callers can
detect partial discovery (e.g., catch error from callKalshiApi inside
fetchEventMarkets and throw a new Error including eventTicker and the original
error), keep the function signature and return type (Promise<KalshiMarket[]>)
but ensure any Kalshi API failure bubbles up rather than returning [] so callers
can handle or log the outage appropriately.

}

/** 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();
}
Loading
Loading