SPB Git forge

spb/wp8_uqo

Public
3commits 1branches 1releases
4.2 MBsize
maindefault branch
5 h agolast push
Python 56.2% TeX 43.7%
12.9 KB · 383 lines python
Raw Blame History
1"""LaTeX table generators for the Polymarket Arrow-Debreu paper."""23from __future__ import annotations45from pathlib import Path67import numpy as np8import pandas as pd910from src.config import TABLES111213def _save_table(content: str, name: str) -> str:14    TABLES.mkdir(parents=True, exist_ok=True)15    path = TABLES / f"{name}.tex"16    path.write_text(content)17    return content181920def _esc(s: str) -> str:21    """Escape LaTeX special characters in text."""22    return str(s).replace("$", r"\$").replace("_", r"\_").replace("&", r"\&").replace("%", r"\%").replace("#", r"\#")232425def _fmt(x: float, decimals: int = 3) -> str:26    if pd.isna(x):27        return ""28    return f"{x:.{decimals}f}"293031def _stars(p: float) -> str:32    if pd.isna(p):33        return ""34    if p < 0.01:35        return "^{***}"36    if p < 0.05:37        return "^{**}"38    if p < 0.10:39        return "^{*}"40    return ""414243def table_descriptive_stats(metadata: pd.DataFrame) -> str:44    """Table 1: Descriptive statistics of Polymarket contracts."""45    rows = []46    for _, row in metadata.iterrows():47        name = _esc(str(row.get("name", row.get("question", "")))[:40])48        vol = row.get("volume", 0)49        liq = row.get("liquidity", 0)50        n_obs = int(row.get("n_obs", 0))51        mean_p = row.get("mean_price", np.nan)52        std_p = row.get("std_price", np.nan)53        rows.append(54            f"    {name} & {vol:,.0f} & {liq:,.0f} & {n_obs} & {_fmt(mean_p)} & {_fmt(std_p)} \\\\"55        )5657    body = "\n".join(rows)58    tex = rf"""\begin{{table}}[htbp]59\centering60\begin{{threeparttable}}61\caption{{Descriptive Statistics of Polymarket Contracts}}62\label{{tab:descriptive_stats}}63\begin{{tabular}}{{l S S c S S}}64\toprule65Market & {{Volume (\$)}} & {{Liquidity (\$)}} & Obs & {{Mean Price}} & {{Std Dev}} \\66\midrule67{body}68\bottomrule69\end{{tabular}}70\begin{{tablenotes}}71\small72\item \textit{{Notes:}} This table reports summary statistics for the Polymarket contracts analyzed.73Volume and liquidity are reported in US dollars. Mean price represents the average implied probability.74\end{{tablenotes}}75\end{{threeparttable}}76\end{{table}}"""77    return _save_table(tex, "table1_descriptive_stats")787980def table_summary_stats(prices: pd.DataFrame) -> str:81    """Table 2: Summary statistics of price series."""82    stats = []83    for col in prices.columns:84        s = prices[col].dropna()85        from scipy.stats import jarque_bera86        try:87            jb_stat, jb_p = jarque_bera(s)88        except Exception:89            jb_stat, jb_p = np.nan, np.nan90        stats.append({91            "Market": _esc(col[:30]),92            "Mean": s.mean(),93            "Std": s.std(),94            "Min": s.min(),95            "Max": s.max(),96            "Skew": s.skew(),97            "Kurt": s.kurtosis(),98            "JB": jb_stat,99        })100101    rows = []102    for st in stats:103        rows.append(104            f"    {st['Market']} & {_fmt(st['Mean'])} & {_fmt(st['Std'])} & "105            f"{_fmt(st['Min'])} & {_fmt(st['Max'])} & {_fmt(st['Skew'],2)} & "106            f"{_fmt(st['Kurt'],2)} & {_fmt(st['JB'],1)} \\\\"107        )108109    body = "\n".join(rows)110    tex = rf"""\begin{{table}}[htbp]111\centering112\begin{{threeparttable}}113\caption{{Summary Statistics of Prediction Market Prices}}114\label{{tab:summary_stats}}115\begin{{tabular}}{{l S S S S S S S}}116\toprule117Market & {{Mean}} & {{Std}} & {{Min}} & {{Max}} & {{Skew}} & {{Kurt}} & {{JB}} \\118\midrule119{body}120\bottomrule121\end{{tabular}}122\begin{{tablenotes}}123\small124\item \textit{{Notes:}} JB is the Jarque--Bera test statistic for normality.125$^{{***}}$, $^{{**}}$, and $^{{*}}$ denote significance at the 1\%, 5\%, and 10\% levels.126\end{{tablenotes}}127\end{{threeparttable}}128\end{{table}}"""129    return _save_table(tex, "table2_summary_stats")130131132def table_completeness_test(test_result) -> str:133    """Table 3: Arrow-Debreu completeness test results."""134    tex = rf"""\begin{{table}}[htbp]135\centering136\begin{{threeparttable}}137\caption{{Arrow--Debreu Market Completeness Tests}}138\label{{tab:completeness}}139\begin{{tabular}}{{l S}}140\toprule141Statistic & {{Value}} \\142\midrule143    Mean state price sum & {_fmt(test_result.mean_sum, 4)} \\144    Std of state price sum & {_fmt(test_result.std_sum, 4)} \\145    Mean deviation from unity & {_fmt(test_result.mean_deviation, 4)} \\146    Max absolute deviation & {_fmt(test_result.max_deviation, 4)} \\147    All state prices positive & {{{'Yes' if test_result.all_positive else 'No'}}} \\148    Implied discount factor & {_fmt(test_result.discount_factor, 4)} \\149    Number of observations & {len(test_result.daily_sums)} \\150\bottomrule151\end{{tabular}}152\begin{{tablenotes}}153\small154\item \textit{{Notes:}} This table reports results from testing Arrow--Debreu market completeness155conditions using the Fed rate decision markets on Polymarket. Under complete markets,156the sum of state prices equals $1/(1+r_f)$. Mean deviation measures the average157departure from this theoretical prediction.158\end{{tablenotes}}159\end{{threeparttable}}160\end{{table}}"""161    return _save_table(tex, "table3_completeness")162163164def table_event_study(results: dict[str, object]) -> str:165    """Table 4: Event study cumulative abnormal returns."""166    rows = []167    for event_type, result in results.items():168        if hasattr(result, "test_stats") and not result.test_stats.empty:169            for _, row in result.test_stats.iterrows():170                window = row.get("window", "")171                market = _esc(str(row.get("market", "")))172                car = row.get("mean_CAR", np.nan)173                t = row.get("t_stat", np.nan)174                p = row.get("p_value", np.nan)175                stars = _stars(p)176                rows.append(177                    f"    {_esc(event_type)}: {market} & {window} & ${_fmt(car)}{stars}$ & "178                    f"{_fmt(t, 2)} & {_fmt(p)} & {result.n_events} \\\\"179                )180181    body = "\n".join(rows) if rows else "    \\multicolumn{6}{c}{No significant results} \\\\"182    tex = rf"""\begin{{table}}[htbp]183\centering184\begin{{threeparttable}}185\caption{{Event Study: Cumulative Abnormal Returns Around Macroeconomic Events}}186\label{{tab:event_study}}187\begin{{tabular}}{{l l c c c c}}188\toprule189Event Type & Window & {{CAR}} & {{$t$-stat}} & {{$p$-value}} & $N$ \\190\midrule191{body}192\bottomrule193\end{{tabular}}194\begin{{tablenotes}}195\small196\item \textit{{Notes:}} This table reports cumulative abnormal returns (CARs) for prediction market197prices around macroeconomic events. Abnormal returns are computed relative to a constant mean198model estimated over a 60-day estimation window. $t$-statistics are computed using the199cross-sectional standard deviation of CARs. $^{{***}}$, $^{{**}}$, $^{{*}}$ denote200significance at 1\%, 5\%, 10\%.201\end{{tablenotes}}202\end{{threeparttable}}203\end{{table}}"""204    return _save_table(tex, "table4_event_study")205206207def table_granger_causality(f_stats: pd.DataFrame, p_values: pd.DataFrame) -> str:208    """Table 5: Granger causality test results."""209    rows = []210    for caused in f_stats.index:211        for causing in f_stats.columns:212            if caused != causing:213                f_val = f_stats.loc[caused, causing]214                p_val = p_values.loc[caused, causing]215                stars = _stars(p_val)216                rows.append(217                    f"    {_esc(causing)} $\\to$ {_esc(caused)} & ${_fmt(f_val, 2)}{stars}$ & {_fmt(p_val)} \\\\"218                )219220    body = "\n".join(rows)221    tex = rf"""\begin{{table}}[htbp]222\centering223\begin{{threeparttable}}224\caption{{Granger Causality Tests}}225\label{{tab:granger}}226\begin{{tabular}}{{l c c}}227\toprule228Direction & {{$F$-stat}} & {{$p$-value}} \\229\midrule230{body}231\bottomrule232\end{{tabular}}233\begin{{tablenotes}}234\small235\item \textit{{Notes:}} This table reports $F$-statistics and $p$-values for pairwise Granger236causality tests. The null hypothesis is that the causing variable does not Granger-cause237the caused variable. Lag length selected by AIC. $^{{***}}$, $^{{**}}$, $^{{*}}$ denote238significance at 1\%, 5\%, 10\%.239\end{{tablenotes}}240\end{{threeparttable}}241\end{{table}}"""242    return _save_table(tex, "table5_granger")243244245def table_var_results(var_results: dict) -> str:246    """Table 6: VAR estimation results."""247    tex = rf"""\begin{{table}}[htbp]248\centering249\begin{{threeparttable}}250\caption{{Vector Autoregression Estimates}}251\label{{tab:var}}252\begin{{tabular}}{{l {'S ' * min(len(var_results.get('coefficients', {}).keys()), 4)}}}253\toprule254"""255    coefs = var_results.get("coefficients", {})256    if coefs:257        cols = list(coefs.keys())[:4]258        tex += " & ".join(["Variable"] + [f"{{{_esc(c)}}}" for c in cols]) + " \\\\\n\\midrule\n"259        first_col = coefs[cols[0]]260        if isinstance(first_col, pd.Series):261            for var_name in first_col.index[:15]:262                row_vals = [f"{_fmt(coefs[c].get(var_name, np.nan), 3)}" for c in cols]263                tex += f"    {_esc(str(var_name))} & " + " & ".join(row_vals) + " \\\\\n"264    tex += rf"""\bottomrule265\end{{tabular}}266\begin{{tablenotes}}267\small268\item \textit{{Notes:}} VAR model estimates. Optimal lag length selected by AIC.269Standard errors in parentheses. $^{{***}}$, $^{{**}}$, $^{{*}}$ denote significance270at 1\%, 5\%, 10\%.271\end{{tablenotes}}272\end{{threeparttable}}273\end{{table}}"""274    return _save_table(tex, "table6_var")275276277def table_garch(garch_results: dict[str, object]) -> str:278    """Table 7: GARCH estimation results."""279    rows = []280    for name, result in garch_results.items():281        if hasattr(result, "params"):282            for param_name, value in result.params.items():283                se = result.std_errors.get(param_name, np.nan) if hasattr(result, "std_errors") else np.nan284                rows.append(f"    {_esc(name)}: {_esc(str(param_name))} & {_fmt(value, 4)} & ({_fmt(se, 4)}) \\\\")285286    body = "\n".join(rows) if rows else "    \\multicolumn{3}{c}{No results} \\\\"287    tex = rf"""\begin{{table}}[htbp]288\centering289\begin{{threeparttable}}290\caption{{GARCH Model Estimates}}291\label{{tab:garch}}292\begin{{tabular}}{{l S l}}293\toprule294Parameter & {{Estimate}} & (Std Err) \\295\midrule296{body}297\bottomrule298\end{{tabular}}299\begin{{tablenotes}}300\small301\item \textit{{Notes:}} GARCH(1,1) model estimates for prediction market return volatility.302$^{{***}}$, $^{{**}}$, $^{{*}}$ denote significance at 1\%, 5\%, 10\%.303\end{{tablenotes}}304\end{{threeparttable}}305\end{{table}}"""306    return _save_table(tex, "table7_garch")307308309def table_har_rv(har_result) -> str:310    """Table 8: HAR-RV estimation results."""311    rows = []312    if hasattr(har_result, "coefficients"):313        for name, val in har_result.coefficients.items():314            se = har_result.std_errors.get(name, np.nan) if hasattr(har_result, "std_errors") else np.nan315            t = har_result.t_stats.get(name, np.nan) if hasattr(har_result, "t_stats") else np.nan316            rows.append(f"    {_esc(str(name))} & {_fmt(val, 4)} & ({_fmt(se, 4)}) & {_fmt(t, 2)} \\\\")317318    body = "\n".join(rows) if rows else "    \\multicolumn{4}{c}{No results} \\\\"319    r2 = _fmt(har_result.r_squared, 3) if hasattr(har_result, "r_squared") else ""320    adj_r2 = _fmt(har_result.adj_r_squared, 3) if hasattr(har_result, "adj_r_squared") else ""321322    tex = rf"""\begin{{table}}[htbp]323\centering324\begin{{threeparttable}}325\caption{{HAR-RV Model Estimates}}326\label{{tab:har_rv}}327\begin{{tabular}}{{l S l S}}328\toprule329Variable & {{Coefficient}} & (Std Err) & {{$t$-stat}} \\330\midrule331{body}332\midrule333    $R^2$ & \multicolumn{{3}}{{c}}{{{r2}}} \\334    Adj.~$R^2$ & \multicolumn{{3}}{{c}}{{{adj_r2}}} \\335\bottomrule336\end{{tabular}}337\begin{{tablenotes}}338\small339\item \textit{{Notes:}} HAR-RV model following \citet{{corsi2009simple}}.340RV$_{{t+1}}^{{(d)}} = c + \beta_d RV_t^{{(d)}} + \beta_w RV_t^{{(w)}} + \beta_m RV_t^{{(m)}} + \varepsilon_{{t+1}}$.341Newey--West standard errors with 5 lags.342\end{{tablenotes}}343\end{{threeparttable}}344\end{{table}}"""345    return _save_table(tex, "table8_har_rv")346347348def table_liquidity(liquidity_stats: pd.DataFrame) -> str:349    """Table 9: Liquidity regression results."""350    rows = []351    for _, row in liquidity_stats.iterrows():352        var_name = _esc(str(row.get("variable", "")))353        coef = row.get("coefficient", np.nan)354        se = row.get("std_error", np.nan)355        t = row.get("t_stat", np.nan)356        p = row.get("p_value", np.nan)357        stars = _stars(p)358        rows.append(359            f"    {var_name} & ${_fmt(coef, 4)}{stars}$ & ({_fmt(se, 4)}) & {_fmt(t, 2)} \\\\"360        )361362    body = "\n".join(rows) if rows else "    \\multicolumn{4}{c}{No results} \\\\"363    tex = rf"""\begin{{table}}[htbp]364\centering365\begin{{threeparttable}}366\caption{{Liquidity Determinants}}367\label{{tab:liquidity}}368\begin{{tabular}}{{l S l S}}369\toprule370Variable & {{Coefficient}} & (Std Err) & {{$t$-stat}} \\371\midrule372{body}373\bottomrule374\end{{tabular}}375\begin{{tablenotes}}376\small377\item \textit{{Notes:}} OLS regression of bid--ask spread on volume, volatility, and event dummies.378Newey--West standard errors. $^{{***}}$, $^{{**}}$, $^{{*}}$ denote significance at 1\%, 5\%, 10\%.379\end{{tablenotes}}380\end{{threeparttable}}381\end{{table}}"""382    return _save_table(tex, "table9_liquidity")383