# ----------------------------------------------------------------------------- # Immo-Ka — Agrégateur de maisons à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # mortgage/providers/bank_of_canada.py : Banque du Canada — API Valet # (officielle, publique, JSON). Séries de référence : taux hypothécaire # conventionnel affiché 1/3/5 ans, taux directeur, rendement obligataire # 5 ans. purpose="unknown" : séries de RÉFÉRENCE, exclues du comparateur # de prêteurs (la BdC ne prête pas aux particuliers). # ----------------------------------------------------------------------------- from __future__ import annotations from .base import RateProvider SERIES_URL = ("https://www.bankofcanada.ca/valet/observations/" "V80691333,V80691334,V80691335,V39079,BD.CDN.5YR.DQ.YLD/" "json?recent=10") # série -> (terme mois, nom de produit) MORTGAGE_SERIES = { "V80691333": (12, "Taux hypothécaire conventionnel affiché — 1 an"), "V80691334": (36, "Taux hypothécaire conventionnel affiché — 3 ans"), "V80691335": (60, "Taux hypothécaire conventionnel affiché — 5 ans"), } CONTEXT_SERIES = { "V39079": "Taux cible du financement à un jour", "BD.CDN.5YR.DQ.YLD": "Rendement obligataire 5 ans — Gouvernement du Canada", } class BankOfCanadaProvider(RateProvider): provider_id = "bank_of_canada" institution = "Banque du Canada" source_url = "https://www.bankofcanada.ca/rates/interest-rates/" request_delay = 0.5 def fetch(self) -> list[dict]: return self.parse(self.get(SERIES_URL).text) def parse(self, payload: str) -> list[dict]: import json data = json.loads(payload) observations = data.get("observations") or [] # Séries à fréquences mélangées : garder la DERNIÈRE valeur par série. latest: dict[str, tuple[str, float]] = {} for obs in observations: d = obs.get("d", "") for sid, cell in obs.items(): if sid == "d" or not isinstance(cell, dict): continue v = cell.get("v") if v in (None, ""): continue try: latest[sid] = (d, float(v)) except ValueError: continue out: list[dict] = [] for sid, (term, name) in MORTGAGE_SERIES.items(): if sid not in latest: continue d, v = latest[sid] out.append(self.make_product( rate=v, rate_type="fixed", term_months=term, kind="posted", product_name=name, purpose="unknown", conditions=f"Série Valet {sid} — observation du {d} " "(moyenne hebdomadaire des taux affichés)", raw={"series": sid, "date": d, "value": v})) for sid, name in CONTEXT_SERIES.items(): if sid not in latest: continue d, v = latest[sid] out.append(self.make_product( rate=v, rate_type="other", term_months=12, kind="posted", product_name=name, purpose="unknown", conditions=f"Série Valet {sid} — observation du {d}", raw={"series": sid, "date": d, "value": v})) return out