5304534e38
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
154 lines
6.0 KiB
Python
154 lines
6.0 KiB
Python
"""
|
|
Tests for live/monitors/hlp_vault.py — HLP protocol-level market maker tracking.
|
|
"""
|
|
from unittest.mock import patch
|
|
from live.monitors.hlp_vault import HlpVaultMonitor
|
|
|
|
HLP_ADDRESS = "0xfefefefefefefefefefefefefefefefefefefefe"
|
|
|
|
def _mock_meta(): return {"universe": [
|
|
{"name": "BTC", "szDecimals": 5},
|
|
{"name": "ETH", "szDecimals": 6},
|
|
{"name": "SOL", "szDecimals": 7},
|
|
]}
|
|
|
|
def _mock_asset_ctxs(): return [
|
|
{"funding": "0.00001", "markPx": "64500", "oraclePx": "64480", "openInterest": "50000000"},
|
|
{"funding": "0.000005", "markPx": "3200", "oraclePx": "3195", "openInterest": "30000000"},
|
|
{"funding": "0.00002", "markPx": "140", "oraclePx": "139.5", "openInterest": "10000000"},
|
|
]
|
|
|
|
def _mock_clearinghouse(positions=None):
|
|
aps = []
|
|
if positions:
|
|
for coin, (side, szi, entry_px, upnl) in positions.items():
|
|
aps.append({"type": "oneWay", "position": {
|
|
"coin": coin, "side": side, "szi": str(szi),
|
|
"entryPx": str(entry_px), "unrealizedPnl": str(upnl),
|
|
}})
|
|
return {"assetPositions": aps, "withdrawable": "1000000"}
|
|
|
|
|
|
class TestHlpVaultMonitor:
|
|
|
|
def test_initial_state_empty(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
s = monitor.summary()
|
|
assert s["assets_tracked"] == 0
|
|
assert s["total_delta_usd"] == 0.0
|
|
|
|
def test_update_populates_positions(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()], # metaAndAssetCtxs → list
|
|
_mock_clearinghouse({"BTC": ("A", 10.5, 64000, 5250),
|
|
"ETH": ("B", 50.0, 3100, -2500)}), # clearinghouseState → dict
|
|
]
|
|
monitor.update()
|
|
assert monitor.position("BTC") < 0 # side=A = short
|
|
assert monitor.position("ETH") > 0 # side=B = long
|
|
assert monitor.summary()["assets_tracked"] >= 2
|
|
|
|
def test_delta_exposure_usd(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 10.0, 64000, 5000)}),
|
|
]
|
|
monitor.update()
|
|
delta = monitor.delta_exposure()
|
|
assert "BTC" in delta
|
|
assert abs(delta["BTC"]["notional_usd"]) > 600000
|
|
|
|
def test_is_overextended(self):
|
|
monitor = HlpVaultMonitor(testnet=True, overextended_threshold=5.0)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 100.0, 64000, 50000)}),
|
|
]
|
|
monitor.update()
|
|
assert monitor.is_overextended("BTC")
|
|
|
|
def test_not_overextended_with_small_position(self):
|
|
monitor = HlpVaultMonitor(testnet=True, overextended_threshold=5.0)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 1.0, 64000, 500)}),
|
|
]
|
|
monitor.update()
|
|
assert not monitor.is_overextended("BTC")
|
|
|
|
def test_rebalancing_signal_long(self):
|
|
monitor = HlpVaultMonitor(testnet=True, overextended_threshold=5.0)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 100.0, 64000, 50000)}),
|
|
]
|
|
monitor.update()
|
|
signal = monitor.rebalancing_signal("BTC")
|
|
assert signal["overextended"]
|
|
assert signal["signal"] in ("fade_short", "fade_long", "neutral")
|
|
|
|
def test_rebalancing_signal_short(self):
|
|
monitor = HlpVaultMonitor(testnet=True, overextended_threshold=5.0)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"ETH": ("B", 2000.0, 3100, 50000)}),
|
|
]
|
|
monitor.update()
|
|
signal = monitor.rebalancing_signal("ETH")
|
|
assert signal["overextended"]
|
|
|
|
def test_toxicity_score_zero_with_no_data(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
assert monitor.toxicity_score() == 0.0
|
|
|
|
def test_toxicity_score_detects_losing_flow(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({
|
|
"BTC": ("A", 10.0, 64000, -50000),
|
|
"ETH": ("A", 50.0, 3100, -25000),
|
|
}),
|
|
]
|
|
monitor.update()
|
|
score = monitor.toxicity_score()
|
|
assert score > 0
|
|
|
|
def test_historical_tracking(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 10.0, 64000, 0)}),
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 12.0, 64100, 1000)}),
|
|
]
|
|
monitor.update()
|
|
monitor.update()
|
|
history = monitor.delta_history("BTC")
|
|
assert len(history) == 2
|
|
|
|
def test_summary_includes_all_fields(self):
|
|
monitor = HlpVaultMonitor(testnet=True)
|
|
with patch.object(monitor, '_api_post') as m:
|
|
m.side_effect = [
|
|
[_mock_meta(), _mock_asset_ctxs()],
|
|
_mock_clearinghouse({"BTC": ("A", 10.0, 64000, 5000)}),
|
|
]
|
|
monitor.update()
|
|
s = monitor.summary()
|
|
assert "assets_tracked" in s
|
|
assert "total_delta_usd" in s
|
|
assert "toxicity_score" in s
|
|
assert "overextended_assets" in s
|
|
assert "signals" in s
|