---
title: "Logit-Space Pricing"
description: "Black-Scholes for prediction markets: logit-space reservation price, optimal spread, Greeks, and PnL decomposition. Based on Dalen 2025."
icon: "chart-bell"
---

<Note>
  **Pro Feature.** Requires a Pro or Ultra subscription. [Get started at api.mathematicalcompany.com](https://api.mathematicalcompany.com)
</Note>

<Tip>
  **What is this?** Standard Avellaneda-Stoikov market making operates in probability space (0 to 1), but this causes boundary compression problems - prices near 0 or 1 have distorted spread and risk. Logit-space pricing transforms prices via `logit(p) = ln(p/(1-p))`, moving calculations to the real line where Gaussian assumptions work properly. The result: correct Greeks, better spreads near boundaries, and a tradable "belief volatility" parameter analogous to implied vol in options.
</Tip>

# Logit-Space Pricing

Horizon implements the logit-space pricing framework from Dalen 2025 ("Toward Black-Scholes for Prediction Markets"). All math is in Rust; Python pipeline wrappers handle feed ingestion and context injection.

<CardGroup cols={2}>
  <Card title="Logit Transform" icon="right-left">
    `hz.logit()` / `hz.sigmoid()` - the core transforms between probability and logit space.
  </Card>
  <Card title="Logit Greeks" icon="chart-mixed">
    `hz.logit_greeks()` - delta, gamma, theta, belief-vega in the correct mathematical space.
  </Card>
  <Card title="Logit Market Maker" icon="arrows-left-right">
    `hz.logit_market_maker()` - full pipeline factory using logit reservation price and optimal spread.
  </Card>
  <Card title="PnL Decomposition" icon="chart-pie">
    `hz.logit_pnl_decompose()` - Taylor decomposition of PnL into directional, curvature, vega, and residual.
  </Card>
</CardGroup>

---

## Why Logit Space?

In probability space, market making near boundaries (p near 0 or 1) has three problems:

1. **Spread compression**: An optimal spread of 0.04 makes no sense at p=0.02
2. **Greek distortion**: Delta and gamma behave differently near boundaries
3. **Non-Gaussian dynamics**: Price changes aren't normally distributed in probability space

The logit transform `x = ln(p/(1-p))` maps (0,1) to (-inf, +inf), where:
- Gaussian diffusion assumptions hold
- Spread scales naturally with `p(1-p)` (sigmoid derivative)
- Greeks have clean closed-form expressions

---

## Core Functions

### hz.logit / hz.sigmoid

The fundamental transforms.

```python
import horizon as hz

# logit: (0,1) -> (-inf, +inf)
hz.logit(0.5)   # 0.0
hz.logit(0.1)   # -2.197
hz.logit(0.9)   # 2.197

# sigmoid: (-inf, +inf) -> (0,1)
hz.sigmoid(0.0)  # 0.5
hz.sigmoid(-2.0) # 0.119
hz.sigmoid(2.0)  # 0.881

# Inverse property
assert abs(hz.sigmoid(hz.logit(0.73)) - 0.73) < 1e-10
```

### hz.sigmoid_prime / hz.sigmoid_double_prime

Derivatives of the sigmoid function. Used internally for Greeks computation.

```python
hz.sigmoid_prime(0.0)         # 0.25 = p(1-p) at p=0.5
hz.sigmoid_double_prime(0.0)  # 0.0  = p(1-p)(1-2p) at p=0.5
```

---

## Logit Market Making

### hz.logit_reservation_price

Compute the logit-space inventory-skewed fair value.

```python
r = hz.logit_reservation_price(
    mid=0.50,          # Current mid price
    inventory=10.0,    # Net inventory
    gamma=0.5,         # Risk aversion
    belief_vol=0.2,    # Belief volatility
    t=1.0,             # Time horizon
)
# Formula: sigmoid(logit(mid) - q * gamma * sigma_b^2 * T)
```

| Parameter | Type | Description |
|-----------|------|-------------|
| `mid` | `float` | Current mid price (0-1) |
| `inventory` | `float` | Net inventory (positive = long) |
| `gamma` | `float` | Risk aversion parameter |
| `belief_vol` | `float` | Belief volatility (analogous to implied vol) |
| `t` | `float` | Time horizon |

### hz.logit_optimal_spread

Compute the logit-space optimal bid-ask spread.

```python
spread = hz.logit_optimal_spread(
    belief_vol=0.2,   # Belief volatility
    inventory=0.0,    # Current inventory
    gamma=0.5,        # Risk aversion
    kappa=1.5,        # Order arrival intensity
    t=1.0,            # Time horizon
)
# Logit spread = gamma*sigma_b^2*T + (2/gamma)*ln(1 + gamma/kappa)
# Then scaled to probability space via p(1-p)
```

### hz.toxicity_adjusted_spread

Widen spread based on VPIN toxicity signal. Bridges the VPIN detector to spread adjustment.

```python
adjusted = hz.toxicity_adjusted_spread(
    base_spread=0.04,   # Base spread from model
    vpin=0.7,           # VPIN value (0-1)
    sensitivity=1.0,    # How aggressively to widen (0-10)
)
# Formula: base_spread * (1 + sensitivity * vpin)
```

---

## Logit Greeks

### hz.logit_greeks

Compute prediction market Greeks in logit space.

```python
g = hz.logit_greeks(
    price=0.60,       # Current price
    size=10.0,        # Position size
    is_yes=True,      # YES or NO side
    t_hours=24.0,     # Hours to resolution
    belief_vol=0.2,   # Belief volatility
)
print(f"Delta: {g.delta:.4f}")        # Rate of change w.r.t. price
print(f"Gamma: {g.gamma:.4f}")        # Curvature (convexity)
print(f"Theta: {g.theta:.4f}")        # Time decay
print(f"Belief Vega: {g.belief_vega:.4f}")  # Sensitivity to belief vol
```

| Field | Formula | Interpretation |
|-------|---------|----------------|
| `delta` | `sign * size * p(1-p)` | Exposure to price moves, naturally scaled by sigmoid derivative |
| `gamma` | `sign * size * p(1-p)(1-2p)` | Convexity; zero at p=0.5, maximal near boundaries |
| `theta` | `-sign * size * p(1-p) * sigma_b^2 / (2T)` | Time decay from belief volatility |
| `belief_vega` | `sign * size * p(1-p) * 2*sigma_b*T` | Sensitivity to changes in belief vol |

### LogitGreeks Type

| Field | Type | Description |
|-------|------|-------------|
| `delta` | `float` | First-order price sensitivity |
| `gamma` | `float` | Second-order price sensitivity |
| `theta` | `float` | Time decay |
| `belief_vega` | `float` | Belief volatility sensitivity |

---

## PnL Decomposition

### hz.logit_pnl_decompose

Taylor decomposition of realized PnL into component contributions.

```python
attr = hz.logit_pnl_decompose(
    p_before=0.40,     # Price at start
    p_after=0.60,      # Price at end
    size=10.0,         # Position size
    is_yes=True,       # YES or NO side
    vol_before=0.20,   # Belief vol at start
    vol_after=0.25,    # Belief vol at end
    corr_before=0.0,   # Correlation at start (optional)
    corr_after=0.0,    # Correlation at end (optional)
)
print(f"Directional: {attr.directional:.4f}")
print(f"Curvature:   {attr.curvature:.4f}")
print(f"Vega PnL:    {attr.belief_vega_pnl:.4f}")
print(f"Corr PnL:    {attr.correlation_vega_pnl:.4f}")
print(f"Residual:    {attr.residual:.4f}")
# Components sum to actual PnL
```

### LogitPnlAttribution Type

| Field | Type | Description |
|-------|------|-------------|
| `directional` | `float` | Delta * dx (first-order move) |
| `curvature` | `float` | 0.5 * Gamma * dx^2 (second-order) |
| `belief_vega_pnl` | `float` | Vega * (vol_after - vol_before) |
| `correlation_vega_pnl` | `float` | CorrVega * (corr_after - corr_before) |
| `residual` | `float` | Actual PnL minus sum of components |

---

## Pipeline Integration

### hz.logit_market_maker

Full logit-space market maker for `hz.run()`. Drop-in replacement for `hz.market_maker()`.

```python
hz.run(
    pipeline=[
        hz.logit_market_maker(
            base_spread=0.04,
            gamma=0.3,
            kappa=1.5,
            belief_vol=0.2,
            max_position=100.0,
            use_vpin=True,           # Read VPIN from ctx.params
            vpin_sensitivity=1.5,    # Spread widening factor
        ),
    ],
    markets=[market],
    exchange=exchange,
)
```

### hz.logit_greeks_pipeline

Injects logit Greeks into `ctx.params` each cycle.

```python
hz.run(
    pipeline=[
        hz.logit_greeks_pipeline(feed_name="binance", belief_vol=0.2, t_hours=24.0),
        my_strategy,  # Can read ctx.params["logit_delta"], etc.
    ],
    ...
)
```

### hz.logit_pnl_attribution_pipeline

Tracks per-market PnL decomposition over time.

```python
hz.run(
    pipeline=[
        hz.logit_pnl_attribution_pipeline(belief_vol=0.2),
        my_strategy,  # Can read ctx.params["logit_pnl_attribution"]
    ],
    ...
)
```

---

## Greeks-Based PnL Attribution

### hz.greeks_attribution

Batch PnL decomposition for multiple positions using logit Greeks.

```python
from horizon import greeks_attribution

positions = engine.positions()
price_before = {"market-a": 0.40, "market-b": 0.55}
price_after = {"market-a": 0.60, "market-b": 0.50}

results = greeks_attribution(positions, price_before, price_after, belief_vol=0.2)
for r in results:
    print(f"{r.market_id}: dir={r.directional:.4f} curv={r.curvature:.4f}")
```
