Files
ramseshk 4f66ef36a9 feat: Phase 4 — controlled strategy deployment module + 38 tests
New live/ sub-modules for production-ready market making:

live/filters/toxicity.py (ToxicityFilter):
  VPIN-based pre-trade filter. Accumulates buy/sell volume, computes
  VPIN via microstructure module, produces quoting decision:
    - allow_quoting: bool
    - size_multiplier: 0.0–1.0 (graduated reduction approaching alarm)
    - granular thresholds (threshold vs alarm) with smooth reduction

live/treasury.py (Treasury):
  Central capital/risk management — single source of truth:
  - Position tracking per coin (opening, closing, average entry)
  - Realized + unrealized PnL computation
  - Pre-trade constraint checks (inventory limits, fee estimates)
  - Circuit breaker (drawdown, trade count, toxic fill rate, API errors)
  - Liquidation distance monitoring
  - Automatic cooldown reset after trip expiry

live/makers/hl_btc_eth.py:
  HlMaker — per-coin market maker integrating:
    - AvellanedaStoikovMaker (Phase 3) for optimal quotes
    - ToxicityFilter for pre-trade gating
    - Treasury for position/risk checks
  HlMakerPool — manages multiple HlMaker instances with shared treasury
    and coordinated observe_all()/quote_all()

live/monitors/cross_venue.py (CrossVenueMonitor):
  Cross-exchange lead-lag detection via cross-correlation at multiple
  lags. Spot premium (basis proxy) computation. Multi-venue summary.

live/monitors/funding_basis.py (FundingBasisMonitor):
  Funding regime classification, momentum detection, carry PnL
  estimation, basis spread analysis. Uses microstructure/funding.py.

live/monitors/liq_risk.py (LiquidationRiskOverlay):
  Per-position liquidation distance monitoring with tiered warnings
  (safe/warning/danger/critical). Recommended position reduction.

38 tests across 4 files (all pass):
  test_live_filters.py (5)
  test_live_maker.py (9)
  test_live_monitors.py (11)
  test_live_treasury.py (13)

Total test suite: 172 tests, all passing.
2026-08-07 14:47:08 +08:00

100 lines
3.5 KiB
Python

"""
Funding and basis carry monitor.
Tracks funding rates across Hyperliquid and estimates carry
trade profitability. Signals when funding arbitrage is attractive.
Phase 4: informational only — no automated trading.
"""
from __future__ import annotations
from collections import deque
from typing import Optional
from microstructure.funding import (
funding_regime,
funding_predictability,
funding_carry_pnl,
)
class FundingBasisMonitor:
"""Monitor funding rates and carry trade opportunities.
Usage:
monitor = FundingBasisMonitor()
monitor.update_funding("BTC", 0.0001)
monitor.update_spot("BTC", 50000.0)
monitor.update_perp("BTC", 50005.0)
result = monitor.signal("BTC")
"""
def __init__(
self,
funding_window: int = 1440, # 24h at 1 sample/min
samples_per_hour: int = 60,
):
self._samples_per_hour = samples_per_hour
self._funding: dict[str, deque[float]] = {}
self._perp_prices: dict[str, deque[float]] = {}
self._spot_prices: dict[str, deque[float]] = {}
self._window = funding_window
def update_funding(self, coin: str, rate: float):
"""Record an hourly funding rate."""
self._funding.setdefault(coin.upper(), deque(maxlen=self._window)).append(rate)
def update_perp(self, coin: str, price: float):
self._perp_prices.setdefault(coin.upper(), deque(maxlen=self._window)).append(price)
def update_spot(self, coin: str, price: float):
self._spot_prices.setdefault(coin.upper(), deque(maxlen=self._window)).append(price)
def signal(self, coin: str) -> dict:
"""Generate funding/basis signal for a coin."""
c = coin.upper()
funding_list = list(self._funding.get(c, []))
perp_list = list(self._perp_prices.get(c, []))
spot_list = list(self._spot_prices.get(c, []))
if not funding_list:
return {"signal": "insufficient_data", "action": "none"}
regime = funding_regime(funding_list, window_hours=min(24, len(funding_list) // self._samples_per_hour),
n_samples_per_hour=self._samples_per_hour)
predictability = funding_predictability(funding_list)
basis = None
if perp_list and spot_list:
from microstructure.funding import basis_spread
basis = basis_spread(perp_list, spot_list)
carry = funding_carry_pnl(
funding_list,
position_size=1.0,
mark_prices=perp_list if perp_list else None,
n_samples_per_hour=self._samples_per_hour,
)
regime_name = regime.get("regime", "unknown")
action = "none"
if regime_name in ("high_positive",) and predictability.get("is_momentum"):
action = "consider_short" # shorts earn positive funding
elif regime_name in ("high_negative",) and predictability.get("is_momentum"):
action = "consider_long"
return {
"signal": regime_name,
"action": action,
"funding_mean_annual_pct": regime.get("mean_annual_pct", 0),
"momentum": predictability.get("momentum_strength", 0),
"carry_cumulative_pnl": carry.get("cumulative_pnl", 0),
"basis_current_bps": basis.get("current_basis_bps", 0) if basis else 0,
"basis_mean_bps": basis.get("mean_basis_bps", 0) if basis else 0,
}
def summary(self) -> dict:
return {coin: self.signal(coin) for coin in self._funding}