SPB Git

spb/wp26_uqo

Public
6.2 KB · 134 lines python
1# Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca2"""Illustrative simulation of the buyer–seller–lender model (welfare.tex).3Outputs: results/theory/welfare_sim.csv, figures/welfare_failure_vs_s.pdf, figures/welfare_systemic_vs_rho.pdf.4Only LTV = 80 % and the haircut rule are transparent calibrations; all other parameters are ILLUSTRATIVE."""5from __future__ import annotations67import csv8import sys9import time10from pathlib import Path1112import numpy as np1314ROOT = Path(__file__).resolve().parents[1]15sys.path.insert(0, str(ROOT / "src"))16from agentic_valuation.theory import welfare as wf  # noqa: E4021718SEED = 2026102719OUT = ROOT / "results" / "theory"20FIG = ROOT / "figures"21OUT.mkdir(parents=True, exist_ok=True)22FIG.mkdir(parents=True, exist_ok=True)23C1, C2, C3, C4 = "#2a78d6", "#eb6834", "#1baf7a", "#eda100"24INK, MUTED, GRID = "#1a1a19", "#6b6a66", "#e6e5e1"252627def style(ax):28    for side in ("top", "right"):29        ax.spines[side].set_visible(False)30    for side in ("left", "bottom"):31        ax.spines[side].set_color(GRID)32    ax.tick_params(colors=MUTED, labelsize=8)33    ax.yaxis.grid(True, color=GRID, lw=0.6)34    ax.set_axisbelow(True)353637def main() -> None:38    t0 = time.time()39    rng = np.random.default_rng(SEED)40    rows: list[dict] = []41    n = 200_00042    shocks = wf.draw_market_shocks(n, rng)43    s_grid = np.linspace(0.0, 0.25, 26)44    base = wf.WelfareParams()45    variants = {"h1=0,b=0": wf.with_params(base, h1=0.0), "h1=0.5,b=0": wf.with_params(base, h1=0.5)}46    biases = {"b=-0.03": -0.03, "b=0": 0.0, "b=+0.03": 0.03}47    market = {}48    for vname, p in variants.items():49        for bname, b in biases.items():50            for s in s_grid:51                m = wf.simulate_market(b, float(s), p, n, rng, shocks=shocks)52                el = wf.expected_loss(b, float(s), p)53                rows.append({"experiment": "transaction", "variant": vname, "bias": b, "s": round(float(s), 4),54                             "rho": "", **m, "expected_loss": el})55                market[(vname, bname, round(float(s), 4))] = (m, el)5657    # Systemic block: N lenders, common-model correlation rho58    N, n_sys = 50, 20_00059    sys_shocks = {"C": rng.standard_normal(n_sys), "E": rng.standard_normal((n_sys, N))}60    rho_grid = np.linspace(0.0, 1.0, 11)61    systemic = {}62    for s in (0.05, 0.10, 0.15):63        for rho in rho_grid:64            r = wf.systemic_loss(float(rho), 0.0, s, base, N, n_sys, rng, shocks=sys_shocks)65            rows.append({"experiment": "systemic", "variant": f"s={s}", "bias": 0.0, "s": s, "rho": round(float(rho), 3),66                         "sys_mean": r["mean"], "sys_sd": r["sd"], "sys_es95": r["es95"], "cov_pair": r["cov_pair"]})67            systemic[(s, round(float(rho), 3))] = r68    # "Adopting the best common AVM": accuracy improves (s falls) but rho rises to 169    for (s, rho, label) in ((0.15, 0.2, "many_avms"), (0.10, 1.0, "single_best_avm")):70        r = wf.systemic_loss(rho, 0.0, s, base, N, n_sys, rng, shocks=sys_shocks)71        rows.append({"experiment": "concentration", "variant": label, "bias": 0.0, "s": s, "rho": rho,72                     "sys_mean": r["mean"], "sys_sd": r["sd"], "sys_es95": r["es95"], "cov_pair": r["cov_pair"]})7374    fields = ["experiment", "variant", "bias", "s", "rho", "beneficial_rate", "failure_rate", "failure_rate_exact",75              "completion_rate", "mean_price", "seller_surplus", "buyer_surplus", "total_surplus", "lost_surplus",76              "expected_loss", "sys_mean", "sys_sd", "sys_es95", "cov_pair"]77    with open(OUT / "welfare_sim.csv", "w", newline="") as f:78        w = csv.DictWriter(f, fieldnames=fields)79        w.writeheader()80        for row in rows:81            w.writerow({k: row.get(k, "") for k in fields})8283    import matplotlib8485    matplotlib.use("Agg")86    import matplotlib.pyplot as plt8788    plt.rcParams.update({"font.family": "serif", "font.size": 9, "axes.labelcolor": INK, "text.color": INK})8990    # Figure 1: failure rate of beneficial transactions vs s (two haircut rules x three biases)91    fig, axes = plt.subplots(1, 2, figsize=(7.2, 3.2), sharey=True)92    bcols = {"b=-0.03": C2, "b=0": C1, "b=+0.03": C3}93    for ax, (vname, _) in zip(axes, variants.items()):94        for bname, b in biases.items():95            ys = [market[(vname, bname, round(float(s), 4))][0]["failure_rate_exact"] for s in s_grid]96            ax.plot(s_grid, ys, color=bcols[bname], lw=2, label=bname.replace("b=", "bias $b=$"))97        ax.set_title("no uncertainty haircut ($h_1=0$)" if vname.startswith("h1=0,") else98                     "haircut rising with $s$ ($h_1=0.5$)", fontsize=9)99        ax.set_xlabel("valuation error standard deviation $s$")100        style(ax)101    axes[0].set_ylabel("failure rate, beneficial matches")102    axes[0].legend(frameon=False, fontsize=8, loc="upper left")103    fig.tight_layout()104    fig.savefig(FIG / "welfare_failure_vs_s.pdf")105    plt.close(fig)106107    # Figure 2: systemic loss vs rho108    fig, axes = plt.subplots(1, 2, figsize=(7.2, 3.2))109    scols = {0.05: "#9ec5f4", 0.10: "#256abf", 0.15: "#104281"}110    for s in (0.05, 0.10, 0.15):111        axes[0].plot(rho_grid, [systemic[(s, round(float(r), 3))]["es95"] for r in rho_grid], color=scols[s], lw=2,112                     label=f"ES$_{{95}}$, $s={s:.2f}$")113        axes[0].plot(rho_grid, [systemic[(s, round(float(r), 3))]["mean"] for r in rho_grid], color=scols[s], lw=1,114                     ls="--")115        axes[1].plot(rho_grid, [systemic[(s, round(float(r), 3))]["sd"] for r in rho_grid], color=scols[s], lw=2,116                     label=f"$s={s:.2f}$")117    axes[0].set_title("system loss: mean (dashed) and 95 % expected shortfall", fontsize=9)118    axes[1].set_title("standard deviation of the system loss", fontsize=9)119    for ax in axes:120        ax.set_xlabel(r"common-model correlation $\rho$ across lenders' AVMs")121        ax.set_ylim(0, None)122        style(ax)123    axes[0].set_ylabel(f"loss per $N={N}$ loans (share of $V$)")124    axes[0].legend(frameon=False, fontsize=7, loc="upper left")125    axes[1].legend(frameon=False, fontsize=8, loc="upper left")126    fig.tight_layout()127    fig.savefig(FIG / "welfare_systemic_vs_rho.pdf")128    plt.close(fig)129    print(f"wrote {OUT / 'welfare_sim.csv'} ({len(rows)} rows) and 2 figures in {time.time() - t0:.1f} s")130131132if __name__ == "__main__":133    main()134
Files · main
Menu

Theme