#!/usr/bin/env python3 """Ch. 31, figure « taux-bdc » (DONNÉES RÉELLES, Banque du Canada, API Valet, téléchargées le 2026-10-31) : taux effectifs des fonds avancés par les banques à charte pour les prêts hypothécaires résidentiels à taux fixe de 5 ans et plus, assurés (V122667780) et non assurés (V122667786), mensuels, janvier 2013 à août 2026 ; taux hypothécaire conventionnel 5 ans affiché (V80691335, moyennes mensuelles) et taux cible du financement à un jour (V39079). Sortie : ch31-fig-taux-bdc.pdf.""" from __future__ import annotations import sys, pathlib, io sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1])) import pandas as pd, matplotlib.pyplot as plt from traite.style import setup, palette, savefig, LARGEUR, ROOT RAW = ROOT / "data/raw/bdc_valet" def lire_serie(nom: str) -> pd.Series: txt = (RAW / f"{nom}.csv").read_text(encoding="utf-8-sig") obs = txt[txt.index('"OBSERVATIONS"'):].split("\n", 1)[1] df = pd.read_csv(io.StringIO(obs)); df.columns = ["date"] + list(df.columns[1:]) df["date"] = pd.to_datetime(df["date"]) s = pd.to_numeric(df.iloc[:, 1], errors="coerce"); s.index = df["date"] return s.dropna() def lire_groupe() -> pd.DataFrame: txt = (RAW / "A4_RATES_MORTGAGES.csv").read_text(encoding="utf-8-sig") obs = txt[txt.index('"OBSERVATIONS"'):].split("\n", 1)[1] df = pd.read_csv(io.StringIO(obs)); df = df.rename(columns={df.columns[0]: "date"}) df["date"] = pd.to_datetime(df["date"]); df = df.set_index("date").apply(pd.to_numeric, errors="coerce") return df def main(): setup() g = lire_groupe() assure = g["V122667780"].dropna(); non_assure = g["V122667786"].dropna() affiche = lire_serie("V80691335")["2013-01-01":].resample("MS").mean() directeur = lire_serie("V39079")["2013-01-01":].resample("MS").last() fig, ax = plt.subplots(figsize=(LARGEUR, 3.3)) ax.plot(affiche.index, affiche.values, color=palette.cat[2], lw=1.1, label="Taux conventionnel 5 ans affiché (V80691335)") ax.plot(non_assure.index, non_assure.values, color=palette.cat[1], lw=1.6, label="Fonds avancés, non assurés, fixe 5 ans et plus (V122667786)") ax.plot(assure.index, assure.values, color=palette.cat[0], lw=1.6, label="Fonds avancés, assurés, fixe 5 ans et plus (V122667780)") ax.step(directeur.index, directeur.values, where="post", color=palette.ink2, lw=1.0, label="Taux cible du financement à un jour (V39079)") for s, lab, dy in ((assure, "4,12 %", -0.55), (non_assure, "4,58 %", 0.35), (affiche, "6,19 %", 0.3)): ax.annotate(lab, xy=(s.index[-1], s.iloc[-1]), xytext=(s.index[-1] + pd.DateOffset(months=4), s.iloc[-1] + dy), fontsize=7.5, color=palette.ink) ax.annotate("2,50 %", xy=(directeur.index[-1], directeur.iloc[-1]), xytext=(directeur.index[-1] + pd.DateOffset(months=4), directeur.iloc[-1] - 0.1), fontsize=7.5, color=palette.ink) ax.set_ylabel("Taux (%)"); ax.set_ylim(0, 8.5); ax.set_xlim(pd.Timestamp("2013-01-01"), pd.Timestamp("2027-09-01")) ax.legend(loc="upper left", fontsize=7.2) savefig(fig, "ch31/ch31-fig-taux-bdc.pdf") if __name__ == "__main__": main()