Python 56.2%
TeX 43.7%
1"""Liquidity and market quality analysis."""23from __future__ import annotations45import json67import numpy as np8import pandas as pd91011def analyze_volume_patterns(12 volume: pd.Series,13 event_dates: list | None = None,14) -> pd.DataFrame:15 """Analyze volume patterns: trends, day-of-week effects, event-day spikes."""16 stats_data = {17 "mean_volume": volume.mean(),18 "median_volume": volume.median(),19 "std_volume": volume.std(),20 "max_volume": volume.max(),21 "total_volume": volume.sum(),22 }2324 if hasattr(volume.index, "dayofweek"):25 dow = volume.groupby(volume.index.dayofweek).mean()26 for d, label in enumerate(["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]):27 if d in dow.index:28 stats_data[f"avg_volume_{label}"] = dow[d]2930 if event_dates:31 event_ts = pd.to_datetime(event_dates)32 event_vol = volume[volume.index.isin(event_ts)]33 non_event_vol = volume[~volume.index.isin(event_ts)]34 if len(event_vol) > 0 and len(non_event_vol) > 0:35 stats_data["avg_event_day_volume"] = event_vol.mean()36 stats_data["avg_non_event_volume"] = non_event_vol.mean()37 stats_data["event_volume_ratio"] = event_vol.mean() / non_event_vol.mean()3839 return pd.DataFrame([stats_data])404142def compute_concentration_metrics(market_volumes: pd.DataFrame) -> pd.DataFrame:43 """Compute HHI and share metrics across markets."""44 total = market_volumes.sum(axis=1)45 shares = market_volumes.div(total, axis=0)4647 hhi = (shares ** 2).sum(axis=1)48 top1 = shares.max(axis=1)49 top3 = shares.apply(lambda row: row.nlargest(3).sum(), axis=1)5051 return pd.DataFrame({52 "hhi": hhi,53 "top1_share": top1,54 "top3_share": top3,55 })565758def estimate_liquidity_regressions(59 spreads: pd.Series,60 volume: pd.Series,61 volatility: pd.Series,62 event_dummy: pd.Series | None = None,63) -> dict:64 """Regress spread on volume, volatility, and event dummies."""65 import statsmodels.api as sm6667 X = pd.DataFrame({"volume": volume, "volatility": volatility})68 if event_dummy is not None:69 X["event"] = event_dummy70 X = sm.add_constant(X)7172 valid = pd.concat([spreads.rename("spread"), X], axis=1).dropna()73 if len(valid) < 10:74 return {"params": pd.Series(), "summary": "Insufficient data"}7576 y = valid["spread"]77 X_clean = valid.drop(columns=["spread"])7879 model = sm.OLS(y, X_clean).fit(cov_type="HAC", cov_kwds={"maxlags": 5})8081 results_df = pd.DataFrame({82 "variable": model.params.index,83 "coefficient": model.params.values,84 "std_error": model.bse.values,85 "t_stat": model.tvalues.values,86 "p_value": model.pvalues.values,87 })8889 return {90 "params": model.params,91 "results_df": results_df,92 "r_squared": model.rsquared,93 "adj_r_squared": model.rsquared_adj,94 "summary": str(model.summary()),95 }969798def market_quality_from_orderbook(order_book: dict) -> dict:99 """Extract market quality metrics from a single order book snapshot."""100 bids = order_book.get("bids", [])101 asks = order_book.get("asks", [])102103 if not bids or not asks:104 return {105 "quoted_spread": np.nan,106 "midpoint": np.nan,107 "depth_at_best": np.nan,108 "total_depth": np.nan,109 "depth_imbalance": np.nan,110 }111112 best_bid = float(bids[0].get("price", 0))113 best_ask = float(asks[0].get("price", 0))114 midpoint = (best_bid + best_ask) / 2115116 bid_depth = sum(float(b.get("size", 0)) for b in bids)117 ask_depth = sum(float(a.get("size", 0)) for a in asks)118 total_depth = bid_depth + ask_depth119120 return {121 "quoted_spread": best_ask - best_bid,122 "midpoint": midpoint,123 "depth_at_best": float(bids[0].get("size", 0)) + float(asks[0].get("size", 0)),124 "total_depth": total_depth,125 "depth_imbalance": (ask_depth - bid_depth) / total_depth if total_depth > 0 else np.nan,126 }127