Metadata-Version: 2.4
Name: nephyr-risk
Version: 0.1.0
Summary: Prediction market risk management — Kelly sizing, EV calculation, drawdown protection
Author-email: CLM Studios <citlali.lopmonster.studios@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/clm-studios/nephyr-risk
Project-URL: Repository, https://github.com/clm-studios/nephyr-risk
Project-URL: Issues, https://github.com/clm-studios/nephyr-risk/issues
Keywords: prediction-markets,risk-management,kelly-criterion,expected-value,polymarket,kalshi,trading,mcp
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == "mcp"
Provides-Extra: api
Requires-Dist: fastapi>=0.110; extra == "api"
Requires-Dist: uvicorn[standard]>=0.29; extra == "api"
Requires-Dist: pydantic>=2.0; extra == "api"
Requires-Dist: slowapi>=0.1.9; extra == "api"
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: httpx>=0.27; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Provides-Extra: all
Requires-Dist: mcp>=1.0; extra == "all"
Requires-Dist: fastapi>=0.110; extra == "all"
Requires-Dist: uvicorn[standard]>=0.29; extra == "all"
Requires-Dist: pydantic>=2.0; extra == "all"
Requires-Dist: slowapi>=0.1.9; extra == "all"
Dynamic: license-file

# Nephyr Risk

