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

196 lines
7.1 KiB
Python

"""
Funding rate whipsaw trader — premium index decay in final seconds of funding epoch.
Hyperliquid funding settles every 8 hours (UTC 00:00, 08:00, 16:00).
In the final 60 seconds before settlement, the premium index (Mark - Oracle)
must converge to prevent arbitrage. HFTs trade this convergence deterministically.
The strategy:
- If premium is positive with <60s until funding → SHORT perp (price will drop)
- If premium is negative with <60s until funding → LONG perp (price will rise)
- Scale position based on premium magnitude and time remaining
- Close position at funding settlement (T+0)
"""
from __future__ import annotations
import time
from datetime import datetime, timezone
from typing import Optional
class FundingWhipsawTrader:
"""Trade the deterministic decay of the premium index in the final seconds
before Hyperliquid's funding settlement.
Funding epochs: 00:00, 08:00, 16:00 UTC every day.
Premium index = (mark_price - oracle_price) / oracle_price
Usage:
trader = FundingWhipsawTrader()
trader.update(mark_px=64500, oracle_px=64480)
signal = trader.signal()
if signal['action'] != 'none':
# place order: signal['side'], signal['size'], signal['confidence']
"""
def __init__(
self,
min_premium_bps: float = 0.5, # minimum premium in bps to trigger
max_size: float = 0.001, # max position size
enter_seconds_before: float = 60.0, # seconds before funding to enter
close_seconds_after: float = 5.0, # seconds after funding to close
):
self._min_premium_bps = min_premium_bps
self._max_size = max_size
self._enter_seconds = enter_seconds_before
self._close_seconds = close_seconds_after
self._mark_px: float = 0.0
self._oracle_px: float = 0.0
self._premium_bps: float = 0.0
self._last_update: float = 0.0
self._in_position: bool = False
self._position_side: str = ""
self._entry_time: float = 0.0
def update(self, mark_px: float, oracle_px: float):
"""Feed current mark and oracle prices."""
self._mark_px = mark_px
self._oracle_px = oracle_px
self._last_update = time.time()
if oracle_px > 0:
self._premium_bps = (mark_px - oracle_px) / oracle_px * 10000
def seconds_to_funding(self) -> float:
"""Seconds until the next funding settlement (every 8 hours, UTC)."""
now = datetime.now(timezone.utc)
epoch_hours = [0, 8, 16] # Funding at 00:00, 08:00, 16:00 UTC
current_hour = now.hour
# Find next funding epoch
next_epoch_hour = None
for h in epoch_hours:
if h > current_hour or (h == current_hour and now.minute == 0 and now.second < 5):
next_epoch_hour = h
break
if next_epoch_hour is None:
# After 16:00, next is 00:00 tomorrow
next_epoch = now.replace(hour=0, minute=0, second=0, microsecond=0)
from datetime import timedelta
next_epoch += timedelta(days=1)
else:
next_epoch = now.replace(hour=next_epoch_hour, minute=0, second=0, microsecond=0)
delta = (next_epoch - now).total_seconds()
return max(0.0, delta)
def seconds_since_funding(self) -> float:
"""Seconds since the most recent funding settlement."""
seconds_to = self.seconds_to_funding()
if seconds_to < 3600: # <1 hour to next
return 8 * 3600 - seconds_to
return 8 * 3600 + (3600 - seconds_to % 3600) # Approximate
def signal(self) -> dict:
"""Generate trading signal based on premium and time to funding.
Returns:
action: "enter_long", "enter_short", "close", "none"
side: "buy" or "sell" (for orders)
size: position size (scaled by time remaining)
confidence: 0-1 confidence in the signal
premium_bps: current premium in bps
seconds_to_funding: time until settlement
"""
secs = self.seconds_to_funding()
premium = self._premium_bps
# After funding + small delay: close any position
secs_since = self.seconds_since_funding()
if self._in_position and secs_since < self._close_seconds:
self._in_position = False
return {
"action": "close",
"side": "sell" if self._position_side == "buy" else "buy",
"size": self._max_size,
"confidence": 1.0,
"premium_bps": round(premium, 2),
"seconds_to_funding": round(secs, 1),
"reason": "funding_settled",
}
# Outside entry window: no action
if secs > self._enter_seconds or secs < 0:
return {
"action": "none",
"side": "",
"size": 0.0,
"confidence": 0.0,
"premium_bps": round(premium, 2),
"seconds_to_funding": round(secs, 1),
"reason": "outside_entry_window",
}
# Already in position
if self._in_position:
return {
"action": "hold",
"side": self._position_side,
"size": self._max_size,
"confidence": 0.8,
"premium_bps": round(premium, 2),
"seconds_to_funding": round(secs, 1),
"reason": "holding",
}
# Check premium threshold
if abs(premium) < self._min_premium_bps:
return {
"action": "none",
"side": "",
"size": 0.0,
"confidence": 0.0,
"premium_bps": round(premium, 2),
"seconds_to_funding": round(secs, 1),
"reason": "premium_too_small",
}
# Scale size by time remaining (more remaining = more uncertainty = smaller size)
time_factor = max(0.3, secs / self._enter_seconds)
scaled_size = self._max_size * (1.0 - time_factor * 0.5)
if premium > 0:
# Premium positive → perp is expensive → short it
side = "sell"
action = "enter_short"
confidence = min(1.0, abs(premium) / self._min_premium_bps * 0.3)
else:
# Premium negative → perp is cheap → long it
side = "buy"
action = "enter_long"
confidence = min(1.0, abs(premium) / self._min_premium_bps * 0.3)
self._in_position = True
self._position_side = side
self._entry_time = time.time()
return {
"action": action,
"side": side,
"size": round(scaled_size, 8),
"confidence": round(confidence, 3),
"premium_bps": round(premium, 2),
"seconds_to_funding": round(secs, 1),
"reason": f"premium_{premium:.1f}bps_{secs:.0f}s",
}
@property
def premium_bps(self) -> float:
return self._premium_bps
@property
def in_position(self) -> bool:
return self._in_position