Kalshi is a CFTC-regulated prediction market in the United States. Horizon supports the Kalshi REST API with bearer token authentication and automatic retry on 401.
Quick Setup
hz.run(
name="kalshi_mm",
exchange=hz.Kalshi(api_key="..."),
mode="live",
...
)
Credentials
API key
Email / password
Environment variables
hz.Kalshi(api_key="your_api_key")
hz.Kalshi(email="you@example.com", password="...")
export KALSHI_API_KEY="..."
# or
export KALSHI_EMAIL="..."
export KALSHI_PASSWORD="..."
hz.Kalshi() # reads from env
Kalshi Configuration
@dataclass
class Kalshi:
email: str | None = None
password: str | None = None
api_key: str | None = None
api_url: str = "https://trading-api.kalshi.com/trade-api/v2"
| Field | Default | Description |
|---|
email | None | Kalshi account email |
password | None | Kalshi account password |
api_key | None | Kalshi API key (preferred) |
api_url | https://trading-api.kalshi.com/trade-api/v2 | API base URL |
Demo Environment
Kalshi provides a demo environment for testing:
hz.Kalshi(
api_key="...",
api_url="https://demo-api.kalshi.co/trade-api/v2",
)
Use the demo environment to test your strategy with real market data but fake money. You can also override the URL via the KALSHI_API_URL environment variable.
Ticker Mapping
Kalshi markets use tickers (e.g., KXBTC-25FEB16) instead of slugs. When mode="live", Horizon automatically sets the ticker from the market ID:
# Market ID is used as the ticker (uppercased)
hz.run(markets=["KXBTC-25FEB16"], ...)
Or set it explicitly:
market = hz.Market(
id="btc-100k",
ticker="KXBTC-25FEB16",
exchange="kalshi",
)
Side Mapping
Kalshi uses two separate concepts for order direction:
| Concept | Values | Description |
|---|
| action | buy / sell | Whether you’re buying or selling (OrderSide) |
| side | yes / no | Which outcome you’re trading (Side) |
This means “buy yes”, “sell yes”, “buy no”, and “sell no” are all valid combinations.
Authentication
Kalshi uses bearer token authentication:
- On first request, the client authenticates with API key (or email/password)
- Receives a bearer token
- Token is included in subsequent requests via
Authorization: Bearer <token>
- On 401 response, the client automatically re-authenticates and retries (loop-based, not recursive)
Fill Polling
Kalshi fills are polled from the /portfolio/fills endpoint. Each cycle:
- Queries recent fills with tracked order IDs
- Deduplicates against previously seen fill IDs
- Maps Kalshi’s price format (cents) and timestamps to Horizon’s format
Feature Support Matrix
| Feature | Polymarket | Kalshi |
|---|
| Trading (orders/fills) | Yes | Yes |
| Orderbook feed | Yes (WS) | Yes (REST) |
| Market discovery | Yes | Yes |
| Cross-exchange arbitrage | Yes | Yes |
| LLM forecasting | Yes | Yes |
| Resolution analysis | Yes | Yes |
| Resolution sniping | Yes | Yes |
| Oracle ensemble | Full (6 signals) | Partial (3 signals) |
| Flow analysis | Yes | No (no on-chain data) |
| Whale tracking | Yes | No |
| Copy trading | Yes | No |
| Wallet intelligence | Yes | No |
The oracle ensemble runs all 6 signal extractors on Kalshi, but signals that depend on Polymarket on-chain data (smart money flow, microstructure, holder concentration) return neutral (0.5). The remaining signals (momentum, volume profile, temporal patterns) also return neutral since Kalshi has no public trade-level data. LLM forecasting and resolution analysis work fully on both exchanges.
Intelligence Suite with Kalshi
All intelligence tools accept an exchange parameter:
# Resolution analysis
analysis = hz.analyze_resolution("KXBTC100K", exchange="kalshi")
# LLM scan
edges = hz.llm_scan(exchange="kalshi")
# Oracle forecast
forecast = hz.forecast_market("KXBTC100K", exchange="kalshi")
# Edge scanning
edges = hz.scan_edges(exchange="kalshi")
MCP Server
MCP tools also accept the exchange parameter:
analyze_resolution(market_id, exchange="kalshi")
llm_scan(exchange="kalshi")
sniper_scan(exchange="kalshi")
oracle_forecast(market_id, exchange="kalshi")
oracle_edges(exchange="kalshi")