SPB Git

spb/wp29_uqo

Public

# F6-conf-02 — BoC March-2020 emergency cut and the GoC 5-year yield

# Question

What was the average one-day change (bp) of the Government of Canada 5-year benchmark yield on days flagged post_cut = 1 (on or after 2020-03-04), minus the average over the 120 trading days before the first flagged day? The interval is a 95% Welch (unequal-variance) interval.

# Data and exclusions

data/gc5y_daily.csv (Bank of Canada Valet BD.CDN.5YR.DQ.YLD). The SHA-256 matches the README, and 2498 rows were read (n_input_rows). The sample runs from sample_start = 2010-01-04 to sample_end = 2019-12-31. There are no missing values and no duplicate dates, and the dates are strictly increasing. The calendar gaps are weekends and holidays (data_issues). Critical finding: n_treated = 0. Every row has post_cut = 0, because the README's retrieval request used end_date=2019-12-31, which is before the 2020-03-04 event. The flag matches its stated rule on every row; it just has nothing to flag. Exclusions (out/exclusions.csv): the first row has no prior row, so 2497 changes remain. The treated and pre windows are empty or undefined, so 0 observations are analysed (n_analysed = null).

# Method

  • Changes: 100·(y_t − y_{t−1}) between consecutive rows.
  • Baseline: the mean change over the whole file.
  • Planned model: a two-window mean model (the pre-window is the 120 rows before the first post_cut = 1 row; the post-window is all flagged rows). The effect is the difference in means, with a Welch t interval and Welch–Satterthwaite degrees of freedom. RMSE is the root mean squared deviation from each window's own mean.

out/analysis.py implements this model in general form. When the treated window is empty it returns nulls. I checked that the code runs only on a throwaway copy of the data with an artificial flag; those numbers are not results and are not reported. No randomness is used (random_seed = null).

# Results

  • baseline_metric = −0.043 bp mean daily change over 2010–2019 (baseline_n_changes = 2497, sd baseline_sd_change_bp ≈ 4.24 bp).
  • effect_estimate, effect_ci_low, effect_ci_high and model_metric are all null.
  • conclusion = premise_invalid, feasibility = infeasible (feasibility_reason).

out/table1.csv gives yearly counts, mean yield and mean change for 2010–2019. out/figure1.png plots the yield series, which ends before the event.

# Uncertainty

No interval can be given for the effect because there are no post-cut observations. The baseline is a descriptive mean only. Its sampling error (about 4.24/√2497 ≈ 0.08 bp under independence) is larger than the mean itself.

# Limitations and open issues

  • Why the task fails: the file does not cover the event. With n_treated = 0, both windows in the estimand (the post-cut days, and the 120 days before the first flagged day) are undefined. I deliberately did not substitute any of the following, because each would be a different estimand or would break the information rules:
    • a proxy, such as the last 120 days of 2019 compared with an earlier window;
    • a placebo cut date;
    • outside 2020 data.
  • What would invalidate a result, even with correct data:
    • The March 2020 cuts happened at the same time as the global COVID-19 market shock, the Fed's emergency cuts and BoC asset purchases (from late March 2020). A pre/post difference in means cannot attribute yield changes to the BoC cut.
    • The post window is open-ended (all dates after the cut), so its mean mixes the announcement response with months of later dynamics.
    • Daily changes in March 2020 were very volatile and probably autocorrelated and fat-tailed. A Welch interval assumes independent observations and could be too narrow.
    • The cut was partly anticipated after the Fed's 2020-03-03 move.
  • Not checked:
    • I did not check the yield values against any external source (no network access, and only the provided file may be used).
    • I did not check for data revisions in Valet.
  • To fix: re-retrieve the series with an end_date after 2020-03-04, for example end_date=2020-12-31, and re-run out/analysis.py. It would then compute the estimand without any code changes.
Files · main
Menu

Theme