"""Replication entry point for F6-conf-02. Run from workspace root.""" import json, os import numpy as np, pandas as pd import matplotlib; matplotlib.use("Agg") import matplotlib.pyplot as plt from scipy import stats np.random.seed(0) os.makedirs("out", exist_ok=True) f = "data/gc5y_daily.csv" d = pd.read_csv(f) n_rows = len(d) d["date"] = pd.to_datetime(d["date"]) d = d.sort_values("date").reset_index(drop=True) d["chg_bp"] = 100 * d["yield_5y"].diff() issues = [] gaps = d["date"].diff().dt.days issues.append({"issue": f"Sample covers {d.date.min().date()} to {d.date.max().date()} only, although the treatment date is 2020-03-04; the README's retrieval URL used end_date=2019-12-31.", "handling": "No post-cut observations exist; estimand declared not estimable (premise_invalid)."}) issues.append({"issue": f"post_cut is 0 for all {n_rows} rows (n_treated = {int((d.post_cut==1).sum())}).", "handling": "Treatment indicator used as provided, not redefined; effect, CI and model RMSE set to null."}) issues.append({"issue": f"Missing values: {int(d.isna().drop(columns='chg_bp').sum().sum())}; duplicate dates: {int(d.date.duplicated().sum())}; calendar gaps up to {int(gaps.max())} days (weekends/holidays, {int((gaps>4).sum())} gaps >4 days).", "handling": "No missing/duplicate rows; changes computed between consecutive rows as specified; first row has no change and is dropped from change means."}) treated = d[d.post_cut == 1] n_treated = int(len(treated)) baseline = float(d["chg_bp"].mean()) effect = lo = hi = rmse = None; n_an = None if n_treated > 0: first = treated.index[0] pre = d.loc[max(1, first-120):first-1, "chg_bp"] post = treated["chg_bp"].dropna() effect = float(post.mean() - pre.mean()) v1, v0 = post.var(ddof=1)/len(post), pre.var(ddof=1)/len(pre) se = np.sqrt(v1+v0) df = (v1+v0)**2/(v1**2/(len(post)-1)+v0**2/(len(pre)-1)) t = stats.t.ppf(0.975, df); lo, hi = effect-t*se, effect+t*se resid = np.concatenate([post-post.mean(), pre-pre.mean()]) rmse = float(np.sqrt(np.mean(resid**2))); n_an = int(len(post)+len(pre)) if effect is None: concl, feas = "premise_invalid", "infeasible" reason = ("data/gc5y_daily.csv ends on 2019-12-31 and contains no rows with post_cut = 1; " "the post-cut mean change (and hence the effect, its CI and the two-window model RMSE) cannot be computed. " "No proxy or extrapolated estimate is substituted.") else: concl = "positive_effect" if lo > 0 else "negative_effect" if hi < 0 else "no_detectable_effect" feas, reason = "complete", None res = {"task_id": "F6-conf-02", "sample_start": str(d.date.min().date()), "sample_end": str(d.date.max().date()), "n_treated": n_treated, "baseline_metric": round(baseline, 6), "model_metric": rmse, "effect_estimate": effect, "effect_ci_low": lo, "effect_ci_high": hi, "conclusion": concl, "feasibility": feas, "feasibility_reason": reason, "data_issues": issues, "n_input_rows": {"gc5y_daily.csv": n_rows}, "n_analysed": n_an, "random_seed": 0} json.dump(res, open("out/results.json", "w"), indent=2) t1 = d.assign(year=d.date.dt.year).groupby("year").agg( n_days=("yield_5y", "size"), mean_yield=("yield_5y", "mean"), mean_change_bp=("chg_bp", "mean")).reset_index() t1.round(4).to_csv("out/table1.csv", index=False) fig, ax = plt.subplots(figsize=(9, 4)) ax.plot(d.date, d.yield_5y, lw=0.8) ax.axvline(pd.Timestamp("2020-03-04"), color="r", ls="--", label="2020-03-04 cut (outside sample)") ax.set_xlim(d.date.min(), pd.Timestamp("2020-06-30")) ax.set_ylabel("GoC 5-year yield (%)"); ax.set_title("GoC 5-year benchmark yield, file sample 2010-2019") ax.legend(); fig.tight_layout(); fig.savefig("out/figure1.png", dpi=120) print(json.dumps(res, indent=2))