feat: advanced microstructure — sequencer latency, dealer GEX, tick regime, triangular arb

4 new modules with 20 tests:

#22 Sequencer Latency Detector (live/monitors/sequencer_latency.py):
  Detects stale-state windows between WebSocket and REST API.
  - WebSocket vs REST timestamp delta tracking
  - Transport latency percentiles (p50, p99)
  - Liquidation-triggered stale-state detection
  - Stale asset identification for cross-margin arbitrage

#25 Dealer GEX (microstructure/dealer_gex.py):
  Dealer Gamma Exposure estimation via Black-Scholes.
  - Per-strike gamma × OI × spot² GEX computation
  - Pin level detection (strikes where dealers are long gamma)
  - Net GEX aggregation
  - Signal: fade_breakout (long gamma pinning) vs ride_momentum
    (short gamma amplification)
  - nearest_pin() for distance-to-magnet calculation

#30 Tick-Size Regime Exploitation (microstructure/tick_regime.py):
  Detects when asset price approaches tick-size boundaries.
  - Hyperliquid tick schedule (BTC 0.1/0.5, ETH 0.01/0.05, SOL 0.001/0.005)
  - Boundary approach detection with configurable threshold
  - Linear trend estimation for expected bars-to-cross
  - Signal: widen_quotes or tighten_quotes with urgency classification
  - Per-coin state tracking

#33 Triangular Latency Arb (live/monitors/triangular_arb.py):
  Cross-venue A→B→C triangular arbitrage detection.
  - Internal triangular: BTC-USDT → ETH-BTC → ETH-USDT
  - Cross-venue: price discrepancy across slow/fast venues
  - Latency gap detection between venue pairs
  - Implied cross-rate computation vs direct quote
  - Minimum spread threshold gating
