Trading
Crypto Options Infrastructure: Pricing, Greeks, Streaming, and the Deribit Stack
IV surface maintenance, Greeks streaming across 1000+ instruments, and delta hedging pipelines: the data-side challenges of crypto options infrastructure that equities experience doesn't prepare you for.
Options market making is where the next generation of crypto-native trading firms is building edge. Wintermute reported their OTC options volume more than doubled year-over-year. Genesis Digital Assets, Paradigm, and a handful of other sophisticated shops are building infrastructure that would be competitive at top-tier traditional vol desks.
I’ve been studying this space intensively as part of building ZeroCopy’s quantitative trading capabilities. What I found is that the infrastructure challenges in crypto options are genuinely hard - not because the math is novel, but because the data scale, the market structure, and the execution constraints are unlike anything in equities or traditional FX options.
This post is for engineers who need to understand the infrastructure requirements before writing a single line of pricing code.
The Options Data Scale Problem
Before you can price or trade a single option, you need to understand the data volume you’re dealing with.
On Deribit (the dominant crypto options venue), a typical trading day has:
- BTC options: ~40 expiries × ~100 strikes per expiry × 2 (calls + puts) = ~8,000 instruments
- ETH options: similar scale
- Each instrument has a bid/ask spread updating continuously
If you subscribe to full order book depth for every active instrument, you’re looking at:
- ~16,000 active instruments
- Each delivering WebSocket updates at various rates (active near-the-money strikes: several per second; far OTM wings: much slower)
- Conservative average: 50 updates/second × 16,000 instruments = 800,000 updates/second
That’s the full-universe subscription model. In practice, no single trading system subscribes to all instruments simultaneously - you focus on the active expiries and the strikes near the current spot price.
A realistic “production options desk” subscription set:
- Weekly expiries (the most actively traded): full order book depth
- Monthly expiries near the current date: full order book depth
- Quarterly expiries (quarterly rollup dates): ticker updates only
- Deep OTM wings across all expiries: mark price only (for IV surface construction)
This reduces the active subscription to ~200-500 instruments, delivering on the order of 1,000-5,000 updates/second - manageable with a well-designed consumer.
Greeks Streaming: What Your Risk System Needs
For every options position, your risk system requires five Greeks in real-time:
Delta (Δ): Sensitivity of option price to underlying price movement. A delta of 0.5 means the option price moves 1 move in BTC. Delta hedging = maintaining a position in BTC (or BTC perps) that offsets the net delta of your options book.
Gamma (Γ): Rate of change of delta. High gamma positions change their hedge ratio quickly as price moves. A short gamma position (common for options sellers) means you lose money as volatility increases because your delta hedge needs constant adjustment.
Vega (ν): Sensitivity to implied volatility. Long vega = you profit when IV increases. Short vega = you profit when IV decreases. The direction of your vega position is often the central decision in an options strategy.
Theta (Θ): Time decay. Options lose value as expiry approaches, all else equal. Short options positions earn theta over time but are exposed to gamma and vega risk.
Rho (ρ): Interest rate sensitivity. Less critical in crypto than in traditional finance, but becomes relevant for long-dated options as crypto interest rates are non-trivial.
Deribit publishes Greeks for every instrument in their WebSocket feed. The ticker channel for each instrument includes:
{
"channel": "ticker.BTC-28MAR25-80000-C.100ms",
"data": {
"instrument_name": "BTC-28MAR25-80000-C",
"best_bid_price": 4250.0,
"best_ask_price": 4310.0,
"mark_price": 4280.0,
"mark_iv": 62.5,
"bid_iv": 61.8,
"ask_iv": 63.2,
"greeks": {
"delta": 0.485,
"gamma": 0.000012,
"vega": 148.3,
"theta": -42.1,
"rho": 18.5
}
}
}
This is remarkable: Deribit provides pre-computed Greeks at exchange speed. You don’t need to implement Black-Scholes in your critical path for basic Greek aggregation. However, you cannot blindly trust exchange-published Greeks for sophisticated strategies - the exchange uses a specific vol model and you may have different views on the vol surface.
The Implied Volatility Surface
The implied volatility (IV) surface is the 3D object that options professionals actually trade. It’s a mapping from (expiry, strike) → implied volatility, updated in real-time as option prices change.
Why does the surface matter? Because options at different strikes and expiries imply different volatilities for the same underlying. The well-known volatility smile (or skew in equity options) is a cross-section of this surface at a fixed expiry - options far from the current spot price imply higher volatility than at-the-money options.
In crypto, the IV surface has distinctive features:
- Strong upside skew in BTC: Call options at high strikes often trade at higher IV than puts at symmetric strikes. This reflects the market’s fear of missing a rally (FOMO) more than fear of a crash - opposite of equity markets.
- High absolute volatility levels: 40-80% annual IV is normal for BTC; equities are typically 15-25%.
- Contango structure in term IV: Near-term IV is usually lower than long-dated IV (normal) but can invert around known events (protocol upgrades, ETF decisions, macro dates).
For infrastructure, maintaining the IV surface means:
- Subscribing to mark IV for all active instruments
- Fitting a smooth surface model (SVI parametrization is common in crypto) to the scattered IV points
- Interpolating IV at arbitrary strikes for instruments you’re quoting but don’t have market quotes for
- Updating the surface as IV quotes change
The surface update is computationally expensive - a full SVI fit across all strikes takes 5-50ms depending on implementation. The practical approach: maintain the raw IV quotes in memory (fast, always current) and run the surface fit asynchronously (slower, used for model-dependent decisions rather than real-time hedging).
WebSocket Architecture for Deribit
Here’s a working Python implementation for connecting to Deribit and maintaining a real-time options chain for BTC. This is the foundation of any options infrastructure:
import asyncio
import json
import websockets
from collections import defaultdict
from dataclasses import dataclass, field
from typing import Optional
DERIBIT_WS_URL = "wss://www.deribit.com/ws/api/v2"
@dataclass
class OptionGreeks:
delta: float = 0.0
gamma: float = 0.0
vega: float = 0.0
theta: float = 0.0
rho: float = 0.0
@dataclass
class OptionQuote:
instrument: str
bid: float = 0.0
ask: float = 0.0
mark_price: float = 0.0
mark_iv: float = 0.0
bid_iv: float = 0.0
ask_iv: float = 0.0
greeks: OptionGreeks = field(default_factory=OptionGreeks)
last_updated_ms: int = 0
class DeribitOptionsChain:
"""
Maintains a real-time options chain for a single underlying (BTC or ETH).
Handles WebSocket connection, subscription management, and state.
"""
def __init__(self, currency: str = "BTC"):
self.currency = currency
self._instruments: list[str] = []
self._quotes: dict[str, OptionQuote] = {}
self._ws: Optional[websockets.WebSocketClientProtocol] = None
self._msg_id = 0
def _next_id(self) -> int:
self._msg_id += 1
return self._msg_id
async def connect_and_stream(self):
"""Main entry point: connect, fetch instruments, subscribe, stream."""
async with websockets.connect(DERIBIT_WS_URL) as ws:
self._ws = ws
print(f"Connected to Deribit. Fetching {self.currency} instruments...")
# Step 1: Get all active option instruments
await self._fetch_instruments(ws)
print(f"Found {len(self._instruments)} active {self.currency} options")
# Step 2: Subscribe to tickers for active instruments
# Batch subscriptions to avoid exceeding rate limits
# Deribit allows ~100 channels per subscription message
BATCH_SIZE = 80
channels = [
f"ticker.{inst}.100ms" # 100ms updates
for inst in self._instruments
]
for i in range(0, len(channels), BATCH_SIZE):
batch = channels[i:i + BATCH_SIZE]
await self._subscribe(ws, batch)
await asyncio.sleep(0.1) # Small delay between batches
print(f"Subscribed to {len(channels)} instrument tickers")
# Step 3: Process incoming messages
async for message in ws:
await self._process_message(json.loads(message))
async def _fetch_instruments(self, ws) -> None:
"""Fetch all active option instruments for the currency."""
msg = {
"jsonrpc": "2.0",
"id": self._next_id(),
"method": "public/get_instruments",
"params": {
"currency": self.currency,
"kind": "option",
"expired": False
}
}
await ws.send(json.dumps(msg))
response = json.loads(await ws.recv())
instruments = response.get("result", [])
self._instruments = [
inst["instrument_name"]
for inst in instruments
if inst.get("is_active", True)
]
async def _subscribe(self, ws, channels: list[str]) -> None:
"""Subscribe to a batch of channels."""
msg = {
"jsonrpc": "2.0",
"id": self._next_id(),
"method": "public/subscribe",
"params": {"channels": channels}
}
await ws.send(json.dumps(msg))
async def _process_message(self, msg: dict) -> None:
"""Process incoming WebSocket message."""
# Subscription confirmation - skip
if "id" in msg and "result" in msg:
return
# Ticker update
if msg.get("method") == "subscription":
params = msg.get("params", {})
channel = params.get("channel", "")
data = params.get("data", {})
if channel.startswith("ticker."):
self._update_quote(data)
def _update_quote(self, data: dict) -> None:
"""Update the option quote for an instrument."""
instrument = data.get("instrument_name")
if not instrument:
return
greeks_data = data.get("greeks", {})
quote = OptionQuote(
instrument=instrument,
bid=float(data.get("best_bid_price") or 0),
ask=float(data.get("best_ask_price") or 0),
mark_price=float(data.get("mark_price") or 0),
mark_iv=float(data.get("mark_iv") or 0),
bid_iv=float(data.get("bid_iv") or 0),
ask_iv=float(data.get("ask_iv") or 0),
greeks=OptionGreeks(
delta=float(greeks_data.get("delta") or 0),
gamma=float(greeks_data.get("gamma") or 0),
vega=float(greeks_data.get("vega") or 0),
theta=float(greeks_data.get("theta") or 0),
rho=float(greeks_data.get("rho") or 0),
),
)
self._quotes[instrument] = quote
def net_delta(self) -> float:
"""Compute aggregate delta across all held positions (Greeks × position qty)."""
return sum(q.greeks.delta for q in self._quotes.values())
def instruments_by_expiry(self) -> dict[str, list[OptionQuote]]:
"""Group current quotes by expiry date."""
by_expiry: dict[str, list[OptionQuote]] = defaultdict(list)
for quote in self._quotes.values():
# Deribit instrument format: BTC-28MAR25-80000-C
parts = quote.instrument.split("-")
if len(parts) >= 2:
expiry = parts[1]
by_expiry[expiry].append(quote)
return dict(by_expiry)
# Usage:
async def main():
chain = DeribitOptionsChain(currency="BTC")
await chain.connect_and_stream()
if __name__ == "__main__":
asyncio.run(main())
Delta Hedging: The Infrastructure Demand
For any options position, maintaining delta neutrality requires continuous hedging in the underlying. The frequency and urgency of that hedging depends on your gamma exposure.
Gamma tells you how fast delta changes as price moves. If you have high gamma exposure (long options positions), your delta changes rapidly with price movement - requiring frequent small hedges. If you’re short gamma (sold options), your delta exposure grows as price moves away from your strikes, potentially requiring large hedges at the worst moments (during large moves, which is when you’re bleeding most).
The operational infrastructure for delta hedging:
-
Net delta calculator: Run continuously, aggregating delta across all options positions weighted by quantity held. When net delta exceeds your threshold, fire a hedge order.
-
Hedge threshold: You don’t hedge every tick (transaction costs would destroy you). Instead, set a delta band - if net delta moves outside ±0.05 BTC, hedge to center. The band width is a function of expected gamma PnL vs transaction costs.
-
Hedge execution: Typically via BTC/USDT perps (because perps have no expiry cost, tight spreads, high liquidity). The perp delta is 1:1 with the underlying.
-
Hedge timing: For short gamma positions, large price moves require larger hedges at worse prices. This is the fundamental exposure - you’re always selling into down moves and buying into up moves. Infrastructure must support sub-second hedge initiation to minimize slippage during fast markets.
class DeltaHedgeEngine:
"""
Manages delta hedging for an options book.
Integrates with the options chain and a perp execution layer.
"""
def __init__(
self,
options_book: dict[str, tuple[OptionQuote, float]], # instrument -> (quote, qty)
hedge_threshold_btc: float = 0.05,
):
self.options_book = options_book
self.hedge_threshold_btc = hedge_threshold_btc
self._perp_hedge_qty: float = 0.0 # Current perp hedge (negative = short)
def net_options_delta(self) -> float:
"""Sum of (delta × position quantity) across all options positions."""
total_delta = 0.0
for instrument, (quote, qty) in self.options_book.items():
total_delta += quote.greeks.delta * qty
return total_delta
def net_book_delta(self) -> float:
"""Total delta including the perp hedge position."""
return self.net_options_delta() + self._perp_hedge_qty
def required_hedge_trade(self) -> float | None:
"""
Returns the quantity to trade in perps to get delta-neutral.
None if within threshold (no trade needed).
Positive = buy perps, negative = sell perps.
"""
book_delta = self.net_book_delta()
if abs(book_delta) < self.hedge_threshold_btc:
return None # Within band, don't trade
# Target: net_options_delta + (current_perp + trade) = 0
# trade = -net_options_delta - current_perp
target_perp = -self.net_options_delta()
trade_qty = target_perp - self._perp_hedge_qty
return trade_qty
def record_hedge_fill(self, qty: float) -> None:
"""Update internal perp position after hedge fill."""
self._perp_hedge_qty += qty
The Mistakes Most Engineers Make
Mistake 1: Trying to subscribe to all instruments. Start with the active expiries and near-the-money strikes. Full universe subscription is only necessary for vol surface construction, and even then you can use mark price snapshots rather than full order book streams.
Mistake 2: Using exchange-published Greeks without validation. Deribit’s Greeks are correct for their model, but your model may differ. Build a local Greek calculator using your vol model and compare against Deribit’s numbers. Large discrepancies indicate model differences that matter for hedging decisions.
Mistake 3: Not handling instrument expiry. Options expire. Your subscription list grows stale as new strikes are listed and old ones expire. Build an instrument lifecycle manager that refreshes the active instrument list every few minutes and resubscribes as needed.
Mistake 4: Delta hedging too frequently. Every hedge trade pays the bid-ask spread on the perp. If you’re gamma-negative and hedging on every tick, the transaction costs will exceed your theta income. Implement a cost-aware hedging policy with explicit band thresholds.
Mistake 5: Ignoring the term structure. Vega at different expiries is not fungible. A position that is vega-neutral across all expiries might still be significantly exposed to specific expiry IV changes. Your risk system needs vega bucketed by expiry, not just net vega.
The options infrastructure build is one of the most complex undertakings in trading technology. The data scale, the Greek aggregation, the delta hedging feedback loop, and the vol surface maintenance are each independently hard problems. If you’re serious about building in this space, start with Deribit’s WebSocket API, build the chain subscriber first, validate that your Greek aggregation is correct on paper positions, and only then connect to live execution.
The firms that are winning in crypto options - Wintermute, Paradigm, the quantitative prop shops - have been building this infrastructure for 3-5 years. The gap between a working prototype and a production options book is measured in years of edge cases. Build the foundation right.
Continue Reading
Enjoyed this?
Get one deep infrastructure insight per week.
Free forever. Unsubscribe anytime.
You're in. Check your inbox.