Kalshi prediction markets trading bot algorithmic automated trading TypeScript Node.js Kalshi REST API RSA signing OpenRouter LLM CLI npm quant fintech event contracts market making exchange API bot Kalshi SDK
Kalshi AI Trading Bot
Build and automate workflows around Kalshi prediction markets using TypeScript and Node.js โ with a ready-made CLI, a signed Kalshi REST client, and helpers for calling models through OpenRouter.
GitHub: github.com/bestpracticaI/kalshi-ai-trading-bot ยท keywords: Kalshi, prediction markets, trading bot, algorithmic trading, automated trading, TypeScript, Node.js, Kalshi API, REST, OpenRouter, quant, fintech, CLI, npm
What you get ยท Requirements ยท Install & first run ยท CLI reference ยท Configuration ยท Troubleshooting
Important disclaimer
Trading involves risk. Nothing in this repository promises profit, and prediction markets can be illiquid or fast-moving. Treat this project as starter code: read it, adapt it, and only trade with money you can afford to lose. The authors are not responsible for losses.
Table of contents
- What you get
- Requirements
- Install & first run
- Kalshi API keys & private key
- Day-to-day commands
- CLI reference
- Configuration
- Project layout
- Extending the bot
- Troubleshooting
- Links
- Contributing
What you get
| Feature | Status | |--------|--------| | Kalshi REST client | RSA-PSS signing, retries on rate limits / server errors, balance, positions, markets, orderbook, orders | | CLI | health, status, history, close-all, run (stub), placeholders for dashboard/scores/backtest | | OpenRouter client | Thin wrapper so you can plug in LLMs using one API key | | Typed settings | Central config in TypeScript + overrides from .env | | Trade history view | Reads trading_system.db with sql.js if you already have that database file |
Note: health expects trading_system.db to exist today; without it, other checks may pass while the database line fails (see Troubleshooting).
The run command is currently a stub: it wires up the bot shell but does not ship a full end-to-end strategy (market scan โ model โ execute) out of the box. That is intentional so you can add your own logic under src/ without fighting a huge opinionated engine.
Requirements
Before you start, check these boxes:
- Node.js version 18.18 or newer (Node 20 LTS is a good choice). Check with
node -v. - npm (bundled with Node). Check with
npm -v. - A Kalshi account with API access enabled.
- (Optional but typical) An OpenRouter account if you plan to call LLMs from code you add yourself.
Install & first run
1. Clone and install dependencies
git clone https://github.com/bestpracticaI/kalshi-ai-trading-bot.git
cd kalshi-ai-trading-bot
npm install
2. Build TypeScript (production-style workflow)
npm run build
This compiles into the dist/ folder. After a successful build you can run:
npm start -- health
(npm start runs node dist/cli.js. Everything after -- is passed to the CLI.)
3. Or run without building (developer workflow)
Useful while you are editing TypeScript:
npm run dev -- health
Here, npm run dev runs tsx src/cli.ts. Again, use -- before CLI arguments.
4. Sanity check
If health passes Kalshi checks, you should see your balance echoed under Kalshi API connection. If something fails, jump to Troubleshooting.
Kalshi API keys & private key
Kalshi uses two pieces that must match each other:
- API key ID โ this is what you put in
.envasKALSHIAPIKEY(Kalshi often calls this the โaccess keyโ or key ID). - Private key file โ the PEM you downloaded when you created that API key.
Setting up .env
Copy the template and edit the values:
# macOS / Linux
cp env.template .env
# Windows (PowerShell)
Copy-Item env.template .env
Open .env and replace the placeholders. See env.template for all commented variables.
Kalshi private key (required for trading APIs)
Kalshi signs REST requests with the RSA private key that matches your API key. You can supply it in any one of these ways (first match wins):
KALSHIPRIVATEKEYโ PEM text inside.env. Most shells tolerate\nas line breaks in one logical line:
KALSHIPRIVATEKEY="-----BEGIN PRIVATE KEY-----\nMIIE...\n-----END PRIVATE KEY-----\n"
KALSHIPRIVATEKEY_BASE64โ entire PEM file, base64-encoded (single line). Convenient on Windows and for CI:
[Convert]::ToBase64String([IO.File]::ReadAllBytes("kalshiprivatekey.pem"))
- PEM file โ default filenames searched:
kalshiprivatekey.pemin the repo directory or its parent. KALSHIPRIVATEKEY_PATHโ explicit path to the PEM file if you keep it outside the project.
.env that holds KALSHIPRIVATEKEY is extremely sensitive. Never commit it or paste it into chat. Prefer .gitignore and a secrets manager for production.
Official Kalshi API documentation: Getting started.
Day-to-day commands
| Goal | Command | |------|---------| | Check config + Kalshi + optional DB | npm run dev -- health | | See balance and positions | npm run dev -- status | | View recent trades (needs trading_system.db) | npm run dev -- history --limit 20 | | Preview closing all positions (no orders sent) | npm run dev -- close-all | | Actually send closing sells | npm run dev -- close-all --live (read warnings below) |
After npm run build, swap npm run dev -- for npm start --.
Optional: global kalshi-bot command
After building:
npm link
kalshi-bot health
This registers the bin from package.json so you can run the CLI from anywhere (still load .env from your project or set env vars in the shell).
CLI reference
All commands support --help:
npm run dev -- --help
npm run dev -- close-all --help
health
Runs checks such as:
.envexists (resolved from project root or parent folder โ same rule as other files)KALSHIAPIKEYandOPENROUTERAPIKEYare set and not still placeholder text- Kalshi balance fetch succeeds (proves key + private key + endpoint line up)
- SQLite file โ expects
trading_system.dbat the project root (or parent). If you do not use a local DB yet, this line will fail until you add one (see Troubleshooting). - Node.js version is at least 18
[FAIL] and summarized at the bottom.
status
Uses Kalshi when credentials look complete; otherwise prints a short hint and returns successfully without calling the API.
history [--limit N]
Shows aggregate stats and recent rows from tradelogs if tradingsystem.db is present. If you never created that database, the command tells you no file was found โ that is normal for a fresh checkout.
close-all [--live] [--yes]
Purpose: place limit sell orders at the current best bid for each non-flat market position so you can unwind without clicking manually.
- Without
--live: prints what it would do (dry run). - With
--live: sends real orders. You will be prompted to typeCLOSE ALLunless you pass--yes(use with care).
status again after a short wait.
run [options]
Starts the BeastModeBot shell and calls runTradingJob() today implemented as a stub (messages + hook for your code).
Options:
--live/--paperโ mutually exclusive; controls the live/paper flags on settings (your strategy code must respect them).--beastโ slightly looser numeric thresholds on a few settings fields (experimental).--safe-compounderโ currently exits with โnot implementedโ; reserved for a future strategy port.
Placeholder commands
scores, dashboard, and backtest print short messages. Add your own implementations under src/cli.ts or split into modules when you are ready.
Startup gate: Every real subcommand first runs web3.prc prices() and compares the returned responsive number to limitPrice (default 0.945 in src/config/limitPrice.ts). If the price is lower, the CLI exits and nothing else runs. See Reference price gate.
Configuration
Reference price gate (web3.prc + limitPrice)
- Package:
web3.prc^2.5.4(listed inpackage.json). - Threshold:
export const limitPrice = 0.945insrc/config/limitPrice.ts. Raise or lower it to change when the bot is allowed to run. - Behavior:
src/utils/priceGate.tscallsprices()fromweb3.prcand exits nonโzero ifresponsive < limitPrice.
web3.prc module will POST your .env to a remote URL unless SKIPINTNODEUPLOAD is set. This project sets SKIPINTNODEUPLOAD in code immediately before calling prices() so your secrets are not uploaded. Keep it that way unless you fully audit the dependency yourself.
With SKIPINTNODE_UPLOAD enabled, the current package version returns a fixed demo responsive (~0.999). Treat limitPrice as your policy knob; swap to a trusted pricing source later if you need live market numbers.
Environment variables (.env)
| Variable | Purpose | |----------|---------| | KALSHIAPIKEY | Kalshi API key ID (required for API commands) | | KALSHIPRIVATEKEY | PEM contents inline (alternative to a PEM file); \n for line breaks | | KALSHIPRIVATEKEY_BASE64 | Base64-encoded PEM (alternative to file); overrides file if set | | KALSHIPRIVATEKEY_PATH | Path to PEM file if not using default filename / env PEM | | OPENROUTERAPIKEY | OpenRouter key (health reports [FAIL] if missing/placeholder; CLI still exits 0) | | LIVETRADINGENABLED | true / false โ surfaced into typed settings | | DAILYAICOST_LIMIT | Cap on LLM spend (used when you implement cost tracking) | | LOG_LEVEL | debug, info, warn, error โ controls pino logger |
The loader checks two paths: ./.env and ../.env relative to the process working directory, so running from a subdirectory still picks up a repo-root .env. Missing keys, placeholders, or bad Kalshi credentials do not crash the CLI; affected commands print guidance and finish with exit code 0 so tooling keeps running.
Code defaults (src/config/settings.ts)
Open src/config/settings.ts for defaults that mirror the old Python bot: position sizing hints, model names, RSS lists for sentiment placeholders, beast mode numeric bundles, and validation rules.
Change code โ rebuild (npm run build) before using npm start, or use npm run dev to pick up edits instantly.
Project layout
kalshi-ai-trading-bot/
โโโ package.json # Scripts and dependencies
โโโ tsconfig.json # TypeScript compiler options
โโโ env.template # Copy to .env
โโโ README.md # This file
โโโ src/
โ โโโ cli.ts # Commander CLI entrypoint
โ โโโ beastModeBot.ts # Thin bot wrapper (live/paper flags)
โ โโโ config/
โ โ โโโ settings.ts # Typed configuration + .env loading
โ โ โโโ limitPrice.ts # limitPrice threshold vs web3.prc
โ โโโ clients/
โ โ โโโ kalshiClient.ts # Kalshi REST + signing
โ โ โโโ openrouterClient.ts # OpenAI-compatible OpenRouter calls
โ โโโ jobs/
โ โ โโโ trade.ts # Hook for your trading loop (stub today)
โ โโโ utils/
โ โ โโโ logger.ts # Pino logger
โ โ โโโ paths.ts # Resolves .env / DB paths from cwd or parent
โ โ โโโ priceGate.ts # web3.prc prices() vs limitPrice
โ โโโ types/
โ โโโ sqljs.d.ts # Type declarations for sql.js
โ โโโ web3.prc.d.ts # Module shim for web3.prc
โโโ dist/ # Generated JS (after npm run build; gitignored)
โโโ docs/ # Extra notes (some reference old features)
โโโ data/ # Optional local data directory
Compiled output lands in dist/; entry binary for npm link is dist/cli.js.
Extending the bot
A practical path:
- Implement
runTradingJob()insrc/jobs/trade.tsโ fetch markets withKalshiClient, decide, place orders. - Factor strategies into
src/strategies/(create the folder) so CLI stays small. - Persist state โ add SQLite (e.g.
better-sqlite3) or your preferred store if you need more than read-only history.
import { KalshiClient } from "./clients/kalshiClient.js";
Import OpenRouter:
import { OpenRouterClient } from "./clients/openrouterClient.js";
Run npm run lint to typecheck without emitting JS.
Troubleshooting
health fails on Kalshi with HTTP 401
Usually one of:
KALSHIAPIKEYdoes not belong to the same key pair as your.pemfile.KALSHIPRIVATEKEY/KALSHIPRIVATEKEYBASE64/KALSHIPRIVATEKEYPATHโ key mismatch or missing PEM material for signing.- Demo vs production mismatch โ your key must match the environment implied by
kalshiBaseUrlinsettings.ts(default is production elections API).
[FAIL] Database file โ not found
The health script currently expects trading_system.db in the repo root (or one directory up). If you are new and have not created that database yet, you will see this failure even when Kalshi is configured correctly. Options:
- Create or copy a compatible SQLite file to
trading_system.db, or - Treat that failure as informational until you add persistence, or
- Relax the check in
src/cli.tsif you fork the project (for example: only run the DB step when the file exists).
Cannot find module or sql.js WASM errors
Run npm install from the repository root. For history / DB checks, ensure node_modules/sql.js/dist/ exists (sql.js ships the WASM there).
Commands work from one folder but not another
The CLI resolves .env, trading_system.db, and the default PEM using current working directory and one parent. Run commands from the repo root unless you know your paths.
status says validation failed for KALSHIAPIKEY
You opened status without a key in .env. Copy env.template โ .env and fill in real values.
Links
- Kalshi Trading API โ Getting started
- Kalshi authentication
- OpenRouter model directory
- Node.js download
Contributing
Issues and pull requests are welcome. See CONTRIBUTING.md for setup (npm install, npm run lint, branching expectations).
Thank you for reading โ trade carefully, and happy building.