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 = 1row; 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, sdbaseline_sd_change_bp≈ 4.24 bp).effect_estimate,effect_ci_low,effect_ci_highandmodel_metricare 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-runout/analysis.py. It would then compute the estimand without any code changes.