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
This commit is contained in:
ramseshk
2026-08-07 17:52:20 +08:00
parent 31c1fe7fbe
commit 5304534e38
11 changed files with 1892 additions and 0 deletions
+168
View File
@@ -0,0 +1,168 @@
"""
Cross-margin liquidation waterfall prediction.
When a whale's cross-margin portfolio approaches liquidation, the
Hyperliquid liquidation engine selects which asset to dump first based
on maintenance margin requirements and order book liquidity.
By monitoring large cross-margin accounts via clearinghouseState,
we can predict which asset gets liquidated first and position accordingly:
- Widen spreads on the predicted liquidation asset
- Tighten spreads on non-liquidation assets
- Pre-position for the post-liquidation bounce
"""
from __future__ import annotations
import logging
from collections import deque
from typing import Optional
logger = logging.getLogger(__name__)
class LiquidationWaterfall:
"""Predict the order of cross-margin liquidations for large accounts."""
def __init__(
self,
danger_margin_ratio: float = 1.2, # margin_ratio < this = danger
critical_margin_ratio: float = 1.05, # margin_ratio < this = imminent
maintenance_margin_pct: float = 0.03, # 3% maintenance
book_depth_window: int = 10, # levels to estimate liquidity
):
self._danger = danger_margin_ratio
self._critical = critical_margin_ratio
self._mm_pct = maintenance_margin_pct
self._depth_window = book_depth_window
self._accounts: dict[str, dict] = {} # address → positions, equity
self._book_depths: dict[str, dict] = {} # coin → {bid_depth, ask_depth}
self._history: deque = deque(maxlen=1000)
# ── Data feed ────────────────────────────────────────────
def update_account(self, address: str, positions: list[dict], margin_balance: float):
"""Update a tracked account's positions and margin."""
self._accounts[address] = {
"positions": positions,
"margin_balance": margin_balance,
"margin_ratio": self._compute_margin_ratio(positions, margin_balance),
}
def update_book_depth(self, coin: str, bid_depth: float, ask_depth: float):
"""Update estimated order book depth for a coin."""
self._book_depths[coin.upper()] = {
"bid_depth": bid_depth,
"ask_depth": ask_depth,
}
# ── Risk assessment ─────────────────────────────────────
def _compute_margin_ratio(
self, positions: list[dict], margin_balance: float
) -> float:
"""Compute margin ratio = equity / maintenance_margin."""
if not positions or margin_balance <= 0:
return float("inf")
total_mm = 0.0
for pos in positions:
size = abs(float(pos.get("szi", 0)))
px = float(pos.get("entryPx", pos.get("markPx", 0)))
total_mm += size * px * self._mm_pct
return margin_balance / total_mm if total_mm > 0 else float("inf")
def at_risk_accounts(self) -> list[dict]:
"""List accounts approaching liquidation."""
risky = []
for addr, acct in self._accounts.items():
ratio = acct["margin_ratio"]
if ratio < self._danger:
level = "critical" if ratio < self._critical else "danger"
risky.append({
"address": addr[:10] + "...",
"margin_ratio": round(ratio, 3),
"level": level,
"positions": len(acct["positions"]),
})
return sorted(risky, key=lambda r: r["margin_ratio"])
def predict_liquidation_order(self, address: str) -> list[dict]:
"""Predict which assets get liquidated first for a given account.
Returns assets ranked by liquidation priority (first to go = top).
Uses: maintenance_margin_requirement / book_liquidity ratio.
Higher ratio = less liquid relative to margin cost = dumped first.
"""
acct = self._accounts.get(address)
if not acct:
return []
positions = acct["positions"]
ranked = []
for pos in positions:
coin = pos.get("coin", "").upper()
size = abs(float(pos.get("szi", 0)))
px = float(pos.get("entryPx", pos.get("markPx", 0)))
maintenance = size * px * self._mm_pct
# Liquidity: how much the book can absorb before significant slippage
book = self._book_depths.get(coin, {})
side = "buy" if float(pos.get("szi", 0)) < 0 else "sell"
depth = book.get("bid_depth" if side == "buy" else "ask_depth", size * px * 0.1)
# Liquidation priority score: higher = dumped first
liquidity_ratio = maintenance / max(depth, 1e-8)
ranked.append({
"coin": coin,
"size": size,
"notional_usd": round(size * px, 2),
"maintenance_usd": round(maintenance, 2),
"liquidity_ratio": round(liquidity_ratio, 4),
"predicted_first": False, # set below
})
# Sort by liquidity ratio (highest = least liquid = dumped first)
ranked.sort(key=lambda r: r["liquidity_ratio"], reverse=True)
if ranked:
ranked[0]["predicted_first"] = True
return ranked
def signal(self, address: str) -> dict:
"""Generate trading signal based on predicted liquidation waterfall.
If an account is in danger and we can predict the liquidation order:
- Asset predicted to be dumped first → widen spreads, go short
- Other assets in the portfolio → tighten spreads (safer to quote)
"""
acct = self._accounts.get(address)
if not acct or acct["margin_ratio"] > self._danger:
return {"action": "none", "reason": "account_safe"}
order = self.predict_liquidation_order(address)
if not order:
return {"action": "none", "reason": "no_positions"}
first_asset = order[0]
return {
"action": "position",
"reason": f"liquidation_imminent_{acct['margin_ratio']:.2f}",
"margin_ratio": round(acct["margin_ratio"], 3),
"liquidation_target": first_asset["coin"],
"strategy": {
first_asset["coin"]: "widen_spreads_2x",
**{r["coin"]: "tighten_spreads" for r in order[1:]},
},
"predicted_order": [r["coin"] for r in order],
}
def summary(self) -> dict:
return {
"at_risk_accounts": self.at_risk_accounts(),
"tracked_accounts": len(self._accounts),
}