---
title: "Horizon Context"
description: "The Context object injected into every Horizon pipeline function with live data."
icon: "database"
---

Every pipeline function receives a `Context` object with live data about feeds, inventory, the current market, engine status, and custom parameters.

## Context Structure

```python
@dataclass
class Context:
    feeds: dict[str, FeedData]      # Live feed snapshots
    inventory: InventorySnapshot     # Current positions
    market: Market | None            # Current market being quoted
    event: Event | None              # Current event (multi-outcome only)
    status: EngineStatus | None      # Engine status (PnL, kill switch, etc.)
    params: dict[str, Any]           # Custom parameters from hz.run(params=...)
```

## FeedData

Each entry in `ctx.feeds` is a `FeedData` snapshot from the engine's feed manager:

```python
@dataclass
class FeedData:
    price: float = 0.0        # Last trade price
    timestamp: float = 0.0    # Unix timestamp
    bid: float = 0.0          # Best bid
    ask: float = 0.0          # Best ask
```

### Accessing feeds

```python
def fair_value(ctx: hz.Context) -> float:
    btc = ctx.feeds.get("btc", hz.context.FeedData())
    return btc.price  # Latest price from feed
```

<Tip>
  Always use `.get()` with a default `FeedData()` to handle the case where a feed hasn't received data yet.
</Tip>

### Feed staleness

`FeedData` includes a staleness check:

```python
feed = ctx.feeds.get("btc", hz.context.FeedData())
if feed.is_stale(max_age_secs=30.0):
    # Feed hasn't updated in 30 seconds, consider skipping quotes
    pass
```

In live mode, the strategy loop automatically skips quoting for markets when any feed is stale (default threshold: 30 seconds). You can configure this via `params`:

```python
hz.run(
    params={"feed_stale_threshold": 60.0},  # Allow up to 60s staleness
    ...
)
```

## InventorySnapshot

`ctx.inventory` provides the current position state:

```python
@dataclass
class InventorySnapshot:
    positions: list[Position]   # All open positions

    @property
    def net(self) -> float:
        """Net position across all markets (YES - NO)."""

    def net_for_market(self, market_id: str) -> float:
        """Net position for a specific market (YES - NO)."""

    def net_for_event(self, market_ids: list[str]) -> float:
        """Net position across all markets in an event."""

    def positions_for_event(self, market_ids: list[str]) -> list[Position]:
        """Filter positions for markets in an event."""
```

### Inventory-based quoting

```python
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    # Skew quotes based on inventory
    net_pos = ctx.inventory.net
    skew = net_pos * 0.001
    return hz.quotes(fair - skew, spread=0.04, size=5)
```

### Per-market inventory

```python
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    market_pos = ctx.inventory.net_for_market(ctx.market.id)
    # Reduce size when position is large
    size = max(1, 10 - abs(market_pos) * 0.1)
    return hz.quotes(fair, spread=0.04, size=size)
```

## Market

`ctx.market` is the `Market` object currently being quoted:

```python
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    if ctx.market.exchange == "kalshi":
        spread = 0.04  # Wider spread for Kalshi
    else:
        spread = 0.02
    return hz.quotes(fair, spread, size=5)
```

See [Types Reference](/api-reference/types) for all `Market` fields.

## Event

`ctx.event` is set when trading multi-outcome events (via `hz.run(events=...)`). It is `None` for standard binary market strategies.

```python
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    if ctx.event:
        # Access event info
        event_market_ids = [o.market_id for o in ctx.event.outcomes]
        event_pos = ctx.inventory.net_for_event(event_market_ids)
        skew = event_pos * 0.001
        return hz.quotes(fair - skew, spread=0.04, size=5)
    return hz.quotes(fair, spread=0.04, size=5)
```

See [Multi-Outcome Events](/multi-outcome) for the full guide.

## EngineStatus

`ctx.status` provides engine-level metrics:

```python
def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    if ctx.status.kill_switch_active:
        return []  # Don't quote when kill switch is on

    # Reduce size as drawdown increases
    drawdown = -ctx.status.daily_pnl
    size = max(1, 10 - drawdown * 0.5)
    return hz.quotes(fair, spread=0.04, size=size)
```

## Custom Parameters

Pass arbitrary parameters via `hz.run(params=...)` and access them in pipeline functions:

```python
hz.run(
    params={
        "gamma": 0.1,
        "base_spread": 0.02,
        "max_inventory": 50,
    },
    ...
)

def quoter(ctx: hz.Context, fair: float) -> list[hz.Quote]:
    gamma = ctx.params["gamma"]
    spread = ctx.params["base_spread"]
    max_inv = ctx.params["max_inventory"]

    # Widen spread as inventory approaches limit
    inv_ratio = abs(ctx.inventory.net) / max_inv
    adjusted_spread = spread * (1 + gamma * inv_ratio)

    return hz.quotes(fair, adjusted_spread, size=5)
```
