4f66ef36a9
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.
117 lines
3.6 KiB
Python
117 lines
3.6 KiB
Python
"""
|
|
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
|