SPB Git forge

spb/wp8_uqo

Public
3commits 1branches 1releases
4.2 MBsize
maindefault branch
2 h agolast push
Python 56.2% TeX 43.7%
4.0 KB · 127 lines python
Raw Blame History
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