Files
ramseshk 5304534e38 feat: advanced microstructure modules — HLP, Hawkes, Whipsaw, Term Structure, Liq Waterfall, Spoof Detector
6 new modules with 46 new tests (230 total):

#21 HLP Vault Monitor (live/monitors/hlp_vault.py):
  Tracks Hyperliquid's native protocol market maker at address
  0xfefefe... Queries clearinghouseState + metaAndAssetCtxs.
  - Delta exposure per asset (notional + PnL)
  - Overextension detection (notional exceeds  M threshold)
  - Rebalancing signals: fade_short when HLP too short,
    fade_long when HLP too long (front-run forced rebalancing)
  - Toxicity score: HLP losing money = absorbing informed flow
  - Historical delta tracking

#29 Hawkes Processes (microstructure/hawkes.py):
  Multivariate Hawkes calibrator for limit order book dynamics.
  - MLE calibration via SGD gradient descent on log-likelihood
  - Branching ratio enforcement (alpha/beta < 0.99 for stationarity)
  - Intensity computation λ_i(t) with cross-excitation
  - Activity forecasting (expected event count in horizon)
  - Synthetic event generator (Ogata thinning)
  - Pure functions: hawkes_intensity, hawkes_log_likelihood,
    generate_hawkes_events

#23 Funding Whipsaw Trader (live/strategies/funding_whipsaw.py):
  Premium index decay trading in final 60s of funding epoch.
  - Detects deterministic convergence of premium→0 at settlement
  - Time-scaled position sizing (larger closer to settlement)
  - Auto-close after funding epoch completes
  - Confidence scoring based on premium magnitude

#32 Term Structure Monitor (live/monitors/term_structure.py):
  Perp/quarterly/bi-quarterly futures basis curve trading.
  - Quarterly-perp basis with z-score anomaly detection
  - BiQ-quarterly curve steepness monitoring
  - Fair quarterly price via interest rate parity + funding carry
  - Calendar spread signals: buy_basis, sell_basis,
    curve_steepener, curve_flattener

#24 Liquidation Waterfall (live/monitors/liq_waterfall.py):
  Cross-margin liquidation order prediction.
  - Margin ratio tracking (equity / maintenance margin)
  - Danger/critical level classification
  - Asset liquidation priority: maintenance / book_liquidity ratio
    (least liquid asset relative to margin = dumped first)
  - Strategy output: widen_spreads on target, tighten on rest

#31 Spoof Detector (microstructure/spoof_detector.py):
  Adversarial ML-style spoofing pattern recognition.
  - Rule 1: Large order far from mid, cancelled immediately
  - Rule 2: Cancel right before trade approaches price level
  - Rule 3: Oversized order with no fill within short lifetime
  - Spoof probability (rolling window ratio)
  - Cancel-to-fill ratio monitoring
2026-08-07 17:52:20 +08:00

