a13e3fe045
Common (mcp_common): - indicators.py: vol_cone, hurst_exponent, half_life_mean_reversion, garch11_forecast, autocorrelation, rolling_sharpe, var_cvar - options.py (nuovo): oi_weighted_skew, smile_asymmetry, atm_vs_wings_vol, dealer_gamma_profile, vanna_charm_aggregate - microstructure.py (nuovo): orderbook_imbalance (ratio + microprice + slope) - stats.py (nuovo): cointegration_test Engle-Granger + ADF helper Deribit (+6 tool MCP): - get_dealer_gamma_profile (net dealer gamma + flip level) - get_vanna_charm (vanna/charm aggregati pesati OI) - get_oi_weighted_skew, get_smile_asymmetry, get_atm_vs_wings_vol - get_orderbook_imbalance Bybit (+2 tool MCP): - get_orderbook_imbalance, get_basis_term_structure (futures dated curve) Macro (+2 tool MCP): - get_yield_curve_slope (2y10y/5y30y + butterfly + regime) - get_breakeven_inflation (FRED T5YIE/T10YIE/T5YIFR) Sentiment (+3 tool MCP): - get_funding_arb_spread (opportunità arb compatte annualizzate) - get_liquidation_heatmap (heuristic da OI delta + funding extreme, no feed paid Coinglass) - get_cointegration_pairs (Engle-Granger su coppie crypto Binance hourly) Tutto in TDD pure-Python (no numpy/scipy in mcp_common). README aggiornato con elenco completo. 442 test totali verdi. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
78 lines
2.4 KiB
Python
78 lines
2.4 KiB
Python
"""Microstructure indicators: orderbook imbalance, slope, microprice.
|
|
|
|
Tutte le funzioni accettano bids/asks come list[list[price, qty]] (formato
|
|
standard dei ticker exchange) e ritornano metriche aggregate exchange-agnostic.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
|
|
def orderbook_imbalance(
|
|
bids: list[list[float]],
|
|
asks: list[list[float]],
|
|
depth: int = 10,
|
|
) -> dict[str, float | None]:
|
|
"""Imbalance ratio = (bid_vol - ask_vol) / (bid_vol + ask_vol) sui top-`depth`
|
|
livelli. Range [-1, +1]. Positivo = bid pressure, negativo = ask pressure.
|
|
|
|
Microprice (Stoll-Bertsimas): mid pesato dalla size opposta
|
|
→ P_micro = (P_bid * Q_ask + P_ask * Q_bid) / (Q_bid + Q_ask). Best level only.
|
|
|
|
Slope: variazione cumulata di volume per unità di prezzo (proxy per
|
|
liquidità in profondità).
|
|
"""
|
|
if not bids and not asks:
|
|
return {
|
|
"imbalance_ratio": None,
|
|
"bid_volume": 0.0,
|
|
"ask_volume": 0.0,
|
|
"microprice": None,
|
|
"bid_slope": None,
|
|
"ask_slope": None,
|
|
}
|
|
|
|
top_bids = bids[:depth]
|
|
top_asks = asks[:depth]
|
|
bid_vol = sum(q for _, q in top_bids)
|
|
ask_vol = sum(q for _, q in top_asks)
|
|
total = bid_vol + ask_vol
|
|
|
|
if total == 0:
|
|
ratio = None
|
|
else:
|
|
ratio = (bid_vol - ask_vol) / total
|
|
|
|
# Microprice: best bid, best ask. Weighted by opposite-side size.
|
|
microprice = None
|
|
if top_bids and top_asks:
|
|
bp, bq = top_bids[0]
|
|
ap, aq = top_asks[0]
|
|
denom = bq + aq
|
|
if denom > 0:
|
|
microprice = (bp * aq + ap * bq) / denom
|
|
|
|
bid_slope = _depth_slope(top_bids, ascending_price=False)
|
|
ask_slope = _depth_slope(top_asks, ascending_price=True)
|
|
|
|
return {
|
|
"imbalance_ratio": ratio,
|
|
"bid_volume": bid_vol,
|
|
"ask_volume": ask_vol,
|
|
"microprice": microprice,
|
|
"bid_slope": bid_slope,
|
|
"ask_slope": ask_slope,
|
|
}
|
|
|
|
|
|
def _depth_slope(levels: list[list[float]], ascending_price: bool) -> float | None:
|
|
"""Calcola |Δq / Δp| sul primo vs penultimo livello.
|
|
Slope alto = liquidità che crolla rapidamente in profondità (book sottile).
|
|
"""
|
|
if len(levels) < 2:
|
|
return None
|
|
p_first, q_first = levels[0]
|
|
p_last, q_last = levels[-1]
|
|
dp = abs(p_last - p_first)
|
|
if dp == 0:
|
|
return None
|
|
return abs(q_first - q_last) / dp
|