"""Liquidity and market quality analysis.""" from __future__ import annotations import json import numpy as np import pandas as pd def analyze_volume_patterns( volume: pd.Series, event_dates: list | None = None, ) -> pd.DataFrame: """Analyze volume patterns: trends, day-of-week effects, event-day spikes.""" stats_data = { "mean_volume": volume.mean(), "median_volume": volume.median(), "std_volume": volume.std(), "max_volume": volume.max(), "total_volume": volume.sum(), } if hasattr(volume.index, "dayofweek"): dow = volume.groupby(volume.index.dayofweek).mean() for d, label in enumerate(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]): if d in dow.index: stats_data[f"avg_volume_{label}"] = dow[d] if event_dates: event_ts = pd.to_datetime(event_dates) event_vol = volume[volume.index.isin(event_ts)] non_event_vol = volume[~volume.index.isin(event_ts)] if len(event_vol) > 0 and len(non_event_vol) > 0: stats_data["avg_event_day_volume"] = event_vol.mean() stats_data["avg_non_event_volume"] = non_event_vol.mean() stats_data["event_volume_ratio"] = event_vol.mean() / non_event_vol.mean() return pd.DataFrame([stats_data]) def compute_concentration_metrics(market_volumes: pd.DataFrame) -> pd.DataFrame: """Compute HHI and share metrics across markets.""" total = market_volumes.sum(axis=1) shares = market_volumes.div(total, axis=0) hhi = (shares ** 2).sum(axis=1) top1 = shares.max(axis=1) top3 = shares.apply(lambda row: row.nlargest(3).sum(), axis=1) return pd.DataFrame({ "hhi": hhi, "top1_share": top1, "top3_share": top3, }) def estimate_liquidity_regressions( spreads: pd.Series, volume: pd.Series, volatility: pd.Series, event_dummy: pd.Series | None = None, ) -> dict: """Regress spread on volume, volatility, and event dummies.""" import statsmodels.api as sm X = pd.DataFrame({"volume": volume, "volatility": volatility}) if event_dummy is not None: X["event"] = event_dummy X = sm.add_constant(X) valid = pd.concat([spreads.rename("spread"), X], axis=1).dropna() if len(valid) < 10: return {"params": pd.Series(), "summary": "Insufficient data"} y = valid["spread"] X_clean = valid.drop(columns=["spread"]) model = sm.OLS(y, X_clean).fit(cov_type="HAC", cov_kwds={"maxlags": 5}) results_df = pd.DataFrame({ "variable": model.params.index, "coefficient": model.params.values, "std_error": model.bse.values, "t_stat": model.tvalues.values, "p_value": model.pvalues.values, }) return { "params": model.params, "results_df": results_df, "r_squared": model.rsquared, "adj_r_squared": model.rsquared_adj, "summary": str(model.summary()), } def market_quality_from_orderbook(order_book: dict) -> dict: """Extract market quality metrics from a single order book snapshot.""" bids = order_book.get("bids", []) asks = order_book.get("asks", []) if not bids or not asks: return { "quoted_spread": np.nan, "midpoint": np.nan, "depth_at_best": np.nan, "total_depth": np.nan, "depth_imbalance": np.nan, } best_bid = float(bids[0].get("price", 0)) best_ask = float(asks[0].get("price", 0)) midpoint = (best_bid + best_ask) / 2 bid_depth = sum(float(b.get("size", 0)) for b in bids) ask_depth = sum(float(a.get("size", 0)) for a in asks) total_depth = bid_depth + ask_depth return { "quoted_spread": best_ask - best_bid, "midpoint": midpoint, "depth_at_best": float(bids[0].get("size", 0)) + float(asks[0].get("size", 0)), "total_depth": total_depth, "depth_imbalance": (ask_depth - bid_depth) / total_depth if total_depth > 0 else np.nan, }