[![PyPI version](https://badge.fury.io/py/nephyr-risk.svg)](https://pypi.org/project/nephyr-risk/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**Prediction market risk management for Python bots and AI agents.**

Nephyr Risk packages CLM Studios' battle-tested risk math (used in live Polymarket and Kalshi trading bots) as a clean, stateless Python library — with an MCP server so Claude, ChatGPT, and any MCP-compatible agent can call it directly.

Zero dependencies for the core. No database, no API calls, no side effects.

---

## Installation

```bash
# Core library only (no external dependencies)
pip install nephyr-risk

# With MCP server support
pip install "nephyr-risk[mcp]"

# With REST API support
pip install "nephyr-risk[api]"
```

---

## Quick Start

```python
from nephyr_risk import (
    calculate_kelly_size,
    calculate_ev,
    check_risk_status,
    validate_trade,
    get_exposure,
    simulate_drawdown,
)

# 1. Size a position: 60% model probability, 40¢ market price, $1000 bankroll
kelly = calculate_kelly_size(probability=0.60, entry_price=0.40, bankroll=1000)
print(kelly.size)          # ~50.0 (capped at 5% of bankroll)
print(kelly.capped)        # True
print(kelly.full_kelly)      # ~0.3333 (full Kelly)

# 2. Check expected value
ev = calculate_ev(model_probability=0.65, market_price=0.50, platform="polymarket")
print(ev.ev)           # ~0.13 (after fees)
print(ev.edge)         # 0.15 (before fees)
print(ev.is_positive)  # True

# 3. Check current risk status
status = check_risk_status(bankroll=950, daily_pnl=-50, peak_bankroll=1000)
print(status.status)    # 'ACTIVE'
print(status.can_trade) # True

# 4. Pre-trade validation
check = validate_trade(proposed_size=50, bankroll=1000, daily_pnl=0, peak_bankroll=1000)
print(check.approved)                  # True
print(check.projected_daily_loss_pct)  # 5.0

# 5. Portfolio exposure
exposure = get_exposure(
    positions=[{"size": 80}, {"size": 60}, {"size": 40}],
    bankroll=1000
)
print(exposure["total_exposure_pct"])    # 18.0
print(exposure["concentration_risk"])   # False

# 6. Simulate a drawdown scenario
scenario = simulate_drawdown(bankroll=1000, loss_sequence=[30, 40, 50, 60, 80])
print(scenario["max_drawdown_pct"])  # 26.0 → SHUTDOWN triggered
print(scenario["would_shutdown"])    # True
print(scenario["shutdown_after_n"])  # 5
```

---

## API Reference

### `calculate_kelly_size(probability, entry_price, bankroll, kelly_fraction=0.25, max_position_pct=5.0, min_size=1.0)` → `KellyResult`

Quarter-Kelly position sizing with a bankroll-percentage hard cap.

| Param | Type | Description |
|---|---|---|
| `probability` | float | Model probability of YES (0 < p < 1) |
| `entry_price` | float | Market price for YES contract (0 < price < 1) |
| `bankroll` | float | Available capital in USD |
| `kelly_fraction` | float | Fraction of full Kelly (default 0.25) |
| `max_position_pct` | float | Hard cap as % of bankroll (default 5%) |
| `min_size` | float | Minimum position size in USD (default $1) |

Returns `KellyResult(size, full_kelly, quarter_kelly, capped, cap_reason)`.
Returns `size=0` with a descriptive `cap_reason` for invalid inputs or no edge.

---

### `calculate_ev(model_probability, market_price, platform="polymarket")` → `EVResult`

Expected value calculation after platform fees.

| Param | Type | Description |
|---|---|---|
| `model_probability` | float | Your model's YES probability |
| `market_price` | float | Current YES price on the exchange |
| `platform` | str | `"polymarket"` or `"kalshi"` |

Returns `EVResult(ev, edge, fee_per_leg, round_trip_fee, is_positive)`.

Fee models:
- **Polymarket**: `fee = 0.02 × price` (flat 2% per leg)
- **Kalshi**: `fee = 0.07 × P × (1−P)` (peaks at P=0.50: 1.75¢/contract)

---

### `calculate_fee(price, platform)` → `float`

Fee per leg for a given price and platform. Use `calculate_polymarket_fee(price)` or `calculate_kalshi_fee(price)` for direct access.

---

### `check_risk_status(bankroll, daily_pnl, peak_bankroll, daily_loss_limit_pct=10.0, drawdown_limit_pct=25.0)` → `RiskStatus`

Current risk status against configured limits.

Returns `RiskStatus(status, can_trade, daily_loss_pct, drawdown_pct, warnings)`.

Status values:
- `"ACTIVE"` — All clear, trading allowed
- `"PAUSED"` — Daily loss limit hit, resume tomorrow
- `"SHUTDOWN"` — Drawdown limit hit, manual review required

Warnings are raised (non-blocking) at 70% of each limit.

---

### `validate_trade(proposed_size, bankroll, daily_pnl, peak_bankroll, daily_loss_limit_pct=10.0, drawdown_limit_pct=25.0)` → `TradeValidation`

Pre-trade risk check. Projects worst case (full loss on proposed trade) against limits.

Returns `TradeValidation(approved, rejection_reason, projected_daily_loss_pct, projected_drawdown_pct)`.

---

### `get_exposure(positions, bankroll)` → `dict`

Portfolio exposure summary.

`positions` is a list of dicts, each with at least `{"size": float}`.

Returns: `total_exposure_usd`, `total_exposure_pct`, `largest_position_usd`, `largest_position_pct`, `num_positions`, `concentration_risk` (bool, triggers at >10% of bankroll in one position), `positions_above_5pct`.

---

### `simulate_drawdown(bankroll, loss_sequence, daily_loss_limit_pct=10.0, drawdown_limit_pct=25.0)` → `dict`

Simulate a hypothetical sequence of losses.

`loss_sequence` is a list of dollar amounts (positive = loss, negative = gain).

Returns: `final_bankroll`, `max_drawdown_pct`, `max_daily_loss_pct`, `total_loss_usd`, `would_pause`, `would_shutdown`, `pause_after_n`, `shutdown_after_n`, `events` (per-step detail).

---

## MCP Server

Nephyr Risk ships as an MCP server so any MCP-compatible agent (Claude, ChatGPT, etc.) can call these functions as tools.

```bash
pip install "nephyr-risk[mcp]"
nephyr-risk-mcp
```

The server exposes all 7 core functions as MCP tools with full JSON schemas. Add it to your `~/.claude/claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "nephyr-risk": {
      "command": "nephyr-risk-mcp"
    }
  }
}
```

---

## REST API

Nephyr Risk also ships as a FastAPI REST API for use from any language or tool.

### Install and run

```bash
pip install "nephyr-risk[api]"

# Start the server (reload mode for development)
python -m api

# Or use uvicorn directly
uvicorn api.app:app --host 0.0.0.0 --port 8000 --reload
```

Interactive Swagger UI is available at **http://localhost:8000/docs** once the server is running.

### Endpoints

| Method | Path | Function |
|--------|------|----------|
| `GET` | `/v1/health` | Health check |
| `POST` | `/v1/kelly` | `calculate_kelly_size` |
| `POST` | `/v1/ev` | `calculate_ev` |
| `POST` | `/v1/risk-status` | `check_risk_status` |
| `POST` | `/v1/validate-trade` | `validate_trade` |
| `POST` | `/v1/exposure` | `get_exposure` |
| `POST` | `/v1/drawdown` | `simulate_drawdown` |
| `POST` | `/v1/fee` | `calculate_fee` |

### curl examples

```bash
# Health check
curl http://localhost:8000/v1/health

# Kelly position sizing
curl -s -X POST http://localhost:8000/v1/kelly \
  -H "Content-Type: application/json" \
  -d '{"probability": 0.6, "entry_price": 0.4, "bankroll": 1000}' | python -m json.tool

# Expected value (Kalshi)
curl -s -X POST http://localhost:8000/v1/ev \
  -H "Content-Type: application/json" \
  -d '{"model_probability": 0.65, "market_price": 0.50, "platform": "kalshi"}' | python -m json.tool

# Risk status check
curl -s -X POST http://localhost:8000/v1/risk-status \
  -H "Content-Type: application/json" \
  -d '{"bankroll": 950, "daily_pnl": -50, "peak_bankroll": 1000}' | python -m json.tool

# Pre-trade validation
curl -s -X POST http://localhost:8000/v1/validate-trade \
  -H "Content-Type: application/json" \
  -d '{"proposed_size": 50, "bankroll": 1000, "daily_pnl": 0, "peak_bankroll": 1000}' | python -m json.tool

# Portfolio exposure
curl -s -X POST http://localhost:8000/v1/exposure \
  -H "Content-Type: application/json" \
  -d '{"positions": [{"size": 80}, {"size": 60}, {"size": 40}], "bankroll": 1000}' | python -m json.tool

# Drawdown simulation
curl -s -X POST http://localhost:8000/v1/drawdown \
  -H "Content-Type: application/json" \
  -d '{"bankroll": 1000, "loss_sequence": [30, 40, 50, 60, 70]}' | python -m json.tool

# Fee calculation
curl -s -X POST http://localhost:8000/v1/fee \
  -H "Content-Type: application/json" \
  -d '{"price": 0.5, "platform": "polymarket"}' | python -m json.tool
```

### API key authentication (optional)

Set the `NEPHYR_API_KEYS` environment variable to a comma-separated list of valid keys.
When set, requests with an `X-API-Key` header are validated against the list.
Requests without the header are allowed (free-tier placeholder).

```bash
export NEPHYR_API_KEYS="key-abc123,key-def456"
python -m api
```

```bash
curl -H "X-API-Key: key-abc123" -X POST http://localhost:8000/v1/kelly \
  -H "Content-Type: application/json" \
  -d '{"probability": 0.6, "entry_price": 0.4, "bankroll": 1000}'
```

---

## Pricing

| Tier | Details |
|------|---------|
| **Free** | 50 calls/month |
| **Paid** | $29/month — unlimited calls |
| **Agent-to-agent** | $0.005/call |

---

## License

MIT — see [LICENSE](LICENSE).

Built by [CLM Studios](https://github.com/clm-studios).
