> ## Documentation Index
> Fetch the complete documentation index at: https://mathematicalcompany.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Horizon Risk Pipeline

> The 8-point risk pipeline that guards every Horizon order before it reaches the exchange.

Every order passes through an **8-point risk pipeline** in Rust before reaching the exchange. The pipeline is designed to prevent catastrophic losses and enforce trading limits.

## Risk Pipeline

The checks run in order. If any check fails, the order is rejected immediately:

| # | Check                | What it does                                   | Config                                |
| - | -------------------- | ---------------------------------------------- | ------------------------------------- |
| 1 | **Kill switch**      | Blocks all orders when active                  | `engine.activate_kill_switch(reason)` |
| 2 | **Price validation** | Rejects price outside `price_min`..`price_max` | `price_min`, `price_max`              |
| 3 | **Size validation**  | Rejects size ≤ 0                               | -                                     |
| 4 | **Max order size**   | Caps individual order size                     | `max_order_size`                      |
| 5 | **Position limit**   | Caps position per market                       | `max_position`                        |
| 6 | **Notional limit**   | Caps total portfolio notional                  | `max_notional`                        |
| 7 | **Drawdown check**   | Activates kill switch on excessive drawdown    | `max_drawdown_pct`                    |
| 8 | **Rate limit**       | Token bucket (sustained + burst)               | `rate_limit`, `rate_burst`            |
| 9 | **Dedup**            | Rejects duplicate orders within window         | `dedup_window_ms`                     |

<Warning>
  When the drawdown check triggers, it automatically activates the kill switch and cancels all open orders across all exchanges. This is a hard stop that requires manual intervention.
</Warning>

## Configuration

### Risk Builder

The `Risk` class provides a clean builder API:

```python theme={null}
hz.run(
    risk=hz.Risk(
        max_position=100,       # Max contracts per market (default: 100)
        max_notional=1000,      # Max total portfolio value (default: 1000)
        max_drawdown_pct=5,     # Kill switch at 5% drawdown (default: 5)
        max_order_size=50,      # Max single order size (default: 50)
        rate_limit=50,          # Sustained orders/sec (default: 50)
        rate_burst=300,         # Burst capacity (default: 300)
        price_min=0.01,         # Min valid price (default: 0.01)
        price_max=0.99,         # Max valid price (default: 0.99)
    ),
    ...
)
```

<Note>
  `hz.Risk()` does not support `max_position_per_event`. For event-level position limits, use `RiskConfig` directly (see below).
</Note>

### Equity Risk Preset

For equity strategies, use `Risk.equity()` which sets appropriate defaults:

```python theme={null}
hz.run(
    risk=hz.Risk.equity(
        max_position=1000,      # shares per symbol
        max_notional=100_000,   # total portfolio value
        max_drawdown_pct=5,
        max_order_size=500,
    ),
    ...
)
```

`Risk.equity()` sets `price_min=0.01` and `price_max=100000` (vs 0.01-0.99 for prediction markets).

<Note>
  Use default `hz.Risk()` for prediction markets (price: 0.01-0.99). Use `Risk.equity()` for equities/options (price: 0.01-100,000). For crypto, customize `price_min`/`price_max` to match the asset's range.
</Note>

### RiskConfig (Direct)

For full control, use the Rust `RiskConfig` directly:

```python theme={null}
from horizon import RiskConfig

config = RiskConfig(
    max_position_per_market=100.0,
    max_portfolio_notional=1000.0,
    max_daily_drawdown_pct=5.0,
    max_order_size=50.0,
    rate_limit_sustained=50,
    rate_limit_burst=300,
    dedup_window_ms=1000,
    max_position_per_event=200.0,   # Event-level limit (None = disabled)
)
```

| Parameter                 | Default | Description                                                                          |
| ------------------------- | ------- | ------------------------------------------------------------------------------------ |
| `max_position_per_market` | 100.0   | Maximum position size per market                                                     |
| `max_portfolio_notional`  | 1000.0  | Maximum total portfolio notional value                                               |
| `max_daily_drawdown_pct`  | 5.0     | Kill switch trigger (% of daily baseline)                                            |
| `max_order_size`          | 50.0    | Maximum size for a single order                                                      |
| `rate_limit_sustained`    | 50      | Sustained orders per second                                                          |
| `rate_limit_burst`        | 300     | Burst capacity for the token bucket                                                  |
| `dedup_window_ms`         | 1000    | Window for duplicate order detection (ms)                                            |
| `max_position_per_event`  | `None`  | Maximum total position across all outcomes in a registered event. `None` = disabled. |
| `price_min`               | 0.01    | Minimum valid limit-order price                                                      |
| `price_max`               | 0.99    | Maximum valid limit-order price. Set to 100000 for equities.                         |

## Kill Switch

The kill switch is a global emergency stop:

```python theme={null}
# Activate: blocks all orders + cancels existing
engine.activate_kill_switch("manual halt")

# Deactivate: resume trading
engine.deactivate_kill_switch()

# Check status
status = engine.status()
if status.kill_switch_active:
    print(f"Kill switch reason: {status.kill_switch_reason}")
```

The kill switch is automatically activated when:

* Daily drawdown exceeds `max_drawdown_pct`
* You can also trigger it manually or from a pipeline function

<Tip>
  In the TUI dashboard, press `k` to toggle the kill switch.
</Tip>

## Drawdown Tracking

The strategy loop automatically tracks drawdown:

1. On startup, the daily baseline is set to the current total P\&L
2. Each cycle, `update_daily_pnl()` is called with the latest total P\&L
3. If P\&L drops below `baseline * (1 - max_drawdown_pct / 100)`, the kill switch triggers

```python theme={null}
# Manually set the daily baseline
engine.set_daily_baseline(1000.0)

# Update P&L (done automatically in the main loop)
engine.update_daily_pnl(current_pnl)
```

## Rate Limiting

The rate limiter uses a **token bucket** algorithm:

* **Sustained rate**: refill rate in orders per second
* **Burst capacity**: maximum tokens available for bursts

This allows short bursts of rapid order submission while enforcing a sustainable average rate.

## Dedup Window

The dedup check prevents submitting identical orders within a configurable time window. Two orders are considered duplicates if they have the same:

* Market ID
* Side (Yes/No/Long)
* Order side (Buy/Sell)
* Size
* Price

Default window: 1000ms.

## Event Risk Limits

When trading [multi-outcome events](/multi-outcome), you can set `max_position_per_event` to cap total exposure across all outcomes in an event:

```python theme={null}
config = RiskConfig(
    max_position_per_market=100.0,
    max_position_per_event=200.0,  # Caps total across all outcomes
)
```

This check only applies to markets registered in an event via `engine.register_event()` (or via `hz.run(events=...)`). Markets not in any event are unaffected.

When `max_position_per_event` is `None` (the default), event-level risk checks are skipped entirely.

## Netting and Risk

When [netting pairs](/multi-exchange) are configured, the notional limit check accounts for hedged positions. For each netting pair (market\_a, market\_b), the hedged portion is subtracted from the total portfolio notional:

```
adjusted_notional = raw_notional - sum(min(exposure_a, exposure_b) * 0.5)
```

This allows larger positions when they're hedged across exchanges.
