Nephyr Risk
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
# 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
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.
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:
{
"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
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
# 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).
export NEPHYR_API_KEYS="key-abc123,key-def456"
python -m api
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.
Built by CLM Studios.
Metadata
Release files for nephyr-risk 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| nephyr_risk-0.1.1.tar.gz | 41.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| nephyr_risk-0.1.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 71.3 kB
Release files / nephyr_risk-0.1.1.tar.gz
| Download URL | nephyr_risk-0.1.1.tar.gz |
|---|---|
| Size | 41.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dc74ffb0870d412a98e156ec6ae1f29455ea5dbbadd2e70567ecbdc21cc6d5cb
|
|
BLAKE2b-256 checksum How to use checksums |
5f994936416cb6ac661d74f3d097ded6eee31934d5dc51bd4879fbb5df8c53e0
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|
Release files / nephyr_risk-0.1.1-py3-none-any.whl
| Download URL | nephyr_risk-0.1.1-py3-none-any.whl |
|---|---|
| Size | 30.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f6fe2105b9d70244b6dd00fbceebb2cf632a5bfac930dee6242934c4a1adc21f
|
|
BLAKE2b-256 checksum How to use checksums |
b7cdf5c18eed08e8f6735961798780ae2c6f39000d03674ea88ebd3a8994c31c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.3
|