MCP Server Kalshi
README.md
# MCP Server Kalshi
A Model Context Protocol (MCP) server for the Kalshi prediction market trading platform. This server provides automated access to market data, order management, and portfolio operations through Kalshi's REST API.
## Features
### Portfolio Management
- View account balance and portfolio value
- Track positions across all markets
- Monitor order history and fills
- Review settlement history
### Market Discovery
- Search and filter markets by various criteria
- Get real-time market prices and statistics
- View orderbook depth and bid/ask spreads
- Browse events and series
### Order Management
- Create buy/sell orders (market or limit)
- Cancel existing orders
- Amend orders atomically
- Submit batch orders (up to 20)
## Available MCP Tools
### Portfolio Tools
#### `get_balance`
Get the portfolio balance of the logged-in member in cents.
**Returns**: Available balance, portfolio value, total returns, etc.
**Example**:
```python
# No parameters required
balance = await get_balance()
print(f"Available balance: ${balance['balance'] / 100:.2f}")
```
#### `get_positions`
Get a list of all of your positions.
**Parameters**:
- `limit` (optional): Number of results per page (1-1000, default 100)
- `cursor` (optional): Pagination cursor for next page
- `status` (optional): Filter by position status
- `market_ticker` (optional): Filter by market ticker
- `event_ticker` (optional): Filter by event ticker
**Example**:
```python
positions = await get_positions({"limit": 50, "status": "open"})
```
#### `get_orders`
Get a list of all of your orders.
**Parameters**:
- `limit` (optional): Number of results per page (1-1000)
- `cursor` (optional): Pagination cursor
- `status` (optional): Filter by order status
**Example**:
```python
orders = await get_orders({"status": "open", "limit": 100})
```
#### `get_fills`
Get a list of all of your order fills.
**Parameters**:
- `limit` (optional): Number of results per page
- `cursor` (optional): Pagination cursor
#### `get_settlements`
Get a list of all of your settlements.
**Parameters**:
- `limit` (optional): Number of results per page
- `cursor` (optional): Pagination cursor
### Market Discovery Tools
#### `search_markets`
Search and filter Kalshi markets by various criteria (event, series, status, time ranges).
**Parameters**:
- `event_ticker` (optional): Filter by event ticker
- `series_ticker` (optional): Filter by series ticker
- `status` (optional): Market status filter ("open", "closed", "settled")
- `max_close_ts` (optional): Markets closing before this Unix timestamp (ms)
- `min_close_ts` (optional): Markets closing after this Unix timestamp (ms)
- `tickers` (optional): Comma-separated list of market tickers
- `limit` (optional): Results per page (1-1000, default 100)
- `cursor` (optional): Pagination cursor
**Example**:
```python
markets = await search_markets({
"status": "open",
"series_ticker": "PRES",
"limit": 50
})
for market in markets['markets']:
print(f"{market['ticker']}: ${market['yes_price']}¢")
```
#### `get_market`
Get detailed information about a specific market by ticker.
**Parameters**:
- `ticker` (required): Market ticker
**Returns**: Full market details including prices, volume, open interest, and status.
**Example**:
```python
market = await get_market({"ticker": "PRES-2025-WINNER-DEM"})
print(f"YES price: {market['yes_price'] / 100:.2%}")
print(f"Volume: {market['volume']}")
```
#### `get_orderbook`
Get current bid/ask spreads and orderbook depth for a market.
**Parameters**:
- `ticker` (required): Market ticker
- `depth` (optional): Number of price levels (1-100, default 5)
**Returns**: Orderbook with YES and NO side price levels.
**Example**:
```python
book = await get_orderbook({"ticker": "PRES-2025-WINNER-DEM", "depth": 10})
print(f"Best YES bid: {book['yes'][0]['price']}¢")
print(f"Best YES ask: {book['yes'][-1]['price']}¢")
```
### Event/Series Tools
#### `search_events`
Search for events (collections of related markets) by series or status.
**Parameters**:
- `series_ticker` (optional): Filter by series ticker
- `status` (optional): Event status filter
- `with_nested_markets` (optional): Include market data in response
- `limit` (optional): Results per page (1-1000, default 100)
- `cursor` (optional): Pagination cursor
**Example**:
```python
events = await search_events({"series_ticker": "PRES", "limit": 20})
for event in events['events']:
print(f"{event['event_ticker']}: {event['title']}")
```
#### `get_event`
Get details about a specific event by its ticker.
**Parameters**:
- `event_ticker` (required): Event ticker
**Returns**: Event metadata including title, status, and category.
#### `get_series`
Get information about a market series (category/topic).
**Parameters**:
- `series_ticker` (required): Series ticker
**Returns**: Series metadata including title, category, frequency, and tags.
**Example**:
```python
series = await get_series({"series_ticker": "POLITICS"})
print(f"Series: {series['title']}")
print(f"Frequency: {series['frequency']}")
```
### Order Management Tools
#### `create_order`
Create an order to buy or sell contracts in a market.
**Parameters**:
- `action` (required): "buy" or "sell"
- `ticker` (required): Market ticker
- `side` (required): "yes" or "no"
- `count` (required): Number of contracts
- `type` (required): "market" or "limit"
- `yes_price` (optional): YES limit price in cents (1-99, required for limit orders)
- `no_price` (optional): NO limit price in cents (1-99, required for limit orders)
- `expiration_ts` (optional): Order expiration Unix timestamp (ms)
- `client_order_id` (optional): Client-specified order ID
**Example**:
```python
order = await create_order({
"action": "buy",
"ticker": "PRES-2025-WINNER-DEM",
"side": "yes",
"count": 10,
"type": "limit",
"yes_price": 52
})
```
#### `cancel_order`
Cancel an existing order by its order ID.
**Parameters**:
- `order_id` (required): Kalshi order UUID
**Returns**: Cancelled order confirmation.
**Example**:
```python
result = await cancel_order({"order_id": "abc-123-xyz"})
print(f"Cancelled order status: {result['order']['status']}")
```
#### `amend_order`
Modify an existing order's price or quantity (atomic cancel + create operation).
**Parameters**:
- `order_id` (required): Order UUID to amend
- `new_count` (optional): New contract count
- `new_yes_price` (optional): New YES price in cents (1-99)
- `new_no_price` (optional): New NO price in cents (1-99)
**Note**: At least one of `new_count`, `new_yes_price`, or `new_no_price` must be provided.
**Example**:
```python
new_order = await amend_order({
"order_id": "abc-123-xyz",
"new_yes_price": 55,
"new_count": 20
})
print(f"Amended to new order: {new_order['order_id']}")
```
#### `batch_orders`
Submit multiple orders atomically (all succeed or all fail, max 20 orders).
**Parameters**:
- `orders` (required): List of order objects (max 20)
**Returns**: List of created order objects.
**Example**:
```python
result = await batch_orders({
"orders": [
{
"action": "buy",
"ticker": "MARKET-1",
"side": "yes",
"count": 10,
"type": "limit",
"yes_price": 50
},
{
"action": "buy",
"ticker": "MARKET-2",
"side": "no",
"count": 5,
"type": "limit",
"no_price": 45
}
]
})
print(f"Created {len(result['orders'])} orders")
```
## Common Usage Scenarios
### Scenario 1: Browse Markets and Place a Trade
```python
# 1. Search for open markets in the PRES series
markets = await search_markets({
"status": "open",
"series_ticker": "PRES",
"limit": 20
})
# 2. Get detailed information about a specific market
market = await get_market({"ticker": "PRES-2025-WINNER-DEM"})
print(f"Current YES price: {market['yes_price']}¢")
print(f"Volume: {market['volume']}")
# 3. Check the orderbook for liquidity
book = await get_orderbook({"ticker": "PRES-2025-WINNER-DEM", "depth": 5})
print(f"Best YES bid: {book['yes'][0]['price']}¢")
# 4. Place a limit order
order = await create_order({
"action": "buy",
"ticker": "PRES-2025-WINNER-DEM",
"side": "yes",
"count": 10,
"type": "limit",
"yes_price": 52
})
print(f"Order created: {order['order_id']}")
```
### Scenario 2: Monitor and Manage Active Positions
```python
# 1. Check account balance
balance = await get_balance()
print(f"Available: ${balance['balance'] / 100:.2f}")
# 2. View open positions
positions = await get_positions({"status": "open", "limit": 100})
for pos in positions['positions']:
print(f"{pos['market_ticker']}: {pos['quantity']} contracts, PnL: ${pos['unrealized_pnl_cents'] / 100:.2f}")
# 3. View open orders
orders = await get_orders({"status": "open"})
for order in orders['orders']:
print(f"{order['ticker']}: {order['count']} @ {order['yes_price']}¢")
# 4. Cancel or amend an order if needed
await cancel_order({"order_id": "abc-123-xyz"})
```
### Scenario 3: Explore Events and Series
```python
# 1. Browse available series
series = await get_series({"series_ticker": "POLITICS"})
print(f"Series: {series['title']}")
# 2. Find related events
events = await search_events({
"series_ticker": "POLITICS",
"status": "active",
"limit": 50
})
# 3. Get markets for a specific event
event = await get_event({"event_ticker": "PRES-2025"})
markets = await search_markets({"event_ticker": "PRES-2025"})
```
### Scenario 4: Batch Trading
```python
# Submit multiple orders atomically (all or nothing)
result = await batch_orders({
"orders": [
{
"action": "buy",
"ticker": "MARKET-A",
"side": "yes",
"count": 10,
"type": "limit",
"yes_price": 50
},
{
"action": "buy",
"ticker": "MARKET-B",
"side": "no",
"count": 15,
"type": "limit",
"no_price": 45
},
{
"action": "sell",
"ticker": "MARKET-C",
"side": "yes",
"count": 5,
"type": "limit",
"yes_price": 60
}
]
})
print(f"Successfully created {len(result['orders'])} orders")
```
## Configuration
### Claud Desktop
<details>
<summary>Setting up with UVX</summary>
```json
"mcpServers": {
"kalshi": {
"command": "uvx",
"args": ["mcp-server-kalshi"],
"env": {
"KALSHI_PRIVATE_KEY_PATH": "PATH TO YOUR RSA KEY FILE",
"KALSHI_API_KEY": "<YOUR KALSHI API KEY>",
"BASE_URL": "https://api.elections.kalshi.com"
}
}
}
```
</details>
<details>
<summary>Setting up with Docker</summary>
1. Build the container from root directory `docker build -t mcp-server-kalshi .`
2. Configure client to run the container (ensure the bind command gives the container access to your rsa key files)
```json
"mcpServers": {
"kalshi": {
"command": "docker",
"args": ["run", "--rm", "-i", "--mount", "type=bind,src=/Users/username,dst=/Users/username", "-e", "KALSHI_PRIVATE_KEY_PATH", "-e", "KALSHI_API_KEY","-e", "BASE_URL", "mcp-server-kalshi"],
"env": {
"KALSHI_PRIVATE_KEY_PATH": "PATH TO YOUR RSA KEY FILE",
"KALSHI_API_KEY": "<YOUR KALSHI API KEY>",
"BASE_URL": "https://api.elections.kalshi.com"
}
}
}
```
</details>
## Local Development
1. Create a `.env` file in the root directory with the following variables
* `BASE_URL` The kalshi API URL
* `KALSHI_API_KEY` The API key for the corresponding environment
* `KALSHI_PRIVATE_KEY_PATH` A filepath to a file containing your Kalshi RSA private key
2. Install deps via `uv pip install -e .` Dev deps can be installed with `uv pip install -e .[dev]`
3. Run with `uv run start`
## Authentication Setup
### Getting Kalshi API Credentials
To use this MCP server, you'll need Kalshi API credentials:
1. **Production Environment** (real money):
- Create a Kalshi account at [kalshi.com](https://kalshi.com)
- Follow the [API Keys documentation](https://trading-api.readme.io/reference/api-keys) to generate:
- An API Key (string)
- An RSA Private Key (PEM file)
- Set `BASE_URL=https://api.elections.kalshi.com`
2. **Demo Environment** (test account, recommended for development):
- Create a demo account following [these instructions](https://trading-api.readme.io/reference/creating-a-demo-account)
- Generate demo API credentials
- Set `BASE_URL=https://demo-api.kalshi.co`
### Environment Variables
The server requires three environment variables:
| Variable | Description | Example |
|----------|-------------|---------|
| `KALSHI_API_KEY` | Your Kalshi API key string | `a1b2c3d4-e5f6-7890-abcd-ef1234567890` |
| `KALSHI_PRIVATE_KEY_PATH` | Absolute path to your RSA private key PEM file | `/Users/username/.kalshi/private_key.pem` |
| `BASE_URL` | Kalshi API base URL | `https://api.elections.kalshi.com` (prod) or `https://demo-api.kalshi.co` (demo) |
**Security Note**: Keep your private key secure and never commit it to version control. The server uses RSA-PSS with SHA-256 for authentication.
### UVX
To run in MCP inspector
```bash
npx @modelcontextprotocol/inspector uv --directory /path/to/your/mcp-server-kalshi run start
```
To run in claud desktop, update your MCP config to:
```json
{
"mcpServers": {
"kalshi": {
"command": "uv",
"args": [
"--directory",
"/<path to repo root directory>",
"run",
"start"
],
"env": {
"KALSHI_PRIVATE_KEY_PATH": "PATH TO YOUR RSA KEY FILE",
"KALSHI_API_KEY": "<YOUR KALSHI API KEY>",
"BASE_URL": "https://api.elections.kalshi.com"
}
}
}
}
```
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues