Metadata-Version: 2.4
Name: kalshi-pandas
Version: 0.1.0
Summary: pandas-style Python client for the Kalshi Trade API
License-Expression: Apache-2.0
Keywords: api-client,pandas,kalshi,prediction-markets
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: <3.14,>=3.11
Description-Content-Type: text/markdown
Requires-Dist: cachetools>=5.3
Requires-Dist: cryptography>=42.0
Requires-Dist: httpx>=0.28
Requires-Dist: orjson>=3.10
Requires-Dist: pandas[computation,output-formatting,performance]>=2.2
Requires-Dist: pandera>=0.22
Requires-Dist: python-dotenv>=1.0
Requires-Dist: tqdm>=4.66
Provides-Extra: ws
Requires-Dist: websocket-client>=1.8; extra == "ws"
Requires-Dist: websockets>=13.0; extra == "ws"
Provides-Extra: explorer
Requires-Dist: streamlit>=1.30; extra == "explorer"
Requires-Dist: plotly>=5.0; extra == "explorer"
Provides-Extra: mcp
Requires-Dist: fastmcp>=2.0; extra == "mcp"
Requires-Dist: tabulate>=0.9; extra == "mcp"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-httpx>=0.35; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: ruff>=0.9; extra == "dev"
Requires-Dist: mypy>=1.11; extra == "dev"
Requires-Dist: pandas-stubs>=2.2; extra == "dev"
Requires-Dist: types-cachetools>=5.3; extra == "dev"

# kalshi-pandas

> pandas-style Python client for the Kalshi Trade API

