Every pipeline function receives a Context object with live data about feeds, inventory, the current market, engine status, and custom parameters.
Context Structure
@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:
@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
def fair_value(ctx: hz.Context) -> float:
btc = ctx.feeds.get("btc", hz.context.FeedData())
return btc.price # Latest price from feed
Always use .get() with a default FeedData() to handle the case where a feed hasn’t received data yet.
Feed staleness
FeedData includes a staleness check:
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:
hz.run(
params={"feed_stale_threshold": 60.0}, # Allow up to 60s staleness
...
)
InventorySnapshot
ctx.inventory provides the current position state:
@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
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
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:
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 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.
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 for the full guide.
EngineStatus
ctx.status provides engine-level metrics:
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:
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)