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.
This commit is contained in:
ramseshk
2026-08-07 14:47:08 +08:00
parent 639dd4fb6d
commit 4f66ef36a9
13 changed files with 1229 additions and 0 deletions
View File
+116
View File
@@ -0,0 +1,116 @@
"""
Pre-trade toxicity filter — blocks quoting when market is adversarially informed.
Uses VPIN and flow toxicity metrics from microstructure module to
determine whether to quote, reduce size, or go flat.
"""
from __future__ import annotations
from collections import deque
from typing import Optional
class ToxicityFilter:
"""VPIN-based flow toxicity filter for market-making.
Monitors trade flow and order book to detect informed trading.
Blocks or reduces quoting when toxicity crosses thresholds.
Usage:
tf = ToxicityFilter()
tf.update_trade(buy_vol=0.1, sell_vol=0.05)
decision = tf.check()
if decision["allow_quoting"]:
size_mult = decision["size_multiplier"]
"""
def __init__(
self,
vpin_threshold: float = 0.3,
vpin_alarm: float = 0.5,
vpin_window: int = 50,
volume_bucket_size: float = 1.0,
size_reduction_steps: int = 5,
):
self._threshold = vpin_threshold
self._alarm = vpin_alarm
self._window = vpin_window
self._bucket_size = volume_bucket_size
self._steps = size_reduction_steps
self._buy_vol: deque[float] = deque(maxlen=5000)
self._sell_vol: deque[float] = deque(maxlen=5000)
self._current_vpin: float = 0.0
self._last_obi: float = 0.0
self._trade_count: int = 0
def update_trade(self, buy_vol: float, sell_vol: float):
"""Feed a classified trade volume observation."""
self._buy_vol.append(buy_vol)
self._sell_vol.append(sell_vol)
self._trade_count += 1
def update_book_imbalance(self, obi: float):
"""Feed current order-book imbalance."""
self._last_obi = obi
def compute(self):
"""Recompute VPIN from accumulated volume data."""
from microstructure.toxicity import compute_vpin
result = compute_vpin(
list(self._buy_vol),
list(self._sell_vol),
volume_bucket_size=self._bucket_size,
n_buckets=self._window,
)
self._current_vpin = result.get("vpin_value", 0.0)
def check(self) -> dict:
"""Evaluate current toxicity state.
Returns:
allow_quoting: whether to quote at all
size_multiplier: fraction of base size to quote (1.0 = full, 0.0 = none)
vpin: current VPIN
obi: current OBI
reason: explanation if blocked/reduced
"""
self.compute()
v = self._current_vpin
if v >= self._alarm:
return {
"allow_quoting": False,
"size_multiplier": 0.0,
"vpin": round(v, 4),
"obi": round(self._last_obi, 4),
"reason": f"VPIN {v:.3f} >= alarm {self._alarm}",
}
if v >= self._threshold:
reduction = (v - self._threshold) / (self._alarm - self._threshold)
size = max(0.0, 1.0 - reduction)
return {
"allow_quoting": size > 0.0,
"size_multiplier": round(size, 2),
"vpin": round(v, 4),
"obi": round(self._last_obi, 4),
"reason": f"VPIN {v:.3f} >= threshold {self._threshold}",
}
return {
"allow_quoting": True,
"size_multiplier": 1.0,
"vpin": round(v, 4),
"obi": round(self._last_obi, 4),
"reason": "ok",
}
@property
def vpin(self) -> float:
return self._current_vpin
@property
def trade_count(self) -> int:
return self._trade_count