A pandas-style Python client for the [Kalshi](https://kalshi.com) prediction-market API. Every list endpoint returns a `pandas.DataFrame`, every dict endpoint returns a `TypedDict`, and every shape is enforced by a `pandera` schema. Sync + async, with optional WebSocket, Streamlit explorer, and FastMCP server extras.

## Compatibility

| kalshi-pandas | Tested Kalshi Trade API |
|---|---|
| 0.1.0 | 3.12.0 |

The package follows its own semver — bug fixes and helper additions ship as patch/minor releases regardless of upstream API changes. The currently-targeted upstream API version is exposed at runtime as `KalshiPandas.API_VERSION`.

## Install

```bash
uv pip install kalshi-pandas              # core
uv pip install "kalshi-pandas[ws]"        # + WebSocket
uv pip install "kalshi-pandas[explorer]"  # + Streamlit dashboard
uv pip install "kalshi-pandas[mcp]"       # + FastMCP server
uv pip install "kalshi-pandas[dev]"       # + test/lint toolchain
```

## Authentication

Kalshi uses **RSA-PSS request signing**. Each authenticated request sends three headers (`KALSHI-ACCESS-KEY`, `KALSHI-ACCESS-SIGNATURE`, `KALSHI-ACCESS-TIMESTAMP`); the client builds them automatically once you provide your API key ID and private key.

### One-time setup

1. Create an API key in the Kalshi web UI ([prod](https://kalshi.com/account/profile) or [demo](https://demo.kalshi.co/account/profile)). Save the **key ID** and the **private key PEM file** that downloads.
2. Copy the example env file and fill in the values:
   ```bash
   cp .env.example .env
   $EDITOR .env
   ```
3. The relevant env vars are:
   - `KALSHI_API_KEY_ID` — your key ID
   - `KALSHI_PRIVATE_KEY_PATH` — absolute path to the PEM file (preferred)
   - `KALSHI_PRIVATE_KEY_PEM` — inline PEM string (alternative, useful for CI)
   - `KALSHI_API_BASE_URL` — optional base-URL override (defaults to production)

You can also pass them directly to the constructor:

```python
KalshiPandas(api_key_id="...", private_key_path="/path/to/key.pem")
```

The client reads env vars in `__post_init__`, so loading `.env` (via `python-dotenv` or your shell) before instantiation is enough.

## Quickstart

### Sync

```python
from dotenv import load_dotenv
from kalshi_pandas import KalshiPandas

load_dotenv()

with KalshiPandas() as client:
    # Public endpoint — no auth needed
    df = client.get_markets(limit=50)
    print(df.head())

    # Private endpoint — uses signed request
    balance = client.get_balance()
    print(balance)
```

### Async

```python
import asyncio
from dotenv import load_dotenv
from kalshi_pandas import AsyncKalshiPandas

load_dotenv()

async def main():
    async with AsyncKalshiPandas() as client:
        df = await client.get_markets(limit=50)
        print(df.head())

asyncio.run(main())
```

## Features

| Feature | Module |
|---|---|
| Sync HTTP client | `kalshi_pandas.KalshiPandas` |
| Async HTTP client | `kalshi_pandas.AsyncKalshiPandas` |
| WebSocket (sync) | `kalshi_pandas.KalshiWebSocket` (extra: `ws`) |
| WebSocket (async) | `kalshi_pandas.AsyncKalshiWebSocket` (extra: `ws`) |
| Pandera schemas | `kalshi_pandas.schemas` |
| TypedDicts | `kalshi_pandas.types` |
| Exception hierarchy | `kalshi_pandas.exceptions` |
| Streamlit explorer | `kalshi-pandas-explore` (extra: `explorer`) |
| FastMCP server | `kalshi-pandas-mcp` (extra: `mcp`) |

## Pagination

Every cursor-paginated list endpoint has an `_all` variant that auto-paginates:

```python
df = client.get_markets()        # one page
df = client.get_markets_all()    # all pages, concatenated
```

## Error handling

```python
from kalshi_pandas import KalshiAPIError, KalshiAuthError, KalshiRateLimitError

try:
    df = client.get_markets()
except KalshiAuthError:
    ...
except KalshiRateLimitError:
    ...
except KalshiAPIError as e:
    print(e.status_code, e.url, e.detail)
```

## Development

```bash
uv pip install -e ".[dev]"
uv run pytest tests/test_unit.py -v
uv run ruff check .
uv run mypy kalshi_pandas
```

CI runs the same checks on every push and PR via `.github/workflows/ci.yml` across Python 3.11, 3.12, and 3.13.

## Releasing

Releases are cut by pushing a `v*` tag. `.github/workflows/release.yml` then:

1. **Runs the test job first** — ruff + mypy + mocked pytest + live integration suite. If this fails, the build, publish, and release jobs are all skipped automatically.
2. Builds sdist + wheel via `uv build`
3. Publishes to PyPI via [trusted publishing](https://docs.pypi.org/trusted-publishers/) (OIDC, no API token)
4. Creates a GitHub release with the built artifacts attached

**One-time setup** (before the first release):

1. Create the project on PyPI (or reserve the name).
2. Add a pending publisher at <https://pypi.org/manage/account/publishing/>:
   - PyPI project name: `kalshi-pandas`
   - Owner: your GitHub user/org
   - Repository name: `kalshi-pandas`
   - Workflow name: `release.yml`
   - Environment name: `pypi`
3. In your GitHub repo settings, create an `Environment` named `pypi`.
4. Add the live-test secrets to the **same `pypi` environment** (Settings → Environments → pypi → Environment secrets):
   - `KALSHI_API_KEY_ID`
   - `KALSHI_PRIVATE_KEY_PEM` (paste the entire PEM contents, including `-----BEGIN ... END-----` lines)

**To cut a release:**

```bash
# 1. Update CHANGELOG.md — move [Unreleased] entries under a new version header
# 2. Bump version in pyproject.toml
# 3. Commit, tag, push
git commit -am "Release v0.1.1"
git tag v0.1.1
git push origin main --tags
```

**To force a release when the test job is broken (not the code):**

```bash
gh workflow run release.yml -f tag=v0.1.1 -f skip_tests=true
```

## License

Apache-2.0