198 lines
7.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Term structure monitor — perp vs quarterly vs bi-quarterly futures basis.
Hyperliquid offers perpetual (funding-based), quarterly, and bi-quarterly
futures contracts. The basis curve (perp→quarterly→bi-quarterly) contains
information about market expectations and can be traded.
Anomalies:
- Perp funding deeply negative but quarterly basis remains steep →
go long perp (collect funding), short quarterly (lock basis)
- Quarterly futures converging to perp at expiration →
calendar spread mean-reversion
- Bi-quarterly premium over quarterly deviating from fair value →
curve steepener/flattener trades
"""
from __future__ import annotations
from collections import deque
from typing import Optional
class TermStructureMonitor:
"""Monitor perp/futures term structure for arbitrage opportunities.
Tracks:
- Perp funding rate and mark price
- Quarterly futures price
- Bi-quarterly futures price (if available)
- Basis spreads: quarterly-perp, biq-quarterly, biq-perp
- Calendar spread mean-reversion
"""
def __init__(
self,
funding_window: int = 1440, # 24h of 1-min samples
basis_window: int = 100,
):
self._funding_window = funding_window
self._basis_window = basis_window
self._perp_prices: dict[str, deque[float]] = {}
self._quarterly_prices: dict[str, deque[float]] = {}
self._biq_prices: dict[str, deque[float]] = {}
self._funding_rates: dict[str, deque[float]] = {}
self._funding_epoch_seconds: int = 8 * 3600
self._quarterly_expiry_days: int = 90
self._biq_expiry_days: int = 180
# ── Data feed ────────────────────────────────────────────
def update_perp(self, coin: str, price: float, funding_rate: float):
c = coin.upper()
self._perp_prices.setdefault(c, deque(maxlen=self._basis_window)).append(price)
self._funding_rates.setdefault(c, deque(maxlen=self._funding_window)).append(funding_rate)
def update_quarterly(self, coin: str, price: float):
self._quarterly_prices.setdefault(coin.upper(), deque(maxlen=self._basis_window)).append(price)
def update_biq(self, coin: str, price: float):
self._biq_prices.setdefault(coin.upper(), deque(maxlen=self._basis_window)).append(price)
# ── Basis computation ────────────────────────────────────
def quarterly_perp_basis(self, coin: str) -> dict | None:
"""Basis between quarterly future and perpetual."""
q = list(self._quarterly_prices.get(coin.upper(), []))
p = list(self._perp_prices.get(coin.upper(), []))
min_len = min(len(q), len(p))
if min_len < 2:
return None
qq = q[-min_len:]
pp = p[-min_len:]
basis_bps = [(qq[i] - pp[i]) / pp[i] * 10000 for i in range(min_len) if pp[i] > 0]
if not basis_bps:
return None
return {
"current_bps": round(basis_bps[-1], 2),
"mean_bps": round(sum(basis_bps) / len(basis_bps), 2),
"std_bps": round(_std(basis_bps), 2),
"z_score": round((basis_bps[-1] - sum(basis_bps) / len(basis_bps)) / max(_std(basis_bps), 0.01), 2),
"n_samples": len(basis_bps),
}
def biq_quarterly_basis(self, coin: str) -> dict | None:
"""Basis between bi-quarterly and quarterly futures (curve steepness)."""
bq = list(self._biq_prices.get(coin.upper(), []))
q = list(self._quarterly_prices.get(coin.upper(), []))
min_len = min(len(bq), len(q))
if min_len < 2:
return None
bb = bq[-min_len:]
qq = q[-min_len:]
basis_bps = [(bb[i] - qq[i]) / qq[i] * 10000 for i in range(min_len) if qq[i] > 0]
if not basis_bps:
return None
return {
"current_bps": round(basis_bps[-1], 2),
"mean_bps": round(sum(basis_bps) / len(basis_bps), 2),
"std_bps": round(_std(basis_bps), 2),
"z_score": round((basis_bps[-1] - sum(basis_bps) / len(basis_bps)) / max(_std(basis_bps), 0.01), 2),
"n_samples": len(basis_bps),
}
def fair_quarterly_price(self, coin: str, risk_free_annual: float = 0.05) -> dict | None:
"""Compute fair quarterly price from perp via interest rate parity."""
p = list(self._perp_prices.get(coin.upper(), []))
if not p:
return None
perp_px = p[-1]
days = self._quarterly_expiry_days
funding = list(self._funding_rates.get(coin.upper(), []))
avg_funding = sum(funding[-100:]) / max(len(funding[-100:]), 1) if funding else 0
# Fair quarterly = perp * (1 + (r + avg_funding) * days/365)
carry_rate = risk_free_annual + avg_funding * 3 * 365 # annualize 8h funding
fair_px = perp_px * (1 + carry_rate * days / 365)
return {
"perp_price": perp_px,
"fair_quarterly": round(fair_px, 2),
"carry_rate_annual_pct": round(carry_rate * 100, 2),
"days_to_expiry": days,
}
def signal(self, coin: str) -> dict:
"""Generate term-structure trading signal.
Returns:
signal: "buy_basis", "sell_basis", "curve_steepener", "curve_flattener", "none"
"""
c = coin.upper()
qp = self.quarterly_perp_basis(c)
bq = self.biq_quarterly_basis(c)
fair = self.fair_quarterly_price(c)
signals = []
# Check quarterly-perp basis anomalies
if qp and abs(qp["z_score"]) > 2.0:
if qp["z_score"] > 0:
signals.append({
"signal": "sell_basis",
"reason": f"Quarterly {qp['z_score']:.1f}σ rich vs perp",
"confidence": min(1.0, abs(qp["z_score"]) / 4.0),
})
else:
signals.append({
"signal": "buy_basis",
"reason": f"Quarterly {qp['z_score']:.1f}σ cheap vs perp",
"confidence": min(1.0, abs(qp["z_score"]) / 4.0),
})
# Check curve steepness
if bq and abs(bq["z_score"]) > 2.0:
if bq["z_score"] > 0:
signals.append({
"signal": "curve_flattener",
"reason": f"BiQ {bq['z_score']:.1f}σ rich vs quarterly",
"confidence": min(1.0, abs(bq["z_score"]) / 4.0),
})
else:
signals.append({
"signal": "curve_steepener",
"reason": f"BiQ {bq['z_score']:.1f}σ cheap vs quarterly",
"confidence": min(1.0, abs(bq["z_score"]) / 4.0),
})
result = {
"coin": c,
"signals": signals,
"primary_signal": signals[0]["signal"] if signals else "none",
"quarterly_perp_basis": qp,
"biq_quarterly_basis": bq,
"fair_quarterly": fair,
}
return result
def summary(self) -> dict:
return {coin: self.signal(coin) for coin in self._perp_prices}
def _std(vals: list[float]) -> float:
"""Population standard deviation."""
if len(vals) < 2:
return 0.0
mean = sum(vals) / len(vals)
return (sum((v - mean) ** 2 for v in vals) / len(vals)) ** 0.5