This commit is contained in:
ramseshk
2026-08-10 10:34:56 +08:00
parent 27c096dc9e
commit e0be9f4d40
5 changed files with 937 additions and 0 deletions
+179
View File
@@ -0,0 +1,179 @@
"""
Dealer Gamma Exposure (GEX) estimation and pinning/fading signals.
In options markets, dealers delta-hedge their portfolios. Their gamma
exposure determines whether they amplify or suppress price movements:
- Long gamma → dealers buy low, sell high → suppress vol, "pin" price
- Short gamma → dealers buy high, sell low → amplify vol, create gamma squeeze
GEX = Σ (gamma_per_contract × open_interest × spot_price)
For crypto (no options on Hyperliquid yet), we approximate GEX using:
- BTC/ETH options open interest from Deribit
- Estimated dealer gamma via Black-Scholes
- Pin levels at strikes with max GEX
Strategy: when dealer GEX is heavily positive at a strike, fade breakouts.
When GEX is negative, ride the momentum with the dealers.
"""
from __future__ import annotations
import math
from collections import defaultdict
from typing import Optional
class DealerGEX:
"""Estimate dealer gamma exposure and generate pinning/fading signals.
Uses option open interest data and Black-Scholes gamma to compute
per-strike GEX. Aggregates to net GEX and identifies pin levels.
Usage:
gex = DealerGEX(spot=64500, risk_free=0.05)
gex.add_strike(strike=65000, call_oi=500, put_oi=300, expiry_days=7, iv=0.60)
print(gex.pin_levels())
print(gex.signal())
"""
def __init__(
self,
spot: float = 0.0,
risk_free: float = 0.05,
pin_threshold: float = 0.01, # 1% of spot to consider "at pin level"
):
self._spot = spot
self._rf = risk_free
self._pin_threshold = pin_threshold
self._strikes: list[dict] = [] # [{strike, call_oi, put_oi, gamma, gex_usd, ...}]
self._net_gex: float = 0.0
self._pin_levels: list[dict] = []
# ── Data feed ────────────────────────────────────────────
def set_spot(self, spot: float):
self._spot = spot
def add_strike(
self,
strike: float,
call_oi: float = 0,
put_oi: float = 0,
expiry_days: float = 30,
iv: float = 0.50,
):
"""Add OI data at a specific strike."""
gamma_call = self._bs_gamma(strike, "call", expiry_days, iv)
gamma_put = self._bs_gamma(strike, "put", expiry_days, iv)
# GEX per strike = gamma × OI × spot² × 0.01 (scaling)
gex_call = gamma_call * call_oi * self._spot * self._spot * 0.01
gex_put = gamma_put * put_oi * self._spot * self._spot * 0.01
self._strikes.append({
"strike": strike,
"call_oi": call_oi,
"put_oi": put_oi,
"gamma_call": round(gamma_call, 8),
"gamma_put": round(gamma_put, 8),
"gex_call_usd": round(gex_call, 2),
"gex_put_usd": round(gex_put, 2),
"gex_total_usd": round(gex_call + gex_put, 2),
})
def compute(self):
"""Aggregate GEX and find pin levels."""
self._net_gex = sum(s["gex_total_usd"] for s in self._strikes)
# Find pin levels: strikes where GEX is highest (magnets)
# and is positive (dealers long gamma = pinning)
if self._strikes:
max_gex = max(abs(s["gex_total_usd"]) for s in self._strikes)
self._pin_levels = [
{"strike": s["strike"], "gex_usd": s["gex_total_usd"], "is_pin": s["gex_total_usd"] > 0}
for s in self._strikes if abs(s["gex_total_usd"]) > max_gex * 0.3
]
self._pin_levels.sort(key=lambda l: abs(l["gex_usd"]), reverse=True)
# ── Signals ─────────────────────────────────────────────
def nearest_pin(self) -> dict | None:
"""Find the nearest pin level to current spot."""
if not self._spot or not self._strikes:
return None
distances = []
for s in self._strikes:
dist_pct = abs(s["strike"] - self._spot) / self._spot
if s["gex_total_usd"] > 0: # only positive GEX acts as pin
distances.append({
"strike": s["strike"],
"distance_pct": round(dist_pct * 100, 2),
"gex_usd": s["gex_total_usd"],
"strength": "strong" if s["gex_total_usd"] > abs(self._net_gex) * 0.1 else "weak",
})
if not distances:
return None
return min(distances, key=lambda d: d["distance_pct"])
def signal(self) -> dict:
"""Generate trading signal based on dealer GEX.
Returns:
signal: "fade_breakout", "ride_momentum", "neutral"
reason: explanation
"""
self.compute()
if not self._spot:
return {"signal": "neutral", "reason": "no_spot"}
pin = self.nearest_pin()
dist_pct = pin["distance_pct"] if pin else 100.0
if self._net_gex > 1e6 and pin and dist_pct < self._pin_threshold * 100:
return {
"signal": "fade_breakout",
"reason": f"Dealers long gamma ${self._net_gex:,.0f} — pinning at ${pin['strike']:,.0f} ({dist_pct:.1f}%)",
"net_gex_usd": round(self._net_gex, 2),
"pin_strike": pin["strike"],
"direction": "mean_reversion",
}
elif self._net_gex < -1e6:
return {
"signal": "ride_momentum",
"reason": f"Dealers short gamma ${self._net_gex:,.0f} — amplifying moves",
"net_gex_usd": round(self._net_gex, 2),
"direction": "trend_following",
}
else:
return {"signal": "neutral", "reason": "balanced_gex", "net_gex_usd": round(self._net_gex, 2)}
# ── Black-Scholes gamma ─────────────────────────────────
def _bs_gamma(self, strike: float, opt_type: str, T_days: float, sigma: float) -> float:
"""Black-Scholes gamma for a European option."""
if self._spot <= 0 or strike <= 0 or T_days <= 0 or sigma <= 0:
return 0.0
T = T_days / 365.0
d1 = (math.log(self._spot / strike) + (self._rf + 0.5 * sigma * sigma) * T) / (sigma * math.sqrt(T))
phi = math.exp(-d1 * d1 / 2.0) / math.sqrt(2.0 * math.pi)
return phi / (self._spot * sigma * math.sqrt(T))
# ── Summary ─────────────────────────────────────────────
def summary(self) -> dict:
pin = self.nearest_pin()
return {
"spot": self._spot,
"net_gex_usd": round(self._net_gex, 2),
"net_gex_m": round(self._net_gex / 1e6, 2),
"strikes_tracked": len(self._strikes),
"pin_levels": self._pin_levels[:5],
"nearest_pin": pin,
"signal": self.signal(),
}