SPB Git

Ch. 47 livré (50 p., dossier complet, Monte-Carlo) ; renvois normalisés

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Oct 31, 2026) parent 2810dd3 Browse files

60 changed files +5,653 −58

60 changed files
Unified Split
added analysis/python/cases/ch02_droits.py +176 −0
@@ -0,0 +1,176 @@
1+#!/usr/bin/env python3
2+"""Chapitre 2 — Droits immobiliers et bases de valeur : calculs de l'exemple intégral sur le cas 3 (12 logements, Montréal,
3+DONNÉES SYNTHÉTIQUES). Valeur du droit du locateur (baux en place) vs valeur de la pleine propriété libre de baux (loyers de
4+marché) ; valeur des baux pour les locataires ; sensibilité à la rotation et au taux d'actualisation ; chiffres des exercices.
5+Toutes les grandeurs du cas 3 proviennent de `traite.cas3` (module partagé) ; les formules financières de `traite.finance`.
6+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch02_droits.py"""
7+from __future__ import annotations
8+import sys, copy, pathlib
9+import numpy as np, pandas as pd
10+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
11+from traite import cas3, finance as fin
12+
13+ROTATIONS = [0.0, 1 / 12, 2 / 12, 3 / 12, 6 / 12] # 0, 1, 2, 3, 6 logements reloués par an (le module arrondit ρ × 12 à l'entier)
14+TAUX = [0.0625, 0.0725, 0.0825]
15+
16+
17+# ----------------------------------------------------------------------------- écart contractuel / marché
18+
19+def etat_marche(etat: pd.DataFrame) -> pd.DataFrame:
20+ """État locatif hypothétique « libre de baux » : chaque logement est reloué au loyer de marché dès l'année 1."""
21+ e = etat.copy()
22+ e["loyer_mensuel_contrat"] = e["loyer_mensuel_marche"].astype(float)
23+ return e
24+
25+
26+def ecart_par_logement(etat: pd.DataFrame, annee_eval: int = 2026) -> pd.DataFrame:
27+ e = etat.copy()
28+ e["ecart_mensuel"] = e["loyer_mensuel_marche"] - e["loyer_mensuel_contrat"]
29+ e["ecart_annuel"] = 12 * e["ecart_mensuel"]
30+ e["ratio_contrat_marche"] = e["loyer_mensuel_contrat"] / e["loyer_mensuel_marche"]
31+ e["anciennete"] = annee_eval - e["locataire_depuis"]
32+ return e[["logement", "pieces", "locataire_depuis", "anciennete", "loyer_mensuel_contrat", "loyer_mensuel_marche",
33+ "ecart_mensuel", "ecart_annuel", "ratio_contrat_marche"]]
34+
35+
36+# ----------------------------------------------------------------------------- formules fermées (équations du chapitre)
37+
38+def valeur_bail_locataire(ecart_annuel: float, n: int, r: float) -> float:
39+ """Valeur du bail pour le locataire = Σ_{t=1}^{n} (L_marché − L_contrat)/(1+r)^t, écart constant (éq. annuité)."""
40+ return ecart_annuel * fin.facteur_va_annuite(r, n)
41+
42+
43+def valeur_droit_temporaire_et_reversion(revenu_annuel: float, n: int, r: float, valeur_reversion: float) -> dict:
44+ """Décomposition d'un droit temporaire (usufruit, bail au locateur) et de la réversion (nue-propriété)."""
45+ va_droit = revenu_annuel * fin.facteur_va_annuite(r, n)
46+ va_rev = valeur_reversion * fin.facteur_va(r, n)
47+ return {"droit_temporaire": va_droit, "reversion": va_rev, "total": va_droit + va_rev}
48+
49+
50+def usufruit_nue_propriete(revenu_annee1: float, g: float, r: float, n: int) -> dict:
51+ """Usufruit de n ans sur un bien dont le revenu net croît au taux g : VA de l'annuité croissante ; nue-propriété = réversion
52+ V_n = R_{n+1}/(r−g) actualisée ; la somme redonne la pleine propriété V_0 = R_1/(r−g) (identité de Gordon)."""
53+ flux = [revenu_annee1 * (1 + g) ** (t - 1) for t in range(1, n + 1)]
54+ va_usufruit = fin.van([0.0] + flux, r)
55+ v_pleine = revenu_annee1 / (r - g)
56+ v_n = revenu_annee1 * (1 + g) ** n / (r - g)
57+ va_nue = v_n * fin.facteur_va(r, n)
58+ return {"V_pleine": v_pleine, "usufruit": va_usufruit, "nue_propriete": va_nue, "somme": va_usufruit + va_nue, "V_n": v_n,
59+ "part_usufruit": va_usufruit / v_pleine}
60+
61+
62+def va_ecart_rotation(ecart_net_annee1: float, r: float, g: float, rho: float, T: int | None = None) -> float:
63+ """Valeur actuelle de l'écart contractuel/marché sous rotation ρ : écart_t = Δ_1 [(1−ρ)(1+g)]^{t−1}.
64+ Perpétuité (T=None) : Δ_1 / (r − g + ρ(1+g)) ; sinon somme finie."""
65+ q = (1 - rho) * (1 + g) / (1 + r)
66+ if T is None:
67+ if q >= 1:
68+ raise ValueError("série divergente : (1−ρ)(1+g) ≥ 1+r")
69+ return ecart_net_annee1 / (1 + r) / (1 - q)
70+ return ecart_net_annee1 / (1 + r) * (1 - q ** T) / (1 - q) if abs(1 - q) > 1e-14 else ecart_net_annee1 * T / (1 + r)
71+
72+
73+# ----------------------------------------------------------------------------- décomposition par le module partagé
74+
75+def decomposition(d: dict, rotation: float | None = None, taux: float | None = None) -> dict:
76+ """V_pleine (libre de baux, loyers de marché) − V_locateur (baux en place) = valeur agrégée des baux pour les locataires.
77+ Les deux valeurs sont des DCF de `traite.cas3` avec les mêmes hypothèses (dépenses, CAPEX, taux terminal, coûts de vente)."""
78+ hyp = copy.deepcopy(d["hyp"])
79+ if rotation is not None:
80+ hyp["rotation_annuelle"] = rotation
81+ if taux is not None:
82+ hyp["taux_actualisation"] = taux
83+ proj_c = cas3.projection_dcf(d["etat"], d["dep"], hyp)
84+ proj_m = cas3.projection_dcf(etat_marche(d["etat"]), d["dep"], hyp)
85+ v_c = cas3.valeur_dcf(proj_c, hyp); v_m = cas3.valeur_dcf(proj_m, hyp)
86+ rne_c = cas3.rne(d["etat"], d["dep"], hyp); rne_m = cas3.rne(etat_marche(d["etat"]), d["dep"], hyp)
87+ return {"rotation": hyp["rotation_annuelle"], "taux": hyp["taux_actualisation"], "proj_contrat": proj_c, "proj_marche": proj_m,
88+ "V_locateur": v_c["valeur"], "V_pleine": v_m["valeur"], "V_locataires": v_m["valeur"] - v_c["valeur"],
89+ "part_locataires": (v_m["valeur"] - v_c["valeur"]) / v_m["valeur"],
90+ "va_flux_contrat": v_c["va_flux"], "va_flux_marche": v_m["va_flux"], "va_vt_contrat": v_c["va_terminale"], "va_vt_marche": v_m["va_terminale"],
91+ "rne1_contrat": rne_c["rne"], "rne1_marche": rne_m["rne"], "rbe_contrat": rne_c["rbe"], "rbe_marche": rne_m["rbe"]}
92+
93+
94+def capitalisation_naive(dec: dict, taux_cap: float) -> dict:
95+ """Erreur classique : capitaliser l'écart de RNE de l'année 1 à perpétuité (comme si les baux ne se renouvelaient jamais au marché)."""
96+ v_c = fin.capitalisation_directe(dec["rne1_contrat"], taux_cap); v_m = fin.capitalisation_directe(dec["rne1_marche"], taux_cap)
97+ return {"Vcap_contrat": v_c, "Vcap_marche": v_m, "ecart_capitalise": v_m - v_c}
98+
99+
100+def sensibilite(d: dict) -> pd.DataFrame:
101+ rows = []
102+ for rho in ROTATIONS:
103+ for r in TAUX:
104+ dec = decomposition(d, rotation=rho, taux=r)
105+ rows.append({"rotation": rho, "taux": r, "V_locateur": dec["V_locateur"], "V_pleine": dec["V_pleine"],
106+ "V_locataires": dec["V_locataires"], "part": dec["part_locataires"]})
107+ return pd.DataFrame(rows)
108+
109+
110+# ----------------------------------------------------------------------------- exercices
111+
112+def exercices(d: dict, base: dict) -> dict:
113+ r = d["hyp"]["taux_actualisation"]
114+ e = ecart_par_logement(d["etat"])
115+ l103 = e.loc[e["logement"] == 103].iloc[0]
116+ ex3 = {"ecart_mensuel": float(l103["ecart_mensuel"]), "ecart_annuel": float(l103["ecart_annuel"]),
117+ "va_5ans": valeur_bail_locataire(float(l103["ecart_annuel"]), 5, r), "va_10ans": valeur_bail_locataire(float(l103["ecart_annuel"]), 10, r),
118+ "va_perpetuite": float(l103["ecart_annuel"]) / r}
119+ # ex 4 : usufruit 15 ans sur un immeuble de 600 000 $ ; revenu net 30 000 $ croissant à 1 %/an ; r = 6 % (V = R_1/(r−g) = 600 000 $)
120+ ex4 = usufruit_nue_propriete(30000.0, 0.01, 0.06, 15)
121+ # ex 5 : valeur au rôle fictive du cas 1, 468 000 $ au 2024-07-01 ; indice +4,43 %/an ; date d'évaluation 2026-09-01 (26 mois)
122+ ex5 = {"role": 468000.0, "mois": 26, "indice": 1.0443 ** (26 / 12), "valeur_indexee": 468000.0 * 1.0443 ** (26 / 12)}
123+ # ex 6 : part des locataires selon la rotation (taux de base) — repris de la sensibilité
124+ # ex 7 : hypothèse extraordinaire : décontamination 150 000 $ + stigmate 5 % sur la valeur DCF du locateur
125+ v = base["V_locateur"]
126+ ex7 = {"V_hyp": v, "couts": 150000.0, "stigmate": 0.05, "V_sans_hyp": (v - 150000.0) * 0.95, "incidence": v - (v - 150000.0) * 0.95}
127+ return {"ex3": ex3, "ex4": ex4, "ex5": ex5, "ex7": ex7}
128+
129+
130+# ----------------------------------------------------------------------------- principal
131+
132+def main(verbose: bool = True) -> dict:
133+ d = cas3.charger()
134+ rev = cas3.revenus(d["etat"], d["hyp"])
135+ e = ecart_par_logement(d["etat"])
136+ base = decomposition(d)
137+ cap = capitalisation_naive(base, cas3.taux_extraits(d["comps"])["mediane"])
138+ hyp = d["hyp"]; r = hyp["taux_actualisation"]; g = hyp["croissance_loyers_marche"]; rho = hyp["rotation_annuelle"]; v = hyp["taux_inoccupation_pertes"]
139+ ecart_net1 = rev["ecart_marche"] * (1 - v)
140+ ferme = {"ecart_net_annee1": ecart_net1,
141+ "perpetuite_rotation": va_ecart_rotation(ecart_net1, r, g, rho),
142+ "perpetuite_sans_rotation": va_ecart_rotation(ecart_net1, r, g, 0.0),
143+ "horizon10_rotation": va_ecart_rotation(ecart_net1, r, g, rho, T=10),
144+ "annuite_5ans": valeur_bail_locataire(ecart_net1, 5, r), "annuite_10ans": valeur_bail_locataire(ecart_net1, 10, r)}
145+ sens = sensibilite(d)
146+ ex = exercices(d, base)
147+ res = {"d": d, "revenus": rev, "ecarts": e, "base": base, "cap": cap, "ferme": ferme, "sensibilite": sens, "exercices": ex}
148+ if verbose:
149+ pd.set_option("display.width", 200); pd.set_option("display.max_columns", 20)
150+ print("=== Cas 3 — écart contractuel / marché (données synthétiques) ===")
151+ print(f"RBP contractuel {rev['rbp_contrat']:,.0f} $ ; RBP au marché {rev['rbp_marche']:,.0f} $ ; écart {rev['ecart_marche']:,.0f} $/an "
152+ f"({rev['ecart_marche']/rev['rbp_marche']*100:.1f} % du marché) ; écart net de vacance (3,9 %) {ecart_net1:,.0f} $")
153+ print(e.to_string(index=False))
154+ print(f"ratio contrat/marché : moyenne {e['ratio_contrat_marche'].mean():.3f}, min {e['ratio_contrat_marche'].min():.3f} (log. {int(e.loc[e['ratio_contrat_marche'].idxmin(),'logement'])}), max {e['ratio_contrat_marche'].max():.3f}")
155+ print("\n=== Décomposition DCF (hypothèses du cas 3 : r = 7,25 %, rotation 15 %, g_c 3,1 %, g_m 3,5 %, R_T 5 %, c_v 2,5 %) ===")
156+ for k in ["rne1_contrat", "rne1_marche", "rbe_contrat", "rbe_marche", "va_flux_contrat", "va_flux_marche", "va_vt_contrat", "va_vt_marche", "V_locateur", "V_pleine", "V_locataires"]:
157+ print(f"{k:18s} {base[k]:>14,.2f} $")
158+ print(f"part des locataires : {base['part_locataires']*100:.2f} %")
159+ print("\nProjection contractuelle vs marché (RBP) :")
160+ pc, pm = base["proj_contrat"], base["proj_marche"]
161+ print(pd.DataFrame({"annee": pc["annee"], "rbp_contrat": pc["rbp"], "rbp_marche_scen": pm["rbp"], "ecart_rbp": pm["rbp"] - pc["rbp"],
162+ "rne_contrat": pc["rne"], "rne_marche": pm["rne"], "ecart_rne": pm["rne"] - pc["rne"]}).round(0).to_string(index=False))
163+ print("\n=== Capitalisation naïve de l'écart (taux médian extrait) ===")
164+ print({k: round(x, 0) for k, x in cap.items()})
165+ print("\n=== Formules fermées (écart net année 1, g = 3,5 %) ===")
166+ print({k: round(x, 0) for k, x in ferme.items()})
167+ print("\n=== Sensibilité rotation × taux (V_locataires et part) ===")
168+ print(sens.assign(logements_an=(sens["rotation"] * 12).round(0), V_locataires=sens["V_locataires"].round(0), part=(sens["part"] * 100).round(2), V_locateur=sens["V_locateur"].round(0), V_pleine=sens["V_pleine"].round(0)).to_string(index=False))
169+ print("\n=== Exercices ===")
170+ for k, x in ex.items():
171+ print(k, {kk: (round(vv, 4) if isinstance(vv, float) else vv) for kk, vv in x.items()})
172+ return res
173+
174+
175+if __name__ == "__main__":
176+ main()
added analysis/python/cases/ch03_cycles.py +513 −0
@@ -0,0 +1,513 @@
1+#!/usr/bin/env python3
2+"""Chapitre 3 — Offre, demande et cycles : calculs de l'exemple intégral et des tableaux.
3+
4+Trois blocs :
5+1. Modèle des quatre quadrants (DiPasquale–Wheaton) : équilibre stationnaire en forme fermée, statique comparative
6+ (taux de capitalisation, demande, coût de construction). PARAMÈTRES PÉDAGOGIQUES (marché type, données synthétiques).
7+2. Dynamique stock-flux trimestrielle avec délai de livraison et inoccupation naturelle : simulation d'un cycle
8+ après un choc de demande, racines caractéristiques du modèle linéaire (condition d'oscillation), sensibilité.
9+3. Données RÉELLES lues dans data/raw : mises en chantier au Québec (Statistique Canada 34-10-0135-01 et 34-10-0143-01),
10+ taux de la Banque du Canada (Valet : V39079, BD.CDN.5YR.DQ.YLD, V80691335, groupe A4), IPC Québec (18-10-0004-01),
11+ IPLN Québec (18-10-0205-01) ; coût d'usage du logement (Poterba) et ratio prix/loyer implicite ; mois d'inventaire
12+ (chiffres APCIQ T3 2026 cités dans sources/notes-E.md).
13+
14+Exécuter depuis la racine : python3 analysis/python/cases/ch03_cycles.py (imprime tous les chiffres cités au chapitre 3).
15+Testé par analysis/python/tests/test_ch03.py.
16+"""
17+from __future__ import annotations
18+import csv, math, pathlib, sys
19+from dataclasses import dataclass, replace
20+import numpy as np
21+import pandas as pd
22+
23+ROOT = pathlib.Path(__file__).resolve().parents[3]
24+sys.path.insert(0, str(ROOT / "analysis/python"))
25+from traite import finance as fin # noqa: E402 (P = R / i : capitalisation directe de la bibliothèque)
26+
27+RAW = ROOT / "data/raw"
28+GRAINE = 20261031
29+
30+# =============================================================================================================
31+# 1. Quatre quadrants — paramètres pédagogiques d'un marché type (SYNTHÉTIQUE)
32+# =============================================================================================================
33+
34+@dataclass(frozen=True)
35+class Parametres4Q:
36+ """Marché type : S en logements, R en $/logement/an, P en $/logement, C en logements/an.
37+ Calibration pédagogique (ordres de grandeur) : loyer 1 200 $/mois ≈ loyer moyen SCHL RMR de Québec oct. 2025
38+ (1 227 $, tous logements) ; taux de capitalisation 5 % ≈ fourchette CBRE Québec T3 2026 (4,25-6,00 %) ;
39+ élasticité-prix de la demande d'espace −0,5 et taux de retrait δ = 1 %/an : hypothèses déclarées (non estimées)."""
40+ S0: float = 100_000.0 # stock initial (logements)
41+ R0: float = 14_400.0 # loyer annuel d'équilibre initial ($/logement/an) = 1 200 $/mois
42+ eta: float = -0.5 # élasticité-prix de la demande d'espace au point initial
43+ i: float = 0.05 # taux de capitalisation (marché des actifs)
44+ delta: float = 0.01 # taux de retrait du stock (démolitions, conversions), par an
45+ c0: float = 240_000.0 # seuil de coût : prix en deçà duquel rien ne se construit ($/logement)
46+
47+ @property
48+ def b(self) -> float:
49+ """Pente de la demande D = a − bR ; b = −η S0 / R0."""
50+ return -self.eta * self.S0 / self.R0
51+
52+ @property
53+ def a(self) -> float:
54+ return self.S0 + self.b * self.R0
55+
56+ @property
57+ def C0(self) -> float:
58+ return self.delta * self.S0
59+
60+ @property
61+ def P0(self) -> float:
62+ return fin.capitalisation_directe(self.R0, self.i)
63+
64+ @property
65+ def c1(self) -> float:
66+ """Pente de l'offre de construction C = (P − c0)/c1, telle que C0 = δ S0 au prix initial."""
67+ return (self.P0 - self.c0) / self.C0
68+
69+
70+def equilibre_4q(p: Parametres4Q, a: float | None = None, i: float | None = None, c0: float | None = None) -> dict:
71+ """Équilibre stationnaire des quatre quadrants en forme fermée :
72+ S* = (a − b i c0) / (1 + b i c1 δ) ; R* = (a − S*)/b ; P* = R*/i ; C* = δ S*."""
73+ a = p.a if a is None else a
74+ i = p.i if i is None else i
75+ c0 = p.c0 if c0 is None else c0
76+ b, c1, d = p.b, p.c1, p.delta
77+ S = (a - b * i * c0) / (1.0 + b * i * c1 * d)
78+ R = (a - S) / b
79+ P = fin.capitalisation_directe(R, i)
80+ C = d * S
81+ assert abs(C - (P - c0) / c1) < 1e-6 * max(1.0, C)
82+ return {"S": S, "R": R, "P": P, "C": C, "a": a, "i": i, "c0": c0}
83+
84+
85+def equilibre_4q_iteratif(p: Parametres4Q, a: float | None = None, i: float | None = None, c0: float | None = None,
86+ n_iter: int = 2000) -> dict:
87+ """Même équilibre par itération de point fixe S ← C(P(R(S)))/δ (vérification numérique de la forme fermée)."""
88+ a = p.a if a is None else a
89+ i = p.i if i is None else i
90+ c0 = p.c0 if c0 is None else c0
91+ S = p.S0
92+ for _ in range(n_iter):
93+ R = (a - S) / p.b
94+ P = fin.capitalisation_directe(R, i)
95+ C = max(0.0, (P - c0) / p.c1)
96+ S = 0.5 * S + 0.5 * C / p.delta # relaxation pour garantir la convergence
97+ R = (a - S) / p.b
98+ return {"S": S, "R": R, "P": fin.capitalisation_directe(R, i), "C": p.delta * S}
99+
100+
101+def court_terme_4q(p: Parametres4Q, a: float | None = None, i: float | None = None) -> dict:
102+ """Court terme : stock fixe à S0 ; le loyer et le prix absorbent le choc."""
103+ a = p.a if a is None else a
104+ i = p.i if i is None else i
105+ R = (a - p.S0) / p.b
106+ P = fin.capitalisation_directe(R, i)
107+ return {"S": p.S0, "R": R, "P": P, "C": max(0.0, (P - p.c0) / p.c1)}
108+
109+
110+def statique_comparative(p: Parametres4Q) -> pd.DataFrame:
111+ """Trois chocs : baisse du taux de capitalisation à 4 %, hausse de la demande de 10 %, hausse du coût de 10 %."""
112+ base = equilibre_4q(p)
113+ chocs = {
114+ "Référence": dict(),
115+ "Taux i : 5 % → 4 %": dict(i=0.04),
116+ "Demande a : +10 %": dict(a=1.10 * p.a),
117+ "Coût c0 : +10 %": dict(c0=1.10 * p.c0),
118+ }
119+ rows = []
120+ for nom, kw in chocs.items():
121+ lt = equilibre_4q(p, **kw)
122+ ct = court_terme_4q(p, a=kw.get("a"), i=kw.get("i")) if "c0" not in kw else {"R": base["R"], "P": base["P"]}
123+ rows.append({"choc": nom, "R_ct": ct["R"], "P_ct": ct["P"], "S_lt": lt["S"], "R_lt": lt["R"], "P_lt": lt["P"], "C_lt": lt["C"],
124+ "dS_pct": 100 * (lt["S"] / base["S"] - 1), "dR_ct_pct": 100 * (ct["R"] / base["R"] - 1),
125+ "dR_lt_pct": 100 * (lt["R"] / base["R"] - 1), "dP_ct_pct": 100 * (ct["P"] / base["P"] - 1),
126+ "dP_lt_pct": 100 * (lt["P"] / base["P"] - 1)})
127+ return pd.DataFrame(rows).set_index("choc")
128+
129+
130+def elasticites_4q(p: Parametres4Q) -> dict:
131+ """Élasticités au point initial : construction/prix (court terme) et stock stationnaire/demande (long terme)."""
132+ eps_C = (1.0 / p.c1) * (p.P0 / p.C0) # (dC/dP)(P/C)
133+ kappa = 1.0 / (p.b * p.i * p.c1) # réactivité de l'offre à l'écart de stock (par an)
134+ base = equilibre_4q(p)
135+ plus = equilibre_4q(p, a=1.01 * p.a)
136+ eps_S_a = (plus["S"] / base["S"] - 1) / 0.01 # élasticité du stock stationnaire à l'ordonnée de demande
137+ return {"eps_C_P": eps_C, "kappa": kappa, "eps_S_a": eps_S_a, "eta": p.eta}
138+
139+
140+# =============================================================================================================
141+# 2. Dynamique stock-flux (SYNTHÉTIQUE) — simulation trimestrielle et racines caractéristiques
142+# =============================================================================================================
143+
144+@dataclass(frozen=True)
145+class ParametresCycle:
146+ p4q: Parametres4Q = Parametres4Q()
147+ v_nat: float = 0.03 # taux d'inoccupation naturel (SCHL RMR Québec oct. 2025 : 2,4 % ; Montréal 2,9 %)
148+ lam: float = 0.5 # vitesse d'ajustement du loyer : ΔR/R = −λ (v − v*) par trimestre
149+ L: int = 6 # délai de livraison (trimestres) entre la mise en chantier et l'ajout au stock
150+ T: int = 80 # horizon (trimestres)
151+ t_choc: int = 8 # trimestre du choc
152+ choc_demande: float = 0.10 # hausse permanente de l'ordonnée de demande (+10 %)
153+ mult_c1: float = 1.0 # multiplicateur de la pente d'offre c1 (1 = référence ; >1 = offre moins élastique)
154+
155+
156+def simuler_cycle(pc: ParametresCycle) -> pd.DataFrame:
157+ """Trimestriel. Demande occupée D = a − bR ; inoccupation v = 1 − D/S ; loyer R_{t+1} = R_t[1 − λ(v_t − v*)] ;
158+ prix P = R/i ; mises en chantier C = max(0, (P − c0)/c1)/4 ; stock S_{t+1} = (1 − δ/4) S_t + C_{t−L}.
159+ L'état initial est stationnaire avec v = v* (l'ordonnée a est ajustée pour que D0 = (1 − v*) S0)."""
160+ p = pc.p4q
161+ c1 = p.c1 * pc.mult_c1
162+ b = p.b
163+ a0 = (1.0 - pc.v_nat) * p.S0 + b * p.R0
164+ S, R = p.S0, p.R0
165+ starts = [p.C0 / 4.0] * (pc.L + 1) # chantiers en cours au régime stationnaire
166+ rows = []
167+ for t in range(pc.T + 1):
168+ a = a0 * (1.0 + pc.choc_demande) if t >= pc.t_choc else a0
169+ D = min(S, a - b * R)
170+ v = 1.0 - D / S
171+ P = fin.capitalisation_directe(R, p.i)
172+ C = max(0.0, (P - p.c0) / c1) / 4.0
173+ rows.append({"t": t, "annee": t / 4.0, "S": S, "D": D, "v": v, "R": R, "P": P, "C": C, "a": a})
174+ starts.append(C)
175+ livraison = starts[-(pc.L + 1)]
176+ S = (1.0 - p.delta / 4.0) * S + livraison
177+ R = R * (1.0 - pc.lam * (v - pc.v_nat))
178+ df = pd.DataFrame(rows)
179+ df["R_mois"] = df["R"] / 12.0
180+ df["C_annuel"] = df["C"] * 4.0
181+ return df
182+
183+
184+def etat_stationnaire_cycle(pc: ParametresCycle, apres_choc: bool = True) -> dict:
185+ """État stationnaire du modèle dynamique (avec inoccupation naturelle) : v = v*, D = (1−v*)S, C = δS/4."""
186+ p = pc.p4q
187+ b = p.b
188+ a0 = (1.0 - pc.v_nat) * p.S0 + b * p.R0
189+ a = a0 * (1.0 + pc.choc_demande) if apres_choc else a0
190+ # (1−v*) S = a − bR ; R = i P ; P = c0 + c1 δ S → (1−v*) S = a − b i (c0 + c1 δ S)
191+ c1 = p.c1 * pc.mult_c1
192+ S = (a - b * p.i * p.c0) / ((1.0 - pc.v_nat) + b * p.i * c1 * p.delta)
193+ P = p.c0 + c1 * p.delta * S
194+ R = p.i * P
195+ return {"S": S, "R": R, "P": P, "C": p.delta * S, "v": pc.v_nat}
196+
197+
198+def resume_cycle(df: pd.DataFrame, pc: ParametresCycle) -> dict:
199+ """Mesures du cycle : pointe et creux de loyer (écart au stationnaire final), pointe d'inoccupation, période."""
200+ ss = etat_stationnaire_cycle(pc)
201+ apres = df[df["t"] >= pc.t_choc]
202+ R_rel = apres["R"] / ss["R"] - 1.0
203+ i_max = int(R_rel.idxmax())
204+ # creux après la première pointe
205+ post = R_rel.loc[i_max:]
206+ i_min = int(post.idxmin())
207+ # seconde pointe (pour la période) : maximum après le creux
208+ post2 = R_rel.loc[i_min:]
209+ i_max2 = int(post2.idxmax()) if len(post2) > 1 else i_max
210+ periode = (i_max2 - i_max) if i_max2 > i_max and post2.max() > 0 else float("nan")
211+ return {"R_pointe_pct": 100 * R_rel.max(), "t_pointe": df.loc[i_max, "t"] - pc.t_choc, "R_creux_pct": 100 * post.min(),
212+ "t_creux": df.loc[i_min, "t"] - pc.t_choc, "v_max_pct": 100 * apres["v"].max(), "v_min_pct": 100 * apres["v"].min(),
213+ "C_max_annuel": apres["C_annuel"].max(), "periode_trim": periode,
214+ "S_final_ecart_pct": 100 * (df["S"].iloc[-1] / ss["S"] - 1), "v_final_pct": 100 * df["v"].iloc[-1],
215+ "amortissement": (post2.max() / R_rel.max()) if periode == periode else float("nan")}
216+
217+
218+def sensibilite_cycle(pc: ParametresCycle, delais=(2, 4, 6, 8, 12), mult_c1=(0.5, 1.0, 2.0), lams=(0.25, 0.5, 1.0)) -> pd.DataFrame:
219+ rows = []
220+ for L in delais:
221+ for m in mult_c1:
222+ for lam in lams:
223+ r = resume_cycle(simuler_cycle(replace(pc, L=L, mult_c1=m, lam=lam)), replace(pc, L=L, mult_c1=m, lam=lam))
224+ rows.append({"L": L, "mult_c1": m, "lam": lam, **r})
225+ return pd.DataFrame(rows)
226+
227+
228+def racines_caracteristiques(kappa: float, delta: float, L: int = 1) -> dict:
229+ """Modèle linéaire annuel S_{t+1} = (1−δ) S_t + κ (a − S_{t−L}) + cte, κ = 1/(b i c1).
230+ Polynôme : r^{L+1} − (1−δ) r^L + κ = 0. Retourne le module maximal, la nature des racines et la période."""
231+ coeffs = np.zeros(L + 2)
232+ coeffs[0] = 1.0
233+ coeffs[1] = -(1.0 - delta)
234+ coeffs[-1] = kappa
235+ r = np.roots(coeffs)
236+ dom = r[np.argmax(np.abs(r))]
237+ complexe = bool(abs(dom.imag) > 1e-9)
238+ periode = (2 * math.pi / abs(np.angle(dom))) if complexe else float("inf")
239+ return {"racines": r, "module": float(abs(dom)), "oscille": complexe, "periode": periode, "stable": bool(abs(dom) < 1.0)}
240+
241+
242+def condition_oscillation_L1(kappa: float, delta: float) -> dict:
243+ """Cas L = 1 en forme fermée : r² − (1−δ) r + κ = 0 ; racines complexes ssi κ > (1−δ)²/4 ; module √κ ;
244+ période 2π / arccos((1−δ)/(2√κ))."""
245+ seuil = (1.0 - delta) ** 2 / 4.0
246+ if kappa > seuil:
247+ return {"seuil": seuil, "oscille": True, "module": math.sqrt(kappa), "periode": 2 * math.pi / math.acos((1 - delta) / (2 * math.sqrt(kappa))),
248+ "stable": kappa < 1.0}
249+ disc = math.sqrt((1 - delta) ** 2 - 4 * kappa)
250+ r1, r2 = ((1 - delta) + disc) / 2, ((1 - delta) - disc) / 2
251+ return {"seuil": seuil, "oscille": False, "module": max(abs(r1), abs(r2)), "periode": float("inf"), "stable": max(abs(r1), abs(r2)) < 1.0}
252+
253+
254+# =============================================================================================================
255+# 3. Données RÉELLES
256+# =============================================================================================================
257+
258+def _lire_statcan(fichier: pathlib.Path, filtre) -> list[dict]:
259+ with open(fichier, encoding="utf-8-sig") as f:
260+ return [r for r in csv.DictReader(f, delimiter=";") if filtre(r) and r["VALEUR"]]
261+
262+
263+def mises_en_chantier_quebec() -> pd.DataFrame:
264+ """34-10-0135-01 : Québec, toutes régions, trimestriel, non désaisonnalisé, mises en chantier par type d'unité.
265+ Périodes étiquetées par le premier mois du trimestre (1948-01 = T1 1948)."""
266+ rows = _lire_statcan(RAW / "statcan_34100135/34100135.csv",
267+ lambda r: r["GÉO"] == "Québec" and r["Désaisonnalisation"] == "Non désaisonnalisés"
268+ and r["Estimations de logement"] == "Logements mis en chantier")
269+ df = pd.DataFrame([{"periode": r["PÉRIODE DE RÉFÉRENCE"], "type": r["Type d'unité"], "unites": float(r["VALEUR"]), "vecteur": r["VECTEUR"]} for r in rows])
270+ df["annee"] = df["periode"].str[:4].astype(int)
271+ df["trimestre"] = df["periode"].str[5:7].map({"01": 1, "04": 2, "07": 3, "10": 4})
272+ df["date"] = pd.PeriodIndex(df["annee"].astype(str) + "Q" + df["trimestre"].astype(str), freq="Q").to_timestamp()
273+ return df.sort_values(["type", "date"]).reset_index(drop=True)
274+
275+
276+def mises_en_chantier_annuelles(df: pd.DataFrame | None = None) -> pd.DataFrame:
277+ """Sommes annuelles (années complètes à 4 trimestres) par type ; part des maisons individuelles et des collectifs."""
278+ df = mises_en_chantier_quebec() if df is None else df
279+ piv = df.pivot_table(index="annee", columns="type", values="unites", aggfunc="sum")
280+ n_trim = df[df["type"] == "Total d'unités"].groupby("annee")["trimestre"].count()
281+ piv["trimestres"] = n_trim
282+ piv = piv.rename(columns={"Total d'unités": "total", "Maisons individuelles": "individuelles", "Logements collectifs": "collectifs",
283+ "Appartements et autres logements": "appartements", "Logements jumelés": "jumeles", "Logements en bande": "bande"})
284+ piv["part_individuelles"] = piv["individuelles"] / piv["total"]
285+ piv["part_collectifs"] = piv["collectifs"] / piv["total"]
286+ piv["part_appartements"] = piv["appartements"] / piv["total"]
287+ return piv
288+
289+
290+def mises_en_chantier_montreal_annuelles() -> pd.Series:
291+ """34-10-0143-01 : RMR de Montréal, mensuel, total d'unités, sommes annuelles."""
292+ rows = _lire_statcan(RAW / "statcan_34100143/34100143.csv",
293+ lambda r: r["GÉO"] == "Montréal, Québec" and r["Estimations de logements"] == "Logements mis en chantier"
294+ and r["Type d'unité"] == "Total d'unités")
295+ s = pd.Series({r["PÉRIODE DE RÉFÉRENCE"]: float(r["VALEUR"]) for r in rows})
296+ s.index = pd.to_datetime(s.index)
297+ ann = s.groupby(s.index.year).sum()
298+ mois = s.groupby(s.index.year).count()
299+ return pd.DataFrame({"total": ann, "mois": mois})
300+
301+
302+def _valet(nom: str) -> pd.Series:
303+ rows = list(csv.reader(open(RAW / "bdc_valet" / f"{nom}.csv", encoding="utf-8-sig")))
304+ i = next(k for k, r in enumerate(rows) if r and r[0] == "date")
305+ s = pd.Series({r[0]: float(r[1]) for r in rows[i + 1:] if len(r) > 1 and r[1]})
306+ s.index = pd.to_datetime(s.index)
307+ return s.sort_index()
308+
309+
310+def taux_bdc_mensuels() -> pd.DataFrame:
311+ """Moyennes mensuelles : taux hypothécaire affiché 5 ans (V80691335), obligation GdC 5 ans et 10 ans, taux directeur (V39079, depuis 2009 dans Valet)."""
312+ out = pd.DataFrame({
313+ "hyp5": _valet("V80691335").resample("MS").mean(),
314+ "gdc5": _valet("BD.CDN.5YR.DQ.YLD").resample("MS").mean(),
315+ "gdc10": _valet("BD.CDN.10YR.DQ.YLD").resample("MS").mean(),
316+ "directeur": _valet("V39079").resample("MS").mean(),
317+ })
318+ return out
319+
320+
321+def changements_taux_directeur(depuis: str = "2020-01-01") -> pd.DataFrame:
322+ """Dates d'effet (Valet, lendemain de l'annonce) et niveaux du taux cible du financement à un jour (V39079)."""
323+ s = _valet("V39079")
324+ ch = s[s.diff().fillna(0) != 0]
325+ ch = ch[ch.index >= depuis]
326+ prev = s.shift(1).reindex(ch.index)
327+ return pd.DataFrame({"avant": prev.values, "apres": ch.values, "variation_pb": ((ch - prev) * 100).round().astype(int).values}, index=ch.index)
328+
329+
330+def taux_effectifs_a4(mois: str = "2026-08-01") -> dict:
331+ """Groupe A4 (fonds avancés) : V122667783 = assuré, fixe 5 ans et plus ; V122667789 = non assuré, fixe 5 ans et plus
332+ (correspondance d'après les métadonnées du fichier ; valeurs d'août 2026 citées dans notes-C/E : 4,12 % et 4,58 %)."""
333+ rows = list(csv.reader(open(RAW / "bdc_valet/A4_RATES_MORTGAGES.csv", encoding="utf-8-sig")))
334+ i = next(k for k, r in enumerate(rows) if r and r[0] == "date")
335+ hdr = rows[i]
336+ rec = next(r for r in rows[i + 1:] if r and r[0] == mois)
337+ d = dict(zip(hdr, rec))
338+ # libellés des séries (bloc SERIES du fichier)
339+ labels = {r[0]: r[1] for r in rows[:i] if len(r) >= 2 and r[0].startswith("V1226677")}
340+ assure = [k for k, v in labels.items() if "Insured" in v and "Uninsured" not in v and "5 years" in v and "Funds advanced" in v]
341+ non_assure = [k for k, v in labels.items() if "Uninsured" in v and "5 years" in v and "Funds advanced" in v]
342+ return {"mois": mois, "assure_fixe_5ans_plus": float(d[assure[0]]) if assure else float("nan"),
343+ "non_assure_fixe_5ans_plus": float(d[non_assure[0]]) if non_assure else float("nan"),
344+ "labels_assure": [labels[k] for k in assure], "labels_non_assure": [labels[k] for k in non_assure]}
345+
346+
347+def ipc_quebec(produit: str = "Ensemble") -> pd.Series:
348+ """18-10-0004-01, Québec, non désaisonnalisé, 2002 = 100 (Ensemble : v41691783 ; Loyer : v41691818)."""
349+ rows = _lire_statcan(RAW / "statcan_18100004/18100004.csv", lambda r: r["GÉO"] == "Québec" and r["Produits et groupes de produits"] == produit)
350+ s = pd.Series({r["PÉRIODE DE RÉFÉRENCE"] + "-01": float(r["VALEUR"]) for r in rows})
351+ s.index = pd.to_datetime(s.index)
352+ return s.sort_index()
353+
354+
355+def ipln_quebec(geo: str = "Québec", composante: str = "Total (maison et terrain)") -> pd.Series:
356+ rows = _lire_statcan(RAW / "statcan_18100205/18100205.csv", lambda r: r["GÉO"] == geo and r["Indices des prix des logements neufs"] == composante)
357+ s = pd.Series({r["PÉRIODE DE RÉFÉRENCE"] + "-01": float(r["VALEUR"]) for r in rows})
358+ s.index = pd.to_datetime(s.index)
359+ return s.sort_index()
360+
361+
362+# --- coût d'usage -------------------------------------------------------------------------------------------
363+
364+TAUX_TAXE_QUEBEC_2026 = 0.007464 + 0.0007899 # Ville de Québec 1-5 log. (0,7464 $/100 $) + taxe scolaire 2026-2027 (0,07899 $/100 $)
365+DELTA_ENTRETIEN_RISQUE = 0.025 # dépréciation + entretien + prime de risque : HYPOTHÈSE PÉDAGOGIQUE déclarée
366+
367+
368+def cout_usage(P: float, i: float, tau: float, delta: float, g: float) -> float:
369+ """u = P (i + τ + δ − g) : coût annuel d'occupation d'un logement de prix P (Poterba 1984)."""
370+ return P * (i + tau + delta - g)
371+
372+
373+def taux_cout_usage(i: float, tau: float, delta: float, g: float) -> float:
374+ return i + tau + delta - g
375+
376+
377+def ratio_prix_loyer(i: float, tau: float, delta: float, g: float) -> float:
378+ """À l'équilibre R = u → P/R = 1/(i + τ + δ − g) (multiple du loyer annuel)."""
379+ u = taux_cout_usage(i, tau, delta, g)
380+ if u <= 0:
381+ return float("inf")
382+ return 1.0 / u
383+
384+
385+def serie_cout_usage(tau: float = TAUX_TAXE_QUEBEC_2026, delta: float = DELTA_ENTRETIEN_RISQUE, debut: str = "2001-01-01") -> pd.DataFrame:
386+ """Série mensuelle réelle : i = taux hypothécaire affiché 5 ans ; π = inflation IPC Québec sur 12 mois ;
387+ g_ipc = π (anticipation = inflation courante) ; g_ipln = croissance annualisée de l'IPLN Québec sur 5 ans ;
388+ u et P/R pour chaque hypothèse. τ et δ constants (déclarés)."""
389+ bdc = taux_bdc_mensuels()
390+ ipc = ipc_quebec()
391+ pi = (ipc / ipc.shift(12) - 1.0) * 100
392+ ipln = ipln_quebec()
393+ g_ipln = ((ipln / ipln.shift(60)) ** (1 / 5) - 1.0) * 100
394+ df = pd.DataFrame({"hyp5": bdc["hyp5"], "gdc5": bdc["gdc5"], "directeur": bdc["directeur"], "pi": pi, "g_ipln": g_ipln}).loc[debut:]
395+ df = df.dropna(subset=["hyp5", "pi"])
396+ df["reel"] = df["hyp5"] - df["pi"]
397+ df["u_ipc"] = df["hyp5"] + 100 * (tau + delta) - df["pi"]
398+ df["u_ipln"] = df["hyp5"] + 100 * (tau + delta) - df["g_ipln"]
399+ df["pr_ipc"] = 100.0 / df["u_ipc"]
400+ df["pr_ipln"] = (100.0 / df["u_ipln"]).where(df["u_ipln"] > 0)
401+ return df
402+
403+
404+DATES_TABLEAU_COUT_USAGE = ["2001-01-01", "2008-01-01", "2012-01-01", "2020-07-01", "2023-10-01", "2026-09-01"]
405+
406+
407+def exemple_cout_usage(prix: float = 463_500.0, i_affiche: float = 0.0619, i_effectif: float = 0.0458, pi: float = 0.031,
408+ g_ipln: float = 0.013, tau: float = TAUX_TAXE_QUEBEC_2026, delta: float = DELTA_ENTRETIEN_RISQUE) -> dict:
409+ """Exemple intégral : unifamiliale au prix médian APCIQ RMR de Québec T3 2026 (463 500 $) ; taux affiché 5 ans 6,19 %
410+ (BdC 2026-10-28) ; taux effectif non assuré fixe ≥ 5 ans 4,58 % (A4, août 2026) ; inflation IPC Québec 3,1 % ;
411+ IPLN Québec +1,3 % (sept. 2026). Semi-élasticité : d ln(P/R) / di = −1/u."""
412+ out = {}
413+ for nom, i in (("affiche", i_affiche), ("effectif", i_effectif)):
414+ for gnom, g in (("ipc", pi), ("ipln", g_ipln)):
415+ u = taux_cout_usage(i, tau, delta, g)
416+ out[f"u_{nom}_{gnom}"] = u
417+ out[f"cout_annuel_{nom}_{gnom}"] = cout_usage(prix, i, tau, delta, g)
418+ out[f"cout_mensuel_{nom}_{gnom}"] = cout_usage(prix, i, tau, delta, g) / 12
419+ out[f"pr_{nom}_{gnom}"] = ratio_prix_loyer(i, tau, delta, g)
420+ # sensibilité : +100 pb sur le taux effectif, anticipation = IPC
421+ u0 = taux_cout_usage(i_effectif, tau, delta, pi)
422+ u1 = taux_cout_usage(i_effectif + 0.01, tau, delta, pi)
423+ out["pr_effectif_ipc_plus100pb"] = 1 / u1
424+ out["var_pr_plus100pb_pct"] = 100 * ((1 / u1) / (1 / u0) - 1)
425+ out["semi_elasticite"] = -1 / u0
426+ # loyer de référence SCHL (RMR Québec oct. 2025, appartements 3 ch. et plus) pour comparaison
427+ out["loyer_schl_3ch_quebec"] = 1475.0
428+ out["loyer_schl_2ch_quebec"] = 1277.0
429+ out["prix_implicite_loyer_3ch_effectif_ipc"] = 1475.0 * 12 / u0
430+ return out
431+
432+
433+# --- indicateurs d'alerte (APCIQ T3 2026, chiffres cités dans sources/notes-E.md) --------------------------------
434+
435+APCIQ_T3_2026 = {
436+ # territoire : (ventes T3 2026, inscriptions en vigueur, médiane unifamiliale)
437+ "Province": (20_935, 42_408, 500_000),
438+ "RMR Montréal": (9_265, 20_350, 645_500),
439+ "RMR Québec": (2_161, 2_204, 463_500),
440+ "RMR Gatineau": (1_044, 2_256, 480_000),
441+ "RMR Sherbrooke": (569, 825, 490_750),
442+ "RMR Trois-Rivières": (372, 447, 395_000),
443+ "RMR Saguenay": (371, 498, 365_500),
444+}
445+
446+
447+def mois_inventaire(inscriptions: float, ventes_trimestre: float) -> float:
448+ """Mois d'inventaire = inscriptions en vigueur / ventes mensuelles moyennes du trimestre."""
449+ return inscriptions / (ventes_trimestre / 3.0)
450+
451+
452+def indicateurs_apciq() -> pd.DataFrame:
453+ rows = []
454+ for terr, (v, insc, med) in APCIQ_T3_2026.items():
455+ rows.append({"territoire": terr, "ventes_t3": v, "inscriptions": insc, "ventes_mensuelles": v / 3.0,
456+ "mois_inventaire": mois_inventaire(insc, v), "mediane_unifamiliale": med})
457+ return pd.DataFrame(rows).set_index("territoire")
458+
459+
460+# =============================================================================================================
461+# Impression
462+# =============================================================================================================
463+
464+def _f(x, d=0):
465+ return f"{x:,.{d}f}".replace(",", " ")
466+
467+
468+def main() -> None:
469+ p = Parametres4Q()
470+ print("=== 1. Quatre quadrants (marché type, SYNTHÉTIQUE) ===")
471+ print(f"b = {p.b:.4f} log./$ ; a = {_f(p.a)} ; P0 = {_f(p.P0)} $ ; C0 = {_f(p.C0)} ; c1 = {p.c1:.2f} $ par (log./an)")
472+ eq = equilibre_4q(p); it = equilibre_4q_iteratif(p)
473+ print(f"Équilibre fermé : S*={_f(eq['S'])} R*={_f(eq['R'])} P*={_f(eq['P'])} C*={_f(eq['C'])} | itératif S={_f(it['S'],1)}")
474+ el = elasticites_4q(p)
475+ print(f"Élasticités : construction/prix = {el['eps_C_P']:.2f} ; kappa = {el['kappa']:.4f} ; stock stationnaire/demande = {el['eps_S_a']:.3f}")
476+ sc = statique_comparative(p)
477+ pd.set_option("display.width", 200); pd.set_option("display.float_format", lambda x: f"{x:,.2f}")
478+ print(sc[["R_ct", "P_ct", "S_lt", "R_lt", "P_lt", "C_lt", "dS_pct", "dR_ct_pct", "dR_lt_pct", "dP_ct_pct", "dP_lt_pct"]])
479+
480+ print("\n=== 2. Dynamique stock-flux (SYNTHÉTIQUE) ===")
481+ pc = ParametresCycle()
482+ df = simuler_cycle(pc); ss = etat_stationnaire_cycle(pc); r = resume_cycle(df, pc)
483+ print(f"Stationnaire après choc : S={_f(ss['S'])} R={_f(ss['R'])} ({ss['R']/12:.0f} $/mois) P={_f(ss['P'])} C={_f(ss['C'])}/an")
484+ print({k: (round(v, 2) if isinstance(v, float) else v) for k, v in r.items()})
485+ for L in (1, 2, 4):
486+ rc = racines_caracteristiques(el["kappa"], p.delta, L)
487+ print(f"Racines (kappa={el['kappa']:.3f}, L={L}) : module={rc['module']:.3f} oscille={rc['oscille']} période={rc['periode']:.1f} ans stable={rc['stable']}")
488+ for k in (0.12, 0.30, 0.60, 1.20):
489+ c = condition_oscillation_L1(k, p.delta)
490+ print(f"L=1, kappa={k}: seuil={c['seuil']:.4f} oscille={c['oscille']} module={c['module']:.3f} période={c['periode']:.2f} stable={c['stable']}")
491+ sens = sensibilite_cycle(pc)
492+ print(sens[(sens["lam"] == 0.5)][["L", "mult_c1", "R_pointe_pct", "R_creux_pct", "v_max_pct", "C_max_annuel", "periode_trim", "amortissement"]].to_string(index=False))
493+
494+ print("\n=== 3. Données réelles ===")
495+ ann = mises_en_chantier_annuelles()
496+ cols = ["total", "individuelles", "collectifs", "part_individuelles", "part_collectifs", "trimestres"]
497+ print(ann.loc[[1990, 2000, 2005, 2010, 2015, 2020, 2021, 2022, 2023, 2024, 2025, 2026], cols])
498+ mtl = mises_en_chantier_montreal_annuelles(); print("Montréal RMR :", mtl.loc[[2024, 2025, 2026]].to_dict())
499+ ch = changements_taux_directeur(); print(ch)
500+ print(taux_effectifs_a4())
501+ cu = serie_cout_usage()
502+ print(cu.loc[DATES_TABLEAU_COUT_USAGE, ["hyp5", "gdc5", "pi", "g_ipln", "reel", "u_ipc", "pr_ipc", "u_ipln", "pr_ipln"]])
503+ print("Dernier mois :", cu.index[-1].date(), cu.iloc[-1][["hyp5", "pi", "u_ipc", "pr_ipc"]].to_dict())
504+ print("IPC Québec sept. 2026 :", ipc_quebec().iloc[-1], "; a/a =", round((ipc_quebec().iloc[-1] / ipc_quebec().iloc[-13] - 1) * 100, 2))
505+ print("IPLN Québec sept. 2026 :", ipln_quebec().iloc[-1], "; a/a =", round((ipln_quebec().iloc[-1] / ipln_quebec().iloc[-13] - 1) * 100, 2))
506+ ex = exemple_cout_usage()
507+ for k, v in ex.items():
508+ print(f" {k}: {v:,.4f}" if isinstance(v, float) else f" {k}: {v}")
509+ print(indicateurs_apciq())
510+
511+
512+if __name__ == "__main__":
513+ main()
added analysis/python/cases/ch04_marches.py +333 −0
@@ -0,0 +1,333 @@
1+#!/usr/bin/env python3
2+"""Chapitre 4 — Marchés résidentiels québécois : lecture des DONNÉES RÉELLES de data/raw et calculs cités dans le texte.
3+
4+Imprime tous les chiffres du chapitre 4 :
5+ * structure du parc (Recensement 2021, tableau 98-10-0240-01, WDS) : ménages par type de construction et mode
6+ d'occupation, Québec et Canada ; parts, taux de propriété par type, indice de dissimilarité Québec/Canada ;
7+ * ménages et locataires par RMR (98-10-0231-01) ; population des RMR 2020-2025 (17-10-0148-01) et TCAC ;
8+ * Indice des prix des logements neufs (18-10-0205-01, déc. 2016 = 100) : niveaux de septembre 2026, variations,
9+ composantes maison/terrain, épisode 2020-2022 ;
10+ * marché locatif SCHL (Enquête sur les logements locatifs, octobre 2025, tableaux 1.1.1, 1.1.2, 1.1.3, 1.1.5 du
11+ fichier rmr-quebec-2025-fr.xlsx) : inoccupation, loyers, univers, variation estimative vs variation brute ;
12+ * indicateurs APCIQ T3 2026 (chiffres cités, calibration.json) : mois d'inventaire, rapports de prix, MRB implicite ;
13+ * Banque du Canada (API Valet) : taux directeur, taux hypothécaire affiché 5 ans, obligation 5 ans, 2000-2026 ;
14+ * droits sur les mutations immobilières 2026 (province, Montréal, Gatineau, Québec) ; sensibilité du prix soutenable
15+ au taux hypothécaire (traite.finance.Hypotheque, composition semestrielle).
16+
17+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch04_marches.py
18+Aucune donnée synthétique n'est utilisée dans ce chapitre. Licences : voir data/raw/SOURCES.md."""
19+from __future__ import annotations
20+import sys, json, csv, pathlib, math
21+import numpy as np, pandas as pd
22+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
23+from traite import finance as fin
24+
25+ROOT = pathlib.Path(__file__).resolve().parents[3]
26+RAW = ROOT / "data/raw"
27+CAL = json.loads((ROOT / "data/synthetic/calibration.json").read_text(encoding="utf-8"))
28+
29+# ----------------------------------------------------------------------------- Recensement 2021 : types et mode d'occupation
30+# Membres de la dimension « Type de construction résidentielle » du tableau 98-10-0240-01 (métadonnées WDS, getCubeMetadata, 2026-10-31)
31+TYPES_240 = {2: "Maison individuelle non attenante", 9: "Maison jumelée", 8: "Maison en rangée", 5: "Appartement ou plain-pied dans un duplex",
32+ 6: "Appartement, immeuble de moins de cinq étages", 3: "Appartement, immeuble de cinq étages ou plus",
33+ 7: "Autre maison individuelle attenante", 10: "Logement mobile"}
34+TYPES_COURTS = {2: "Maison individuelle", 9: "Jumelée", 8: "En rangée", 5: "Duplex", 6: "Appartement < 5 étages",
35+ 3: "Appartement ≥ 5 étages", 7: "Autre attenante", 10: "Mobile"}
36+MODES_240 = {1: "total", 2: "proprietaire", 3: "locataire"}
37+GEO_240 = {1: "Canada", 884: "Québec"}
38+
39+def recensement_types() -> pd.DataFrame:
40+ """Ménages privés selon le type de construction et le mode d'occupation (Canada, Québec), Recensement 2021."""
41+ d = json.loads((RAW / "statcan_recensement2021/98100240_canada_quebec_type_construction_mode_occupation.json").read_text(encoding="utf-8"))
42+ rows = []
43+ for e in d:
44+ o = e["object"]; c = [int(x) for x in o["coordinate"].split(".")]
45+ geo, typ, mode = c[0], c[4], c[5]
46+ rows.append({"geo": GEO_240[geo], "type_id": typ, "mode": MODES_240[mode], "menages": o["vectorDataPoint"][0]["value"]})
47+ df = pd.DataFrame(rows).pivot_table(index=["geo", "type_id"], columns="mode", values="menages").reset_index()
48+ df["type"] = df["type_id"].map(lambda k: TYPES_240.get(k, "Total" if k == 1 else "Autre logement attenant (agrégat)"))
49+ df["court"] = df["type_id"].map(lambda k: TYPES_COURTS.get(k, "Total" if k == 1 else "Attenant (agrégat)"))
50+ tot = df[df["type_id"] == 1].set_index("geo")["total"]
51+ df["part"] = df.apply(lambda r: r["total"] / tot[r["geo"]], axis=1)
52+ df["taux_propriete"] = df["proprietaire"] / df["total"]
53+ return df
54+
55+def structure_parc() -> pd.DataFrame:
56+ """Huit types détaillés (sans l'agrégat 4 ni le total), Québec et Canada côte à côte."""
57+ df = recensement_types()
58+ d = df[df["type_id"].isin(TYPES_240)].copy()
59+ q = d[d["geo"] == "Québec"].set_index("type_id"); c = d[d["geo"] == "Canada"].set_index("type_id")
60+ out = pd.DataFrame({"type": q["type"], "court": q["court"], "qc_menages": q["total"], "qc_part": q["part"], "qc_proprietaires": q["proprietaire"],
61+ "qc_locataires": q["locataire"], "qc_taux_propriete": q["taux_propriete"], "ca_menages": c["total"], "ca_part": c["part"],
62+ "ca_taux_propriete": c["taux_propriete"]})
63+ return out.sort_values("qc_menages", ascending=False)
64+
65+def indice_dissimilarite(parts_a: np.ndarray, parts_b: np.ndarray) -> float:
66+ """D = ½ Σ |s_i^A − s_i^B| : part des ménages à « reclasser » pour que les deux structures coïncident."""
67+ return 0.5 * float(np.abs(np.asarray(parts_a) - np.asarray(parts_b)).sum())
68+
69+def menages_rmr() -> pd.DataFrame:
70+ """98-10-0231-01 : ménages propriétaires / locataires, Canada, Québec, RMR Montréal, Québec, Ottawa-Gatineau (ensemble)."""
71+ geo = {1: "Canada", 24: "Québec (province)", 35: "RMR Montréal", 36: "RMR Québec", 81: "RMR Ottawa-Gatineau (ensemble)"}
72+ d = json.loads((RAW / "statcan_recensement2021/98100231_mode_occupation_canada_quebec_rmr.json").read_text(encoding="utf-8"))
73+ rows = {}
74+ for e in d:
75+ o = e["object"]; c = [int(x) for x in o["coordinate"].split(".")]
76+ rows.setdefault(geo[c[0]], {})[MODES_240[c[6]]] = o["vectorDataPoint"][0]["value"]
77+ df = pd.DataFrame(rows).T
78+ df["part_locataires"] = df["locataire"] / df["total"]
79+ return df
80+
81+# ----------------------------------------------------------------------------- Population des RMR (17-10-0148-01)
82+RMR_148 = {14: "Montréal", 10: "Québec", 17: "Ottawa-Gatineau (partie du Québec)", 11: "Sherbrooke", 12: "Trois-Rivières", 9: "Saguenay",
83+ 13: "Drummondville", 191: "Ensemble des RMR du Québec"}
84+
85+def population_rmr() -> pd.DataFrame:
86+ d = json.loads((RAW / "statcan_17100148/17100148_rmr_quebec_total_latest6.json").read_text(encoding="utf-8"))
87+ rows = {}
88+ for e in d:
89+ o = e["object"]; k = int(o["coordinate"].split(".")[0])
90+ rows[RMR_148[k]] = {int(p["refPerRaw"][:4]): p["value"] for p in o["vectorDataPoint"]}
91+ df = pd.DataFrame(rows).T.sort_index(axis=1)
92+ df["var_2024_2025"] = df[2025] / df[2024] - 1
93+ df["tcac_2020_2025"] = (df[2025] / df[2020]) ** (1 / 5) - 1
94+ return df
95+
96+# ----------------------------------------------------------------------------- IPLN (18-10-0205-01)
97+IPLN_GEO = {"Canada": "Canada", "Québec": "Québec (province)", "Montréal, Québec": "Montréal", "Québec, Québec": "Québec",
98+ "Ottawa-Gatineau, partie Québec, Ontario/Québec": "Gatineau (partie du Québec)", "Sherbrooke, Québec": "Sherbrooke",
99+ "Trois-Rivières, Québec": "Trois-Rivières"}
100+
101+def ipln() -> pd.DataFrame:
102+ """Séries mensuelles (déc. 2016 = 100) : colonnes date, geo, composante (Total / Maison / Terrain), indice."""
103+ rows = []
104+ with open(RAW / "statcan_18100205/18100205.csv", encoding="utf-8-sig") as f:
105+ r = csv.reader(f, delimiter=";"); next(r)
106+ for row in r:
107+ if row[1] in IPLN_GEO and row[10] not in ("", ".."):
108+ comp = {"Total (maison et terrain)": "Total", "Maison seulement": "Maison", "Terrain seulement": "Terrain"}[row[3]]
109+ rows.append({"date": pd.Timestamp(row[0] + "-01"), "geo": IPLN_GEO[row[1]], "composante": comp, "indice": float(row[10]), "vecteur": row[8]})
110+ return pd.DataFrame(rows).sort_values(["geo", "composante", "date"]).reset_index(drop=True)
111+
112+def ipln_stats(df: pd.DataFrame) -> pd.DataFrame:
113+ """Par géographie : dernier mois, niveau total/maison/terrain, variation 12 mois, variation depuis déc. 2016, épisode janv. 2020 → déc. 2022, sommet."""
114+ out = []
115+ for geo, g in df.groupby("geo"):
116+ t = g[g["composante"] == "Total"].set_index("date")["indice"]
117+ last = t.index.max()
118+ rec = {"geo": geo, "dernier_mois": last.strftime("%Y-%m"), "total": t[last], "var_12_mois": t[last] / t[last - pd.DateOffset(years=1)] - 1,
119+ "var_depuis_2016_12": t[last] / t[pd.Timestamp("2016-12-01")] - 1,
120+ "var_2020_01_2022_12": t[pd.Timestamp("2022-12-01")] / t[pd.Timestamp("2020-01-01")] - 1,
121+ "sommet": t.idxmax().strftime("%Y-%m"), "sommet_val": t.max(), "ecart_sommet": t[last] / t.max() - 1,
122+ "var_2000_2016": t[pd.Timestamp("2016-12-01")] / t[pd.Timestamp("2000-01-01")] - 1 if pd.Timestamp("2000-01-01") in t.index else np.nan}
123+ for comp in ("Maison", "Terrain"):
124+ s = g[g["composante"] == comp].set_index("date")["indice"]
125+ rec[comp.lower()] = s.get(last, np.nan)
126+ rec[f"{comp.lower()}_var_depuis_2016_12"] = s.get(last, np.nan) / s.get(pd.Timestamp("2016-12-01"), np.nan) - 1
127+ out.append(rec)
128+ return pd.DataFrame(out).set_index("geo")
129+
130+# ----------------------------------------------------------------------------- SCHL, Enquête sur les logements locatifs (octobre 2025)
131+RMR_SCHL = {"Montréal (RMR)": "Montréal", "Québec (RMR)": "Québec", "Ottawa-Gatineau (RMR) (partie Québec)": "Gatineau (partie du Québec)",
132+ "Sherbrooke (RMR)": "Sherbrooke", "Trois-Rivières (RMR)": "Trois-Rivières", "Saguenay (RMR)": "Saguenay"}
133+_SKIP = {"a", "b", "c", "d", "↑", "↓", "-", "n.d."}
134+TYPES_SCHL = ["studio", "1_chambre", "2_chambres", "3_chambres_plus", "tous"]
135+
136+def _val(tok):
137+ if tok is None: return None
138+ s = str(tok).strip().replace(" ", " ")
139+ if s in ("**", "++", ""): return np.nan # ** : donnée supprimée ; ++ : variation non calculable
140+ try: return float(s.replace(" ", "").replace(",", "."))
141+ except ValueError: return None
142+
143+def _lire_tableau(ws) -> dict:
144+ """Pour chaque centre : 10 valeurs (5 types × [oct. 2024, oct. 2025]) nettoyées des lettres de qualité et des flèches."""
145+ out = {}
146+ for row in ws.iter_rows(values_only=True):
147+ cells = [c for c in row if c is not None]
148+ if not cells: continue
149+ lab = str(cells[0]).strip()
150+ for k, name in RMR_SCHL.items():
151+ if lab.startswith(k.split(" (RMR)")[0]) and "(RMR)" in lab:
152+ vals = []
153+ for tok in cells[1:]:
154+ s = str(tok).strip()
155+ if s in _SKIP: continue
156+ v = _val(tok)
157+ if v is None: continue
158+ vals.append(v)
159+ if len(vals) == 10:
160+ out[name] = vals
161+ return out
162+
163+def schl_rmr() -> pd.DataFrame:
164+ """Inoccupation (%), loyer moyen ($), univers (logements) et variation estimative (%) par RMR, octobre 2024 et 2025 (fichier Québec, tous les centres)."""
165+ import openpyxl
166+ wb = openpyxl.load_workbook(RAW / "schl_rml_2025/rmr-quebec-2025-fr.xlsx", read_only=True, data_only=True)
167+ t111, t112, t113, t115 = (_lire_tableau(wb[f"Tableau 1.1.{i}"]) for i in (1, 2, 3, 5))
168+ rows = []
169+ for name in RMR_SCHL.values():
170+ rec = {"rmr": name}
171+ for j, typ in enumerate(TYPES_SCHL):
172+ rec[f"inocc_{typ}_2024"], rec[f"inocc_{typ}_2025"] = t111[name][2 * j], t111[name][2 * j + 1]
173+ rec[f"loyer_{typ}_2024"], rec[f"loyer_{typ}_2025"] = t112[name][2 * j], t112[name][2 * j + 1]
174+ rec[f"univers_{typ}_2024"], rec[f"univers_{typ}_2025"] = t113[name][2 * j], t113[name][2 * j + 1]
175+ rec[f"varest_{typ}_2024"], rec[f"varest_{typ}_2025"] = t115[name][2 * j], t115[name][2 * j + 1]
176+ rec["var_brute_2_chambres"] = rec["loyer_2_chambres_2025"] / rec["loyer_2_chambres_2024"] - 1
177+ rec["var_brute_tous"] = rec["loyer_tous_2025"] / rec["loyer_tous_2024"] - 1
178+ rows.append(rec)
179+ return pd.DataFrame(rows).set_index("rmr")
180+
181+# ----------------------------------------------------------------------------- APCIQ T3 2026 (chiffres cités avec attribution, calibration.json)
182+def apciq_t3_2026() -> pd.DataFrame:
183+ """Médianes et volumes T3 2026 (APCIQ par le système Centris) tels que notés dans calibration.json ; mois d'inventaire = inscriptions / (ventes trimestrielles / 3)."""
184+ p = CAL["prix_apciq"]
185+ rows = {
186+ "Province": dict(unifamiliale=p["province"]["unifamiliale_prix_median"]["valeur"], copropriete=p["province"]["copropriete_prix_median"]["valeur"],
187+ plex=p["province"]["plex_prix_median"]["valeur"], ventes=p["province"]["ventes_residentielles"]["valeur"], inscriptions=p["province"]["inscriptions_en_vigueur"]["valeur"]),
188+ "Montréal": dict(unifamiliale=645500, copropriete=435000, plex=860500, ventes=9265, inscriptions=20350),
189+ "Québec": dict(unifamiliale=463500, copropriete=334900, plex=600000, ventes=2161, inscriptions=2204),
190+ "Gatineau": dict(unifamiliale=480000, copropriete=297450, plex=617500, ventes=1044, inscriptions=2256),
191+ "Sherbrooke": dict(unifamiliale=490750, copropriete=327000, plex=544500, ventes=569, inscriptions=825),
192+ "Trois-Rivières": dict(unifamiliale=395000, copropriete=287000, plex=420000, ventes=372, inscriptions=447),
193+ "Saguenay": dict(unifamiliale=365500, copropriete=np.nan, plex=373500, ventes=371, inscriptions=498),
194+ }
195+ for k in ("Montréal", "Québec", "Gatineau", "Sherbrooke"): # cohérence avec calibration.json quand la RMR y figure
196+ key = {"Montréal": "rmr_montreal", "Québec": "rmr_quebec", "Gatineau": "rmr_gatineau", "Sherbrooke": "rmr_sherbrooke"}[k]
197+ assert p[key]["unifamiliale_prix_median"]["valeur"] == rows[k]["unifamiliale"], k
198+ df = pd.DataFrame(rows).T
199+ df["mois_inventaire"] = df["inscriptions"] / (df["ventes"] / 3)
200+ df["plex_sur_unifamiliale"] = df["plex"] / df["unifamiliale"]
201+ df["copro_sur_unifamiliale"] = df["copropriete"] / df["unifamiliale"]
202+ return df
203+
204+def mrb_implicite(prix_plex: float, loyer_2ch: float, logements: int = 3) -> float:
205+ """Multiplicateur du revenu brut implicite d'un plex de `logements` logements loués au loyer moyen 2 chambres de la RMR (ordre de grandeur)."""
206+ return fin.multiplicateur_revenu_brut(prix_plex, 12 * logements * loyer_2ch)
207+
208+# ----------------------------------------------------------------------------- Banque du Canada (Valet)
209+def _valet(fichier: str) -> pd.Series:
210+ rows = []
211+ with open(RAW / "bdc_valet" / fichier, encoding="utf-8-sig") as f:
212+ started = False
213+ for line in f:
214+ line = line.strip()
215+ if line.startswith('"OBSERVATIONS"'): started = True; continue
216+ if not started or not line or line.startswith('"date"'): continue
217+ parts = [x.strip('"') for x in line.split(",")]
218+ if len(parts) >= 2 and parts[1] not in ("", "na"):
219+ rows.append((pd.Timestamp(parts[0]), float(parts[1])))
220+ return pd.Series(dict(rows)).sort_index()
221+
222+def bdc() -> dict:
223+ s = {"directeur": _valet("V39079.csv"), "hyp5": _valet("V80691335.csv"), "obl5": _valet("BD.CDN.5YR.DQ.YLD.csv"), "obl10": _valet("BD.CDN.10YR.DQ.YLD.csv")}
224+ d = s["directeur"]
225+ chg = d[d.diff() != 0].dropna()
226+ stats = {"directeur_dernier": (d.index[-1].date().isoformat(), d.iloc[-1]), "directeur_min": (d.idxmin().date().isoformat(), d.min()),
227+ "directeur_max_2000": (d.idxmax().date().isoformat(), d.max()),
228+ "directeur_2022_03": d[d.index >= "2022-03-01"].iloc[0], "directeur_2023_07_12": d[d.index >= "2023-07-13"].iloc[0],
229+ "hyp5_dernier": (s["hyp5"].index[-1].date().isoformat(), s["hyp5"].iloc[-1]), "hyp5_min": (s["hyp5"].idxmin().date().isoformat(), s["hyp5"].min()),
230+ "hyp5_max_2000": (s["hyp5"].idxmax().date().isoformat(), s["hyp5"].max()),
231+ "obl5_dernier": (s["obl5"].index[-1].date().isoformat(), s["obl5"].iloc[-1]), "obl10_dernier": (s["obl10"].index[-1].date().isoformat(), s["obl10"].iloc[-1]),
232+ "changements_2022_2026": [(t.date().isoformat(), v) for t, v in chg[chg.index >= "2022-01-01"].items()]}
233+ return {"series": s, "stats": stats}
234+
235+# ----------------------------------------------------------------------------- Droits sur les mutations immobilières 2026
236+BAREMES_2026 = {
237+ # (borne supérieure de la tranche, taux) ; None = au-delà. Sources : LDMI art. 2 (province) ; montreal.ca ; gatineau.ca (dès le 25 février 2026) ; ville.quebec.qc.ca
238+ "Province (barème légal)": [(62900, 0.005), (315000, 0.01), (None, 0.015)],
239+ "Montréal": [(62900, 0.005), (315000, 0.01), (552300, 0.015), (1104700, 0.02), (2136500, 0.025), (3113000, 0.035), (None, 0.04)],
240+ "Gatineau (dès le 25 février 2026)": [(62900, 0.005), (315000, 0.01), (500000, 0.015), (None, 0.03)],
241+ "Québec (ville)": [(62900, 0.005), (315000, 0.01), (500000, 0.015), (750000, 0.025), (None, 0.03)],
242+}
243+
244+def droits_mutation(base: float, bareme: list[tuple]) -> float:
245+ """Droit progressif par tranches : Σ_k τ_k × (min(B, b_k) − b_{k−1})⁺."""
246+ total, prev = 0.0, 0.0
247+ for borne, taux in bareme:
248+ haut = base if borne is None else min(base, borne)
249+ if haut > prev: total += taux * (haut - prev)
250+ prev = borne if borne is not None else base
251+ if borne is not None and base <= borne: break
252+ return total
253+
254+def tableau_mutations(prix: list[float]) -> pd.DataFrame:
255+ rows = []
256+ for nom, bar in BAREMES_2026.items():
257+ for p in prix:
258+ d = droits_mutation(p, bar)
259+ rows.append({"bareme": nom, "prix": p, "droit": d, "taux_effectif": d / p})
260+ return pd.DataFrame(rows)
261+
262+# ----------------------------------------------------------------------------- Taux et prix soutenable
263+def paiement_mensuel(capital: float, taux: float, amort_ans: int = 25) -> float:
264+ return fin.Hypotheque(capital, taux, amort_ans * 12).paiement
265+
266+def prix_soutenable(paiement: float, taux: float, mise_de_fonds: float = 0.20, amort_ans: int = 25) -> float:
267+ """Prix tel que le paiement mensuel d'un prêt de (1 − m) × prix, au taux donné (composition semestrielle), égale `paiement`."""
268+ i = fin.taux_mensuel_canadien(taux)
269+ capital = paiement * fin.facteur_va_annuite(i, amort_ans * 12)
270+ return capital / (1 - mise_de_fonds)
271+
272+def sensibilite_taux(prix_ref: float = 500000.0, taux_ref: float = 0.0412, taux: tuple = (0.03, 0.035, 0.0412, 0.045, 0.05, 0.055, 0.0619, 0.07)) -> pd.DataFrame:
273+ """Prix médian provincial T3 2026 (500 000 $), mise de fonds 20 %, 25 ans : paiement au taux de référence (assuré fixe ≥ 5 ans, août 2026 : 4,12 %),
274+ puis prix soutenable à paiement constant pour chaque taux."""
275+ cap = prix_ref * 0.8
276+ pmt_ref = paiement_mensuel(cap, taux_ref)
277+ rows = []
278+ for t in taux:
279+ rows.append({"taux": t, "paiement_pour_prix_ref": paiement_mensuel(cap, t), "prix_soutenable": prix_soutenable(pmt_ref, t),
280+ "variation_prix": prix_soutenable(pmt_ref, t) / prix_ref - 1})
281+ return pd.DataFrame(rows), pmt_ref
282+
283+# ----------------------------------------------------------------------------- Ratio prix / loyer annuel
284+def ratio_prix_loyer(prix: float, loyer_mensuel: float) -> float:
285+ return prix / (12 * loyer_mensuel)
286+
287+# ----------------------------------------------------------------------------- Impression
288+def fmt(x, d=0): return f"{x:,.{d}f}".replace(",", " ")
289+
290+def main(verbose: bool = True) -> dict:
291+ res = {}
292+ sp = structure_parc(); res["parc"] = sp
293+ rt = recensement_types(); res["types"] = rt
294+ tot = rt[rt["type_id"] == 1].set_index("geo")
295+ D = indice_dissimilarite(sp["qc_part"].values, sp["ca_part"].values); res["dissimilarite"] = D
296+ mr = menages_rmr(); res["menages_rmr"] = mr
297+ pop = population_rmr(); res["population"] = pop
298+ ip = ipln(); st = ipln_stats(ip); res["ipln"] = ip; res["ipln_stats"] = st
299+ sc = schl_rmr(); res["schl"] = sc
300+ ap = apciq_t3_2026(); res["apciq"] = ap
301+ b = bdc(); res["bdc"] = b
302+ mut = tableau_mutations([500000, 645500, 860500, 1500000]); res["mutations"] = mut
303+ sens, pmt_ref = sensibilite_taux(); res["sensibilite"] = sens; res["paiement_ref"] = pmt_ref
304+ # MRB et prix/loyer implicites
305+ mrb = {r: mrb_implicite(ap.loc[r, "plex"], sc.loc[sc.index[sc.index.str.startswith(r.split(" ")[0])][0], "loyer_2_chambres_2025"])
306+ for r in ["Montréal", "Québec", "Gatineau", "Sherbrooke", "Trois-Rivières", "Saguenay"]}
307+ res["mrb"] = mrb
308+ if not verbose: return res
309+ print("=== Recensement 2021 (98-10-0240-01) — ménages privés")
310+ for g in ("Québec", "Canada"):
311+ print(f" {g} : total {fmt(tot.loc[g,'total'])} ; propriétaires {fmt(tot.loc[g,'proprietaire'])} ({tot.loc[g,'taux_propriete']*100:.1f} %) ; locataires {fmt(tot.loc[g,'locataire'])}")
312+ print(sp[["court", "qc_menages", "qc_part", "qc_taux_propriete", "ca_part", "ca_taux_propriete"]].to_string(float_format=lambda v: f"{v:.4f}"))
313+ print(f" Indice de dissimilarité Québec/Canada (8 types) : {D:.4f}")
314+ agg = rt[(rt["type_id"] == 4)].set_index("geo"); print(f" Agrégat « autre logement attenant » : Québec {fmt(agg.loc['Québec','total'])} ({agg.loc['Québec','part']*100:.1f} %), Canada {agg.loc['Canada','part']*100:.1f} %")
315+ plex_like = sp.loc[[5, 6], "qc_menages"].sum(); print(f" Duplex + appartements < 5 étages, Québec : {fmt(plex_like)} ménages = {plex_like/tot.loc['Québec','total']*100:.1f} %")
316+ print("\n=== Ménages par RMR (98-10-0231-01)"); print(mr.to_string(float_format=lambda v: f"{v:.4f}"))
317+ print("\n=== Population des RMR au 1er juillet (17-10-0148-01)"); print(pop.to_string(float_format=lambda v: f"{v:.4f}"))
318+ print("\n=== IPLN (18-10-0205-01, déc. 2016 = 100)"); print(st.to_string(float_format=lambda v: f"{v:.4f}"))
319+ print("\n=== SCHL — ELL octobre 2025 (fichier rmr-quebec-2025-fr.xlsx, tableaux 1.1.1/1.1.2/1.1.3/1.1.5)")
320+ cols = ["inocc_tous_2024", "inocc_tous_2025", "inocc_2_chambres_2025", "loyer_2_chambres_2024", "loyer_2_chambres_2025", "loyer_tous_2025", "univers_tous_2025",
321+ "varest_2_chambres_2025", "varest_tous_2025", "var_brute_2_chambres", "var_brute_tous"]
322+ print(sc[cols].to_string(float_format=lambda v: f"{v:.4f}"))
323+ print("\n=== APCIQ T3 2026 (chiffres cités) et indicateurs dérivés"); print(ap.to_string(float_format=lambda v: f"{v:.3f}"))
324+ print(" MRB implicite (plex 2-5 log. ↔ 3 logements au loyer moyen 2 ch. SCHL oct. 2025) :", {k: round(v, 2) for k, v in mrb.items()})
325+ print(f" Ratio prix/loyer annuel, Montréal (copropriété 435 000 $ / loyer copropriété locative 2 ch. 1 826 $) : {ratio_prix_loyer(435000, 1826):.1f}")
326+ print("\n=== Banque du Canada (Valet)")
327+ for k, v in b["stats"].items(): print(f" {k} : {v}")
328+ print("\n=== Droits sur les mutations immobilières 2026"); print(mut.to_string(float_format=lambda v: f"{v:.4f}"))
329+ print(f"\n=== Sensibilité au taux : paiement de référence {pmt_ref:.2f} $/mois (prêt 400 000 $, 4,12 %, 25 ans)"); print(sens.to_string(float_format=lambda v: f"{v:.4f}"))
330+ return res
331+
332+if __name__ == "__main__":
333+ main()
added analysis/python/cases/ch06_demembrements.py +280 −0
@@ -0,0 +1,280 @@
1+#!/usr/bin/env python3
2+"""Chapitre 6 — Code civil et droit immobilier : effets des modalités et démembrements du droit de propriété sur la valeur.
3+
4+Imprime tous les chiffres cités dans le chapitre 6 (DONNÉES SYNTHÉTIQUES et hypothèses pédagogiques déclarées) :
5+ * table de mortalité SYNTHÉTIQUE (loi de Gompertz–Makeham à paramètres déclarés ; ce n'est PAS une table de
6+ Statistique Canada) : probabilités de survie et espérances de vie ;
7+ * usufruit à terme fixe et usufruit viager (art. 1120, 1123, 1162 C.c.Q.) sur la maison du cas 1 (Gatineau) :
8+ valeur de l'usufruit U, de la nue-propriété NP, identité U + NP = V, rente viagère équivalente (art. 1171) ;
9+ * emphytéose (art. 1195, 1197, 1208-1210) sur un immeuble locatif dont le RNE est celui du cas 3 (Montréal) :
10+ valeur du droit de l'emphytéote et du droit du propriétaire selon la durée restante ; seuils de 40 ans (D-15.1, art. 1)
11+ et de 50 ans (art. 1040 C.c.Q.) ;
12+ * part indivise (art. 1010, 1015) avec décote déclarée ; servitude de non-construction (art. 1177) par l'élasticité
13+ prix-terrain du modèle hédonique du cas 1 ; rang hypothécaire (art. 2660, 2945) ; droits sur les mutations 2026
14+ (D-15.1, art. 2) ; quantum d'un vice caché (art. 1726-1728) ; budget de recherche au registre foncier.
15+
16+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch06_demembrements.py
17+Les fonctions d'actualisation proviennent de analysis/python/traite/finance.py (aucune réécriture) ; les valeurs des cas
18+proviennent de traite.cas1 et traite.cas3."""
19+from __future__ import annotations
20+import sys, math, pathlib
21+import numpy as np, pandas as pd
22+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
23+from traite import finance as fin
24+
25+ROOT = pathlib.Path(__file__).resolve().parents[3]
26+
27+# ----------------------------------------------------------------------------- hypothèses déclarées
28+# Table de mortalité SYNTHÉTIQUE : force de mortalité de Gompertz–Makeham mu(x) = A + B c^x.
29+# Paramètres pédagogiques choisis pour une espérance de vie à 65 ans de l'ordre de vingt ans (voir table_mortalite()) ;
30+# ils ne reproduisent aucune table officielle. En pratique, utiliser les tables complètes de mortalité de Statistique
31+# Canada (tableau 13-10-0114-01) ou les tables de l'Institut canadien des actuaires, selon la fin de l'évaluation.
32+GM_A, GM_B, GM_C = 0.0004, 0.000025, 1.102
33+OMEGA = 111 # âge limite de la table synthétique (survie nulle au-delà)
34+
35+R_ACTU = 0.05 # taux d'actualisation des flux d'une résidence (hypothèse pédagogique)
36+G_LOYER = 0.015 # croissance annuelle de la valeur locative nette (hypothèse pédagogique)
37+V_ARRONDI = 638_000.0 # valeur de la maison du cas 1 (prédiction hédonique 637 953 $ arrondie au millier)
38+
39+# Emphytéose : immeuble dont le RNE est celui du cas 3 ; monde de Gordon r - g = taux global
40+R_EMPH, G_EMPH = 0.07, 0.025 # r = taux d'actualisation du cas 3 (hypothèses DCF) ; g déclaré
41+RENTE_EMPH = 30_000.0 # rente emphytéotique annuelle fixe (hypothèse pédagogique)
42+N_EMPH = 35 # durée restante de référence (années)
43+
44+# Droits sur les mutations immobilières, tranches 2026 (RLRQ, c. D-15.1, art. 2 ; notes-A § 2.3)
45+TRANCHES_2026 = [(62_900.0, 0.005), (315_000.0, 0.010), (float("inf"), 0.015)]
46+
47+# Registre foncier — grille tarifaire en vigueur à compter du 1er avril 2025 (notes-A § 3.1)
48+TARIFS_REGISTRE = {"consultation_document": 1.0, "inscription": 142.0, "radiation": 162.0,
49+ "etat_certifie": 16.0, "copie_non_certifiee": 26.0, "copie_certifiee": 42.0}
50+
51+
52+# ----------------------------------------------------------------------------- mortalité synthétique
53+
54+def mu(x: float) -> float:
55+ """Force de mortalité synthétique (Gompertz–Makeham)."""
56+ return GM_A + GM_B * GM_C ** x
57+
58+
59+def survie(x: float, t: float) -> float:
60+ """Probabilité tp_x qu'une personne d'âge x soit vivante à l'âge x + t (intégrale exacte de mu)."""
61+ if t <= 0:
62+ return 1.0
63+ if x + t >= OMEGA:
64+ return 0.0
65+ integ = GM_A * t + GM_B * GM_C ** x * (GM_C ** t - 1.0) / math.log(GM_C)
66+ return math.exp(-integ)
67+
68+
69+def esperance_vie(x: float) -> float:
70+ """Espérance de vie abrégée (somme des tp_x, correction d'un demi-an)."""
71+ return sum(survie(x, t) for t in range(1, OMEGA - int(x) + 1)) + 0.5
72+
73+
74+def table_mortalite(ages=(55, 60, 65, 70, 75, 80, 85)) -> pd.DataFrame:
75+ rows = [{"age": a, "esperance_vie": esperance_vie(a), "p10": survie(a, 10), "p20": survie(a, 20), "p30": survie(a, 30)} for a in ages]
76+ return pd.DataFrame(rows).set_index("age")
77+
78+
79+# ----------------------------------------------------------------------------- valeur de la pleine propriété
80+
81+def valeur_cas1() -> dict:
82+ """Prédiction hédonique du sujet du cas 1 (traite.cas1) et valeur arrondie retenue dans le chapitre."""
83+ from traite import cas1
84+ t = cas1.tout()
85+ return {"prediction_hedonique": float(t["prediction_hedonique"]), "V": V_ARRONDI,
86+ "terrain_m2": t["sujet"]["superficie_terrain_m2"], "elasticite_terrain": float(t["modele"]["coef"].loc["ln_T", "coef"])}
87+
88+
89+def loyer_net(V: float, r: float = R_ACTU, g: float = G_LOYER) -> float:
90+ """Valeur locative nette annuelle cohérente avec V dans un monde de Gordon : L = (r - g) V."""
91+ return (r - g) * V
92+
93+
94+# ----------------------------------------------------------------------------- usufruit
95+
96+def usufruit_terme(V: float, n: int, r: float = R_ACTU, g: float = G_LOYER) -> dict:
97+ """Usufruit à terme fixe de n ans : U = somme actualisée des fruits L(1+g)^(t-1), NP = V - U = V((1+g)/(1+r))^n."""
98+ L = loyer_net(V, r, g)
99+ flux = [L * (1 + g) ** (t - 1) for t in range(1, n + 1)]
100+ U = fin.van([0.0] + flux, r)
101+ NP = V - U
102+ return {"n": n, "L": L, "U": U, "NP": NP, "part_NP": NP / V, "NP_formule": V * ((1 + g) / (1 + r)) ** n}
103+
104+
105+def usufruit_viager(V: float, x: int, r: float = R_ACTU, g: float = G_LOYER) -> dict:
106+ """Usufruit viager d'un usufruitier d'âge x (convention : le revenu de l'année t revient à l'usufruitier s'il est vivant au
107+ début de l'année, probabilité (t-1)p_x ; la nue-propriété reçoit la pleine propriété à la fin de l'année du décès)."""
108+ L = loyer_net(V, r, g)
109+ U = 0.0; NP_direct = 0.0; esp = 0.0
110+ for t in range(1, OMEGA - x + 1):
111+ p_debut, p_fin = survie(x, t - 1), survie(x, t)
112+ v = fin.facteur_va(r, t)
113+ U += v * p_debut * L * (1 + g) ** (t - 1)
114+ NP_direct += v * (p_debut - p_fin) * V * (1 + g) ** t
115+ esp += p_fin
116+ return {"age": x, "L": L, "U": U, "NP": V - U, "NP_direct": NP_direct, "part_NP": (V - U) / V,
117+ "esperance_vie": esp + 0.5, "annuite_viagere": U / L}
118+
119+
120+def rente_viagere_equivalente(V: float, x: int, r: float = R_ACTU, g: float = G_LOYER) -> float:
121+ """Rente annuelle constante, versée tant que l'usufruitier vit, de même valeur actuelle que l'usufruit (art. 1171)."""
122+ u = usufruit_viager(V, x, r, g)
123+ a_x = sum(fin.facteur_va(r, t) * survie(x, t - 1) for t in range(1, OMEGA - x + 1))
124+ return u["U"] / a_x
125+
126+
127+def grille_usufruit(V: float, ages=(55, 60, 65, 70, 75, 80, 85), taux=(0.04, 0.05, 0.06), g: float = G_LOYER) -> pd.DataFrame:
128+ """Part de la nue-propriété (NP/V) selon l'âge de l'usufruitier et le taux d'actualisation."""
129+ return pd.DataFrame({f"r={r:.2f}": [usufruit_viager(V, a, r, g)["part_NP"] for a in ages] for r in taux}, index=list(ages))
130+
131+
132+# ----------------------------------------------------------------------------- emphytéose
133+
134+def emphyteose(noi_1: float, rente: float, n: int, r: float = R_EMPH, g: float = G_EMPH, r_prop: float | None = None) -> dict:
135+ """Droits de l'emphytéote et du propriétaire pour une durée restante de n ans.
136+ Monde de Gordon : V_pleine = NOI_1/(r-g) ; NOI_t = NOI_1 (1+g)^(t-1) ; reprise à la fin de l'année n à V_n = V_pleine (1+g)^n.
137+ Si r_prop est donné, les flux du propriétaire (rente + reprise) sont actualisés à ce taux (sinon à r : la somme vaut V_pleine)."""
138+ V = noi_1 / (r - g)
139+ rp = r if r_prop is None else r_prop
140+ flux_emph = [noi_1 * (1 + g) ** (t - 1) - rente for t in range(1, n + 1)]
141+ V_emph = fin.van([0.0] + flux_emph, r)
142+ V_rentes = rente * fin.facteur_va_annuite(rp, n)
143+ V_reprise = V * (1 + g) ** n * fin.facteur_va(rp, n)
144+ V_prop = V_rentes + V_reprise
145+ return {"n": n, "V_pleine": V, "V_emph": V_emph, "V_rentes": V_rentes, "V_reprise": V_reprise, "V_prop": V_prop,
146+ "somme": V_emph + V_prop, "part_emph": V_emph / V, "rente": rente, "noi_1": noi_1}
147+
148+
149+def grille_emphyteose(noi_1: float, rente: float, durees=(60, 50, 45, 40, 35, 30, 20, 10, 5), **kw) -> pd.DataFrame:
150+ return pd.DataFrame([emphyteose(noi_1, rente, n, **kw) for n in durees]).set_index("n")
151+
152+
153+def rne_cas3() -> float:
154+ """RNE normalisé de première année du cas 3 (traite.cas3), seul chiffre du cas repris ici."""
155+ from traite import cas3
156+ return float(cas3.tout()["base"]["rne"])
157+
158+
159+# ----------------------------------------------------------------------------- indivision, servitude, hypothèque, mutations, vice
160+
161+def part_indivise(V: float, quote_part: float, decote: float) -> dict:
162+ """Valeur d'une part indivise : quote-part de la valeur du tout, diminuée d'une décote d'illiquidité déclarée."""
163+ brute = quote_part * V
164+ return {"quote_part": quote_part, "valeur_proportionnelle": brute, "decote": decote, "valeur_part": brute * (1 - decote)}
165+
166+
167+def servitude_terrain(V: float, terrain_m2: float, grevee_m2: float, elasticite: float) -> dict:
168+ """Effet d'une servitude qui soustrait grevee_m2 à l'usage du terrain, par l'élasticité prix-terrain du modèle hédonique :
169+ V_avec = V ((T - T_g)/T)^beta ; approximation déclarée (la servitude n'est pas une réduction de lot)."""
170+ ratio = (terrain_m2 - grevee_m2) / terrain_m2
171+ V_avec = V * ratio ** elasticite
172+ return {"terrain_m2": terrain_m2, "grevee_m2": grevee_m2, "elasticite": elasticite, "V_sans": V, "V_avec": V_avec,
173+ "delta": V - V_avec, "delta_pct": 1 - V_avec / V}
174+
175+
176+def rang_hypothecaire(produit_net: float, creances: list[float]) -> list[dict]:
177+ """Distribution du produit net d'une vente sous contrôle de justice selon le rang (art. 2660, 2945)."""
178+ reste = produit_net; out = []
179+ for i, c in enumerate(creances, 1):
180+ paye = min(c, max(reste, 0.0)); reste -= paye
181+ out.append({"rang": i, "creance": c, "recouvre": paye, "perte": c - paye, "taux_recouvrement": paye / c if c else 1.0})
182+ out.append({"rang": "débiteur", "creance": 0.0, "recouvre": max(reste, 0.0), "perte": 0.0, "taux_recouvrement": float("nan")})
183+ return out
184+
185+
186+def droit_mutation(base: float, tranches=TRANCHES_2026, taux_municipal: list[tuple[float, float]] | None = None) -> dict:
187+ """Droit sur les mutations immobilières (D-15.1, art. 2) : taux par tranche sur la base d'imposition.
188+ taux_municipal : liste optionnelle de (seuil_inferieur, taux) remplaçant 1,5 % au-dessus de 500 000 $ (facultatif, max 3 %)."""
189+ detail = []; prec = 0.0; total = 0.0
190+ for plafond, taux in tranches:
191+ if base <= prec:
192+ break
193+ assiette = min(base, plafond) - prec
194+ montant = assiette * taux
195+ detail.append({"de": prec, "a": min(base, plafond), "taux": taux, "montant": montant}); total += montant; prec = plafond
196+ supplement = 0.0
197+ if taux_municipal:
198+ for seuil, taux in taux_municipal:
199+ if base > seuil:
200+ borne_sup = min([s for s, _ in taux_municipal if s > seuil] + [float("inf")])
201+ supplement += (min(base, borne_sup) - seuil) * (taux - 0.015)
202+ return {"base": base, "detail": detail, "droit_base": total, "supplement_municipal": supplement, "total": total + supplement,
203+ "taux_effectif": (total + supplement) / base}
204+
205+
206+def quantum_vice(prix: float, cout_reparation: float, plus_value: float, prejudice_autre: float = 0.0, vendeur_connaissait: bool = False) -> dict:
207+ """Mesure usuelle du préjudice d'un vice caché : coût des réparations diminué de la plus-value qu'elles procurent
208+ (réduction de prix, art. 1726-1727) ; dommages-intérêts additionnels seulement si le vendeur connaissait le vice (art. 1728)."""
209+ reduction = cout_reparation - plus_value
210+ dommages = prejudice_autre if vendeur_connaissait else 0.0
211+ return {"prix": prix, "reduction_prix": reduction, "dommages": dommages, "total": reduction + dommages,
212+ "prix_ajuste": prix - reduction, "part_prix": reduction / prix}
213+
214+
215+def budget_registre(nb_comparables: int, documents_par_vente: int = 3) -> dict:
216+ """Budget de consultation du registre foncier pour vérifier des ventes comparables (1 $ par document)."""
217+ n_doc = nb_comparables * documents_par_vente
218+ return {"documents": n_doc, "cout": n_doc * TARIFS_REGISTRE["consultation_document"]}
219+
220+
221+# ----------------------------------------------------------------------------- impression
222+
223+def _d(x: float) -> str:
224+ return f"{x:,.0f} $".replace(",", " ")
225+
226+
227+def main() -> None:
228+ print("=== Chapitre 6 — démembrements et modalités (DONNÉES SYNTHÉTIQUES / hypothèses déclarées) ===\n")
229+ tm = table_mortalite()
230+ print("Table de mortalité SYNTHÉTIQUE (Gompertz–Makeham A=%.4f B=%.6f c=%.3f) :" % (GM_A, GM_B, GM_C))
231+ print(tm.round(3).to_string(), "\n")
232+
233+ c1 = valeur_cas1()
234+ V = c1["V"]
235+ print(f"Cas 1 : prédiction hédonique {c1['prediction_hedonique']:.2f} $ → V retenu {_d(V)} ; L = (r-g)V = {_d(loyer_net(V))} / an "
236+ f"({loyer_net(V)/12:.0f} $/mois) avec r = {R_ACTU:.1%}, g = {G_LOYER:.1%}\n")
237+
238+ for n in (10, 20, 30):
239+ u = usufruit_terme(V, n)
240+ print(f"Usufruit à terme {n} ans : U = {_d(u['U'])} ; NP = {_d(u['NP'])} ({u['part_NP']:.1%}) ; vérif. NP formule {_d(u['NP_formule'])}")
241+ print()
242+ for x in (55, 65, 75, 85):
243+ u = usufruit_viager(V, x)
244+ print(f"Usufruit viager, {x} ans : e_x = {u['esperance_vie']:.1f} ; U = {_d(u['U'])} ; NP = {_d(u['NP'])} ({u['part_NP']:.1%}) ; "
245+ f"NP direct = {_d(u['NP_direct'])} ; annuité viagère {u['annuite_viagere']:.3f} ; rente équiv. {_d(rente_viagere_equivalente(V, x))}/an")
246+ print("\nGrille NP/V (âge × r) :"); print(grille_usufruit(V).round(3).to_string(), "\n")
247+
248+ noi = rne_cas3()
249+ e = emphyteose(noi, RENTE_EMPH, N_EMPH)
250+ print(f"Emphytéose : RNE_1 cas 3 = {noi:.2f} $ ; r = {R_EMPH:.1%}, g = {G_EMPH:.1%} → V pleine = {_d(e['V_pleine'])} ; rente {_d(RENTE_EMPH)}/an ; n = {N_EMPH}")
251+ print(f" droit de l'emphytéote {_d(e['V_emph'])} ({e['part_emph']:.1%}) ; propriétaire : rentes {_d(e['V_rentes'])} + reprise {_d(e['V_reprise'])} = {_d(e['V_prop'])} ; somme {_d(e['somme'])}")
252+ e2 = emphyteose(noi, RENTE_EMPH, N_EMPH, r_prop=0.055)
253+ print(f" avec r_prop = 5,5 % : propriétaire {_d(e2['V_prop'])} ; somme {_d(e2['somme'])} (> V pleine de {_d(e2['somme']-e2['V_pleine'])})")
254+ print("\nGrille emphytéose selon la durée restante :")
255+ ge = grille_emphyteose(noi, RENTE_EMPH)[["V_emph", "V_rentes", "V_reprise", "V_prop", "part_emph"]].copy()
256+ ge["part_emph"] = (ge["part_emph"] * 100).round(1); ge[["V_emph", "V_rentes", "V_reprise", "V_prop"]] = ge[["V_emph", "V_rentes", "V_reprise", "V_prop"]].round(0)
257+ print(ge.to_string(), "\n")
258+
259+ for q, d in ((0.5, 0.0), (0.5, 0.10), (0.5, 0.20), (1 / 3, 0.15)):
260+ p = part_indivise(V, q, d)
261+ print(f"Part indivise {q:.3f} décote {d:.0%} : {_d(p['valeur_proportionnelle'])} → {_d(p['valeur_part'])}")
262+ s = servitude_terrain(V, c1["terrain_m2"], 120, c1["elasticite_terrain"])
263+ print(f"\nServitude de non-construction sur 120 m² de {c1['terrain_m2']} m² (élasticité {s['elasticite']:.4f}) : V_avec = {_d(s['V_avec'])} ; ΔV = {_d(s['delta'])} ({s['delta_pct']:.2%})")
264+ print("\nRang hypothécaire, produit net 560 000 $, créances 455 000 / 90 000 / 40 000 :")
265+ for r in rang_hypothecaire(560_000, [455_000, 90_000, 40_000]):
266+ print(" ", r)
267+ m = droit_mutation(V)
268+ print(f"\nDroit de mutation 2026 sur {_d(V)} : " + " ; ".join(f"{t['taux']:.1%} sur {_d(t['a']-t['de'])} = {t['montant']:.2f} $" for t in m["detail"]) +
269+ f" → {m['total']:.2f} $ ({m['taux_effectif']:.3%})")
270+ m2 = droit_mutation(750_000, taux_municipal=[(500_000, 0.02)])
271+ print(f"Droit de mutation 2026 sur 750 000 $ avec taux municipal 2 % au-delà de 500 000 $ : base {m2['droit_base']:.2f} + {m2['supplement_municipal']:.2f} = {m2['total']:.2f} $")
272+ qv = quantum_vice(520_000, 46_000, 9_000, 6_500, True)
273+ print(f"\nVice caché : prix 520 000 $, réparations 46 000 $, plus-value 9 000 $ → réduction {_d(qv['reduction_prix'])} ({qv['part_prix']:.1%}) ; "
274+ f"si vendeur connaissait : + {_d(qv['dommages'])} = {_d(qv['total'])}")
275+ b = budget_registre(5)
276+ print(f"\nRegistre foncier : 5 comparables × 3 documents = {b['documents']} documents → {b['cout']:.0f} $ (1 $/document)")
277+
278+
279+if __name__ == "__main__":
280+ main()
added analysis/python/cases/ch07_servitudes_copro.py +280 −0
@@ -0,0 +1,280 @@
1+#!/usr/bin/env python3
2+"""Chapitre 7 — Cadastre, titres, servitudes et copropriété : exemples chiffrés (DONNÉES SYNTHÉTIQUES, graine 20261031).
3+
4+Trois blocs, tous générés dans ce script (aucun fichier externe, aucune statistique de marché) :
5+ A. Effet d'une servitude de passage mesuré par ventes appariées (traite.comparables.ventes_appariees) ;
6+ comparaison avec le « modèle de la bande » (perte d'utilité d'une bande de terrain grevée).
7+ B. Copropriété divise de huit fractions : valeur relative (art. 1041, 1053 C.c.Q.), quotes-parts, répartition
8+ des charges communes générales et des charges des parties communes à usage restreint (art. 1064 C.c.Q.).
9+ C. Fonds de prévoyance (art. 1071 C.c.Q. ; CCQ, r. 8.01, art. 3 et 7-9) : plan de réparations majeures sur 25 ans,
10+ cotisation annuelle indexée nécessaire (méthode du seuil), comparaison avec la règle du 0,5 % de la valeur de
11+ reconstruction et avec la cotisation actuelle, déficit de capitalisation et incidence sur la valeur d'une fraction.
12+
13+Toutes les fonctions financières proviennent de analysis/python/traite/ (finance.py, comparables.py).
14+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch07_servitudes_copro.py
15+"""
16+from __future__ import annotations
17+import sys, math, pathlib
18+import numpy as np
19+from scipy import stats
20+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
21+from traite import comparables as cp
22+from traite import finance as fin
23+
24+SEED = 20261031
25+
26+# ----------------------------------------------------------------------------- A. servitude par paires
27+N_PAIRES = 12
28+PRIX_MEDIAN_SANS = 445_000.0 # $ ; niveau pédagogique (bungalow de banlieue fictif, terrain 700 m²)
29+SIGMA_PAIRE = 0.08 # dispersion log des prix « sans servitude » entre paires
30+DELTA_SERVITUDE = 0.045 # effet « vrai » de la servitude de passage (proportion du prix) — hypothèse pédagogique
31+SIGMA_BRUIT = 0.025 # bruit de transaction résiduel au sein d'une paire
32+TERRAIN_M2 = 700.0
33+BANDE_LARGEUR_M, BANDE_LONGUEUR_M = 3.0, 40.0 # bande de passage grevée
34+PRIX_TERRAIN_M2 = 180.0 # $/m² de terrain, prix unitaire pédagogique du secteur fictif
35+PERTE_UTILITE = 0.50 # proportion de l'utilité de la bande perdue par le fonds servant (passage non exclusif)
36+
37+
38+def paires_servitude(n: int = N_PAIRES, seed: int = SEED) -> dict:
39+ """Génère n paires (sans, avec servitude de passage) et extrait l'effet par ventes appariées."""
40+ rng = np.random.default_rng(seed)
41+ sans = np.exp(np.log(PRIX_MEDIAN_SANS) + rng.normal(0.0, SIGMA_PAIRE, n))
42+ avec = sans * (1.0 - DELTA_SERVITUDE) * np.exp(rng.normal(0.0, SIGMA_BRUIT, n))
43+ sans = np.round(sans / 500.0) * 500.0
44+ avec = np.round(avec / 500.0) * 500.0
45+ va = cp.ventes_appariees(list(avec), list(sans)) # différences = avec − sans (négatives)
46+ diffs = np.array(va["differences"])
47+ se = va["ecart_type"] / math.sqrt(n)
48+ t_crit = float(stats.t.ppf(0.975, n - 1))
49+ ratios = avec / sans
50+ beta = float(np.mean(np.log(ratios)))
51+ return {
52+ "n": n, "sans": sans, "avec": avec, "diffs": diffs,
53+ "moyenne": va["moyenne"], "mediane": va["mediane"], "ecart_type": va["ecart_type"], "se": se,
54+ "t": va["moyenne"] / se, "t_crit": t_crit,
55+ "ic95": (va["moyenne"] - t_crit * se, va["moyenne"] + t_crit * se),
56+ "effet_relatif_moyen": float(np.mean(ratios - 1.0)), # moyenne des (avec/sans − 1)
57+ "effet_relatif_ratio": float(avec.mean() / sans.mean() - 1.0), # rapport des moyennes
58+ "beta_log": beta, "effet_semilog": cp.ajustement_depuis_coefficient_log(beta),
59+ "ic95_relatif": ((va["moyenne"] - t_crit * se) / sans.mean(), (va["moyenne"] + t_crit * se) / sans.mean()),
60+ }
61+
62+
63+def modele_bande(prix_terrain_m2: float = PRIX_TERRAIN_M2, largeur: float = BANDE_LARGEUR_M,
64+ longueur: float = BANDE_LONGUEUR_M, perte: float = PERTE_UTILITE) -> dict:
65+ """Effet d'une servitude = superficie grevée × prix unitaire du terrain × proportion d'utilité perdue."""
66+ s = largeur * longueur
67+ return {"superficie_bande_m2": s, "valeur_bande": s * prix_terrain_m2, "effet": s * prix_terrain_m2 * perte,
68+ "part_terrain": s / TERRAIN_M2}
69+
70+
71+# ----------------------------------------------------------------------------- B. copropriété : quotes-parts et charges
72+FRACTIONS = [ # (nom, superficie privative m², étage, orientation, stationnement intérieur à usage restreint)
73+ ("F1", 72.0, "RDC", "nord", False), ("F2", 98.0, "RDC", "sud", True),
74+ ("F3", 72.0, "2e", "nord", False), ("F4", 98.0, "2e", "sud", True),
75+ ("F5", 85.0, "3e", "nord", False), ("F6", 110.0, "3e", "sud", True),
76+ ("F7", 85.0, "2e", "est", True), ("F8", 90.0, "3e", "ouest", False),
77+]
78+COEF_ETAGE = {"RDC": 0.96, "2e": 1.00, "3e": 1.04} # méthode déclarée dans l'acte constitutif (fictif)
79+COEF_ORIENTATION = {"nord": 0.97, "est": 1.00, "ouest": 1.00, "sud": 1.03}
80+BASE_QUOTE = 10_000 # quotes-parts exprimées en dix-millièmes
81+
82+CHARGES_COURANTES = { # budget annuel des charges communes générales, hors fonds (dollars de 2026, fictif)
83+ "Assurance de l'immeuble": 12_000.0, "Entretien et réparations courantes": 8_500.0,
84+ "Déneigement et entretien paysager": 5_200.0, "Énergie des parties communes": 4_800.0,
85+ "Gestion et honoraires professionnels": 7_200.0, "Administration et frais divers": 2_300.0,
86+}
87+FRANCHISE_MAX = 10_000.0 # plus haute franchise de la police du syndicat (dégâts d'eau) — fictif
88+FONDS_AUTO_ASSURANCE_SOLDE = 0.0 # fonds d'auto assurance non capitalisé → contribution = moitié de la franchise (r. 4.1, art. 2)
89+ENTRETIEN_STATIONNEMENTS = 3_200.0 # entretien courant des 4 stationnements intérieurs (partie commune à usage restreint)
90+
91+
92+def quotes_parts(fractions=FRACTIONS) -> dict:
93+ """Valeur relative v_i = S_i × c_étage × c_orientation ; quote-part q_i = v_i / Σ v, en dix-millièmes (somme exacte = 10 000)."""
94+ v = np.array([s * COEF_ETAGE[e] * COEF_ORIENTATION[o] for _, s, e, o, _ in fractions])
95+ q = v / v.sum()
96+ brut = q * BASE_QUOTE
97+ ent = np.floor(brut).astype(int)
98+ reste = BASE_QUOTE - ent.sum()
99+ ordre = np.argsort(-(brut - ent)) # plus forts restes
100+ ent[ordre[:reste]] += 1
101+ return {"noms": [f[0] for f in fractions], "superficies": np.array([f[1] for f in fractions]),
102+ "valeur_relative": v, "q": ent / BASE_QUOTE, "q_dixmilliemes": ent, "q_brut": q,
103+ "stationnement": np.array([f[4] for f in fractions])}
104+
105+
106+def contribution_auto_assurance(franchise_max: float = FRANCHISE_MAX, solde: float = FONDS_AUTO_ASSURANCE_SOLDE) -> float:
107+ """CCQ, r. 4.1, art. 2 : moitié de la plus haute franchise si le fonds est capitalisé à moins de la moitié ; différence
108+ entre la franchise et le solde si entre la moitié et la franchise ; nulle au-delà (franchises séisme/inondation exclues)."""
109+ if solde <= franchise_max / 2:
110+ return franchise_max / 2
111+ if solde < franchise_max:
112+ return franchise_max - solde
113+ return 0.0
114+
115+
116+def repartition_charges(cotisation_fonds_prevoyance: float, qp: dict | None = None) -> dict:
117+ """Charges annuelles de chaque fraction : (courantes + fonds de prévoyance + fonds d'auto assurance) × q_i
118+ + part égale de l'entretien des stationnements pour leurs seuls usagers (art. 1064 C.c.Q.)."""
119+ qp = qp or quotes_parts()
120+ courantes = sum(CHARGES_COURANTES.values())
121+ auto = contribution_auto_assurance()
122+ general = courantes + cotisation_fonds_prevoyance + auto
123+ n_stat = int(qp["stationnement"].sum())
124+ part_stat = np.where(qp["stationnement"], ENTRETIEN_STATIONNEMENTS / n_stat, 0.0)
125+ q = qp["q"]
126+ return {"courantes": courantes, "fonds_prevoyance": cotisation_fonds_prevoyance, "auto_assurance": auto, "general": general,
127+ "par_fraction_courantes": q * courantes, "par_fraction_fonds": q * cotisation_fonds_prevoyance,
128+ "par_fraction_auto": q * auto, "par_fraction_general": q * general, "par_fraction_restreint": part_stat,
129+ "par_fraction_total": q * general + part_stat, "total": general + ENTRETIEN_STATIONNEMENTS, "n_stationnements": n_stat}
130+
131+
132+# ----------------------------------------------------------------------------- C. fonds de prévoyance
133+VALEUR_RECONSTRUCTION = 4_200_000.0 # $ ; montant d'assurance (art. 1073 C.c.Q.), fixé par un évaluateur agréé (r. 4.1, art. 3) — fictif
134+HORIZON = 25 # années (CCQ, r. 8.01, art. 3 : « minimalement les 25 prochaines années »)
135+INFLATION = 0.025 # hausse annuelle des coûts de construction retenue dans l'étude (hypothèse pédagogique)
136+RENDEMENT = 0.020 # rendement des placements à capital garanti (art. 1071 : « placé … à capital garanti »)
137+SOLDE_INITIAL = 85_000.0 # solde du fonds de prévoyance au début de l'année 1 (2026) — fictif
138+COTISATION_ACTUELLE = 12_000.0 # cotisation annuelle votée jusqu'ici (fictif) — insuffisante, on le verra
139+COMPOSANTES = [ # (composante, coût en dollars de 2026, années prévues 1..25)
140+ ("Peinture et éclairage des corridors", 18_000.0, (4, 13, 22)),
141+ ("Maçonnerie et balcons", 55_000.0, (5,)),
142+ ("Toiture (membrane)", 75_000.0, (7,)),
143+ ("Pavage et drainage du stationnement", 28_000.0, (9, 21)),
144+ ("Chauffage et ventilation des parties communes", 42_000.0, (12,)),
145+ ("Fenêtres et portes-fenêtres", 140_000.0, (14,)),
146+ ("Portes de garage et équipements du stationnement", 22_000.0, (16,)),
147+ ("Entrée d'eau et drains", 35_000.0, (19,)),
148+]
149+
150+
151+def depenses_prevues(composantes=COMPOSANTES, horizon: int = HORIZON, inflation: float = INFLATION) -> np.ndarray:
152+ """D_t, t = 1..horizon : coûts indexés C_0 (1+π)^t des réparations majeures prévues l'année t."""
153+ d = np.zeros(horizon + 1)
154+ for _, c0, annees in composantes:
155+ for t in annees:
156+ if 1 <= t <= horizon:
157+ d[t] += c0 * (1.0 + inflation) ** t
158+ return d
159+
160+
161+def trajectoire(cotisation_an1: float, solde0: float = SOLDE_INITIAL, rendement: float = RENDEMENT, inflation: float = INFLATION,
162+ horizon: int = HORIZON, composantes=COMPOSANTES, indexer: bool = True) -> dict:
163+ """F_t = F_{t-1}(1+r) + A_t − D_t, avec A_t = A_1 (1+π)^{t−1} si indexée (convention de l'étude), flux en fin d'année."""
164+ d = depenses_prevues(composantes, horizon, inflation)
165+ F = np.zeros(horizon + 1); F[0] = solde0
166+ A = np.zeros(horizon + 1)
167+ for t in range(1, horizon + 1):
168+ A[t] = cotisation_an1 * ((1.0 + inflation) ** (t - 1) if indexer else 1.0)
169+ F[t] = F[t - 1] * (1.0 + rendement) + A[t] - d[t]
170+ return {"F": F, "A": A, "D": d, "min": float(F[1:].min()), "annee_min": int(np.argmin(F[1:]) + 1),
171+ "premiere_annee_negative": next((t for t in range(1, horizon + 1) if F[t] < 0), None)}
172+
173+
174+def cotisation_seuil(plancher: float = 0.0, solde0: float = SOLDE_INITIAL, rendement: float = RENDEMENT, inflation: float = INFLATION,
175+ horizon: int = HORIZON, composantes=COMPOSANTES, indexer: bool = True) -> float:
176+ """Méthode du seuil : plus petite cotisation A_1 (indexée) telle que le solde ne descende jamais sous `plancher`."""
177+ lo, hi = 0.0, 500_000.0
178+ for _ in range(200):
179+ mid = 0.5 * (lo + hi)
180+ if trajectoire(mid, solde0, rendement, inflation, horizon, composantes, indexer)["min"] >= plancher:
181+ hi = mid
182+ else:
183+ lo = mid
184+ return hi
185+
186+
187+def solde_initial_requis(cotisation: float = COTISATION_ACTUELLE, plancher: float = 0.0, **kw) -> float:
188+ """Solde d'ouverture qui rendrait la cotisation actuelle suffisante (sert à mesurer le déficit de capitalisation)."""
189+ lo, hi = 0.0, 5_000_000.0
190+ for _ in range(200):
191+ mid = 0.5 * (lo + hi)
192+ if trajectoire(cotisation, mid, **kw)["min"] >= plancher:
193+ hi = mid
194+ else:
195+ lo = mid
196+ return hi
197+
198+
199+def fonds_composante_unique(cout_actuel: float, annees: int, inflation: float = INFLATION, rendement: float = RENDEMENT) -> dict:
200+ """Vérification par le facteur de fonds d'amortissement : versement constant qui accumule C_0 (1+π)^T en T ans au taux r."""
201+ cT = cout_actuel * (1.0 + inflation) ** annees
202+ sff = fin.facteur_fonds_amortissement(rendement, annees)
203+ return {"cout_indexe": cT, "sff": sff, "versement": cT * sff}
204+
205+
206+def sensibilite_cotisation(inflations=(0.015, 0.020, 0.025, 0.030, 0.035), rendements=(0.010, 0.020, 0.030), plancher: float = 0.0) -> dict:
207+ grille = {r: [cotisation_seuil(plancher, inflation=p, rendement=r) for p in inflations] for r in rendements}
208+ return {"inflations": list(inflations), "rendements": list(rendements), "grille": grille}
209+
210+
211+def incidence_valeur(qp: dict | None = None, cotisation: float = COTISATION_ACTUELLE) -> dict:
212+ """Déficit de capitalisation D_0 = solde requis − solde réel ; ajustement de la fraction i = −q_i × D_0 (hypothèse : capitalisation 1 pour 1)."""
213+ qp = qp or quotes_parts()
214+ requis = solde_initial_requis(cotisation)
215+ deficit = requis - SOLDE_INITIAL
216+ return {"solde_requis": requis, "deficit": deficit, "ajustement_par_fraction": -qp["q"] * deficit}
217+
218+
219+# ----------------------------------------------------------------------------- impression
220+def fmt(x): return f"{x:,.0f}".replace(",", " ")
221+
222+
223+def main() -> dict:
224+ out = {}
225+ # A
226+ p = paires_servitude(); out["paires"] = p
227+ print("== A. Servitude de passage — ventes appariées (n = %d, graine %d)" % (p["n"], SEED))
228+ for i, (s, a, d) in enumerate(zip(p["sans"], p["avec"], p["diffs"]), 1):
229+ print(f" paire {i:2d} : sans {fmt(s):>9} $ avec {fmt(a):>9} $ diff {d:>9,.0f} $ ratio {a/s-1:+.3%}")
230+ print(f" moyenne {p['moyenne']:,.0f} $ ; médiane {p['mediane']:,.0f} $ ; écart-type {p['ecart_type']:,.0f} $ ; erreur-type {p['se']:,.0f} $")
231+ print(f" t = {p['t']:.2f} (t crit. {p['t_crit']:.3f}) ; IC 95 % [{p['ic95'][0]:,.0f} ; {p['ic95'][1]:,.0f}] $")
232+ print(f" effet relatif : moyenne des ratios {p['effet_relatif_moyen']:.4%} ; rapport des moyennes {p['effet_relatif_ratio']:.4%} ; semi-log exp(β)−1 = {p['effet_semilog']:.4%} (β = {p['beta_log']:.5f})")
233+ print(f" IC 95 % relatif [{p['ic95_relatif'][0]:.2%} ; {p['ic95_relatif'][1]:.2%}] ; moyenne des prix sans {p['sans'].mean():,.0f} $")
234+ b = modele_bande(); out["bande"] = b
235+ print(f" modèle de la bande : {b['superficie_bande_m2']:.0f} m² × {PRIX_TERRAIN_M2:.0f} $/m² = {b['valeur_bande']:,.0f} $ × {PERTE_UTILITE:.0%} = {b['effet']:,.0f} $ ({b['part_terrain']:.1%} du terrain)")
236+ # B
237+ qp = quotes_parts(); out["qp"] = qp
238+ print("\n== B. Copropriété divise — valeur relative et quotes-parts (dix-millièmes)")
239+ for n, s, v, q in zip(qp["noms"], qp["superficies"], qp["valeur_relative"], qp["q_dixmilliemes"]):
240+ print(f" {n} : {s:5.0f} m² v = {v:8.2f} q = {q:5d}/10 000")
241+ print(f" Σ v = {qp['valeur_relative'].sum():.2f} ; Σ q = {qp['q_dixmilliemes'].sum()} ; Σ S = {qp['superficies'].sum():.0f} m²")
242+ # C
243+ A_star = cotisation_seuil(); out["A_star"] = A_star
244+ A_floor = cotisation_seuil(plancher=25_000.0); out["A_floor"] = A_floor
245+ regle = 0.005 * VALEUR_RECONSTRUCTION; out["regle_0_5"] = regle
246+ tr_act = trajectoire(COTISATION_ACTUELLE); tr_star = trajectoire(A_star); tr_regle = trajectoire(regle)
247+ out.update({"tr_act": tr_act, "tr_star": tr_star, "tr_regle": tr_regle})
248+ D = tr_star["D"]
249+ print("\n== C. Fonds de prévoyance — plan sur %d ans, π = %.1f %%, r = %.1f %%, F0 = %s $" % (HORIZON, INFLATION * 100, RENDEMENT * 100, fmt(SOLDE_INITIAL)))
250+ tot0 = 0.0
251+ for nom, c0, annees in COMPOSANTES:
252+ for t in annees:
253+ print(f" an {t:2d} ({2026 + t - 1}) : {nom:48s} {fmt(c0):>8} $ → indexé {fmt(c0 * (1 + INFLATION) ** t):>8} $")
254+ tot0 += c0
255+ print(f" total des dépenses prévues : {fmt(tot0)} $ (dollars de 2026) ; {fmt(D.sum())} $ (dollars courants)")
256+ print(f" cotisation actuelle {fmt(COTISATION_ACTUELLE)} $ : solde minimal {tr_act['min']:,.0f} $ (an {tr_act['annee_min']}), premier solde négatif an {tr_act['premiere_annee_negative']} ; solde an 25 {tr_act['F'][-1]:,.0f} $")
257+ print(f" règle 0,5 % × {fmt(VALEUR_RECONSTRUCTION)} = {fmt(regle)} $ : solde minimal {tr_regle['min']:,.0f} $ (an {tr_regle['annee_min']}), premier négatif an {tr_regle['premiere_annee_negative']}")
258+ print(f" cotisation de seuil A* (plancher 0) = {A_star:,.0f} $ (an 1, indexée à {INFLATION:.1%}) ; solde minimal {tr_star['min']:,.2f} $ an {tr_star['annee_min']} ; solde an 25 {tr_star['F'][-1]:,.0f} $ ; an 25 cotisation {tr_star['A'][-1]:,.0f} $")
259+ print(f" cotisation de seuil avec plancher 25 000 $ = {A_floor:,.0f} $")
260+ fc = fonds_composante_unique(75_000.0, 7); out["toiture"] = fc
261+ print(f" vérification toiture seule : {fmt(75000)} $ × 1,025^7 = {fc['cout_indexe']:,.0f} $ ; SFF(2 %, 7) = {fc['sff']:.5f} ; versement {fc['versement']:,.0f} $")
262+ rep = repartition_charges(A_star, qp); out["rep"] = rep
263+ print(f"\n== B'. Répartition des charges avec A* : courantes {fmt(rep['courantes'])} $ + fonds {fmt(rep['fonds_prevoyance'])} $ + auto-assurance {fmt(rep['auto_assurance'])} $ = {fmt(rep['general'])} $ ; stationnements {fmt(ENTRETIEN_STATIONNEMENTS)} $ / {rep['n_stationnements']}")
264+ for i, n in enumerate(qp["noms"]):
265+ print(f" {n} : q {qp['q'][i]:.4f} courantes {rep['par_fraction_courantes'][i]:8,.0f} fonds {rep['par_fraction_fonds'][i]:8,.0f} auto {rep['par_fraction_auto'][i]:6,.0f} restreint {rep['par_fraction_restreint'][i]:6,.0f} total {rep['par_fraction_total'][i]:8,.0f} $/an = {rep['par_fraction_total'][i]/12:6,.0f} $/mois")
266+ print(f" total {rep['total']:,.0f} $ ; contrôle Σ = {rep['par_fraction_total'].sum():,.2f} $")
267+ sens = sensibilite_cotisation(); out["sens"] = sens
268+ print("\n== Sensibilité de A* (plancher 0) : lignes r, colonnes π")
269+ print(" " + " ".join(f"{p:.1%}" for p in sens["inflations"]))
270+ for r, row in sens["grille"].items():
271+ print(f" r={r:.1%} " + " ".join(f"{a:7,.0f}" for a in row))
272+ inc = incidence_valeur(qp); out["incidence"] = inc
273+ print(f"\n== Incidence sur la valeur : solde requis avec {fmt(COTISATION_ACTUELLE)} $ = {inc['solde_requis']:,.0f} $ ; déficit {inc['deficit']:,.0f} $")
274+ for n, a in zip(qp["noms"], inc["ajustement_par_fraction"]):
275+ print(f" {n} : ajustement {a:,.0f} $")
276+ return out
277+
278+
279+if __name__ == "__main__":
280+ main()
added analysis/python/cases/ch08_zonage.py +351 −0
@@ -0,0 +1,351 @@
1+#!/usr/bin/env python3
2+"""Chapitre 8 — Zonage et urbanisme : DONNÉES SYNTHÉTIQUES et calculs de l'exemple intégral.
3+
4+Tout est fictif : la « municipalité de Val-Fictif » est une grille de 20 × 20 lots répartie en cinq zones
5+(H1 unifamiliale, H2 2 à 4 logements, H3 multifamiliale, C mixte, P parc/institutionnel). Un îlot de 36 lots
6+de la zone H1 est rezoné H2 le 1er janvier 2025 (T0). Les ventes (une ou deux par lot résidentiel) suivent
7+une loi déclarée ci-dessous ; l'effet du rezonage y est injecté sous la forme d'une prime croissante avec
8+l'âge de la maison (option de redéveloppement : plus la maison est vieille, plus le terrain domine la valeur).
9+
10+Le script imprime tous les chiffres cités dans manuscript/chapters/ch08.tex et écrit, s'il y a lieu,
11+data/synthetic/ch08_zonage_lots.csv et data/synthetic/ch08_zonage_ventes.csv (graine 20261031).
12+
13+Usage : python3 analysis/python/cases/ch08_zonage.py
14+"""
15+from __future__ import annotations
16+import sys, pathlib, math, json
17+from datetime import date, timedelta
18+import numpy as np
19+import pandas as pd
20+
21+ROOT = pathlib.Path(__file__).resolve().parents[3]
22+sys.path.insert(0, str(ROOT / "analysis/python"))
23+from traite import comparables as cp # noqa: E402
24+
25+SEED = 20261031
26+T0 = date(2025, 1, 1) # entrée en vigueur du rezonage H1 → H2
27+DATE_DEBUT, DATE_FIN = date(2023, 1, 1), date(2026, 8, 31)
28+DATE_REF = date(2024, 7, 1) # référence de l'indice temporel
29+TAUX_MARCHE = 0.03 # croissance annuelle composée des prix (hypothèse)
30+ELAST_HAB, ELAST_TERR = 0.50, 0.15 # élasticités prix/superficie (hypothèses pédagogiques, cf. cas 1 : 0,55 / 0,10)
31+BETA_AGE = -0.004 # effet de l'âge sur ln P (par année)
32+SIGMA = 0.07 # bruit transactionnel (écart-type de ln P)
33+PRIX_BASE = {"H1": 430000.0, "H2": 640000.0, "H3": 1150000.0} # prix de référence par zone (bâtiment type)
34+NCOL = NLIG = 20
35+
36+# ----------------------------------------------------------------------------- lots et zones
37+
38+def zone_du_lot(x: int, y: int) -> str:
39+ if y < 3:
40+ return "C"
41+ if 3 <= y < 8 and x < 4:
42+ return "P"
43+ if y >= 8 and 10 <= x < 15:
44+ return "H2"
45+ if y >= 8 and x >= 15:
46+ return "H3"
47+ return "H1"
48+
49+
50+def est_rezone(x: int, y: int) -> bool:
51+ return y >= 8 and 7 <= x < 10
52+
53+
54+def generer_lots(seed: int = SEED) -> pd.DataFrame:
55+ rng = np.random.default_rng(seed)
56+ rows = []
57+ for y in range(NLIG):
58+ for x in range(NCOL):
59+ z = zone_du_lot(x, y)
60+ front = 20.0 if z in ("H1", "P") else (24.0 if z == "H2" else (30.0 if z == "H3" else 25.0))
61+ prof = 30.0 if z != "H3" else 36.0
62+ s_terr = round(front * prof * rng.uniform(0.92, 1.12), 0)
63+ if z == "H1":
64+ age = int(rng.integers(10, 76)) # maisons de 1951 à 2016
65+ s_hab = round(float(rng.normal(135, 22)), 0)
66+ s_hab = float(min(max(s_hab, 85), 210))
67+ nlog = 1
68+ elif z == "H2":
69+ age = int(rng.integers(5, 71)); s_hab = round(float(rng.normal(230, 35)), 0); nlog = int(rng.integers(2, 5))
70+ elif z == "H3":
71+ age = int(rng.integers(5, 61)); s_hab = round(float(rng.normal(520, 80)), 0); nlog = int(rng.integers(4, 9))
72+ else:
73+ age = int(rng.integers(5, 61)); s_hab = float("nan"); nlog = 0
74+ rows.append({"id_lot": f"L{y:02d}{x:02d}", "x": x, "y": y, "zone_initiale": z,
75+ "zone_apres_T0": "H2" if (z == "H1" and est_rezone(x, y)) else z,
76+ "rezone": bool(z == "H1" and est_rezone(x, y)), "superficie_terrain_m2": s_terr,
77+ "superficie_habitable_m2": s_hab, "age_2026": age, "logements": nlog})
78+ return pd.DataFrame(rows)
79+
80+
81+# ----------------------------------------------------------------------------- ventes
82+
83+def prime_rezonage(age: float) -> float:
84+ """Prime injectée (en ln P) pour une maison de l'îlot rezoné vendue après T0 : 0,03 + 0,0015 × max(âge − 20, 0)."""
85+ return 0.03 + 0.0015 * max(age - 20.0, 0.0)
86+
87+
88+def _indice(d: date) -> float:
89+ return (1.0 + TAUX_MARCHE) ** ((d - DATE_REF).days / 365.25)
90+
91+
92+def generer_ventes(lots: pd.DataFrame, seed: int = SEED) -> pd.DataFrame:
93+ rng = np.random.default_rng(seed + 1)
94+ res = lots[lots["zone_initiale"].isin(["H1", "H2", "H3"])].reset_index(drop=True)
95+ n_jours = (DATE_FIN - DATE_DEBUT).days
96+ rows = []
97+ for _, L in res.iterrows():
98+ n_ventes = 1 + int(rng.random() < 0.5)
99+ jours = sorted(rng.integers(0, n_jours + 1, size=n_ventes).tolist())
100+ for k, j in enumerate(jours):
101+ d = DATE_DEBUT + timedelta(days=int(j))
102+ age_vente = L["age_2026"] - (2026 - d.year)
103+ z = L["zone_initiale"]
104+ lnP = (math.log(PRIX_BASE[z]) + ELAST_HAB * math.log(L["superficie_habitable_m2"] / (135.0 if z == "H1" else 230.0 if z == "H2" else 520.0))
105+ + ELAST_TERR * math.log(L["superficie_terrain_m2"] / 600.0) + BETA_AGE * age_vente + math.log(_indice(d)))
106+ traite_post = bool(L["rezone"] and d >= T0)
107+ if traite_post:
108+ lnP += prime_rezonage(age_vente)
109+ lnP += rng.normal(0.0, SIGMA)
110+ prix = round(math.exp(lnP) / 500.0) * 500.0
111+ rows.append({"id_vente": f"V{len(rows)+1:04d}", "id_lot": L["id_lot"], "zone_initiale": z, "rezone": bool(L["rezone"]),
112+ "date_vente": d.isoformat(), "post_T0": d >= T0, "traite_post": traite_post,
113+ "superficie_habitable_m2": L["superficie_habitable_m2"], "superficie_terrain_m2": L["superficie_terrain_m2"],
114+ "age_vente": int(age_vente), "prix_vente": prix})
115+ return pd.DataFrame(rows)
116+
117+
118+# ----------------------------------------------------------------------------- analyse : paires et DiD
119+
120+def _mois(d1: date, d2: date) -> float:
121+ return (d1 - d2).days / 30.4375
122+
123+
124+def apparier(ventes: pd.DataFrame, date_eval: date = date(2026, 9, 1)) -> pd.DataFrame:
125+ """Chaque vente traitée (îlot rezoné, après T0) est appariée à la vente témoin H1 non rezonée, postérieure à T0,
126+ la plus proche (superficies, âge, date), sans remise. Le prix du témoin est ajusté au temps, à la superficie
127+ habitable, au terrain et à l'âge avec les coefficients déclarés ; la différence est exprimée en % du témoin ajusté."""
128+ tr = ventes[ventes["traite_post"]].copy()
129+ ctl = ventes[(ventes["zone_initiale"] == "H1") & (~ventes["rezone"]) & (ventes["post_T0"])].copy()
130+ utilises: set[str] = set(); paires = []
131+ for _, t in tr.sort_values("date_vente").iterrows():
132+ dt = date.fromisoformat(t["date_vente"])
133+ cand = ctl[~ctl["id_vente"].isin(utilises)]
134+ d = np.sqrt(((cand["superficie_habitable_m2"] - t["superficie_habitable_m2"]) / 15.0) ** 2
135+ + ((cand["superficie_terrain_m2"] - t["superficie_terrain_m2"]) / 80.0) ** 2
136+ + ((cand["age_vente"] - t["age_vente"]) / 8.0) ** 2
137+ + (np.array([_mois(dt, date.fromisoformat(s)) for s in cand["date_vente"]]) / 4.0) ** 2)
138+ c = cand.loc[d.idxmin()]; utilises.add(c["id_vente"])
139+ dc = date.fromisoformat(c["date_vente"])
140+ # ajustements du témoin vers le traité (point de vue du traité) : temps, superficies, âge
141+ a_temps = cp.ajustement_temps_taux(c["prix_vente"], TAUX_MARCHE, _mois(dt, dc))
142+ p1 = c["prix_vente"] + a_temps
143+ fac = ((t["superficie_habitable_m2"] / c["superficie_habitable_m2"]) ** ELAST_HAB
144+ * (t["superficie_terrain_m2"] / c["superficie_terrain_m2"]) ** ELAST_TERR
145+ * math.exp(BETA_AGE * (t["age_vente"] - c["age_vente"])))
146+ p_adj = p1 * fac
147+ paires.append({"id_traite": t["id_vente"], "id_temoin": c["id_vente"], "date_traite": t["date_vente"], "date_temoin": c["date_vente"],
148+ "age_traite": int(t["age_vente"]), "age_temoin": int(c["age_vente"]), "distance": float(d.min()),
149+ "prix_traite": t["prix_vente"], "prix_temoin": c["prix_vente"], "ajust_temps": a_temps,
150+ "facteur_physique": fac, "prix_temoin_ajuste": p_adj, "difference": t["prix_vente"] - p_adj,
151+ "difference_pct": (t["prix_vente"] - p_adj) / p_adj})
152+ return pd.DataFrame(paires)
153+
154+
155+def resume_paires(paires: pd.DataFrame) -> dict:
156+ r = cp.ventes_appariees(paires["prix_traite"].tolist(), paires["prix_temoin_ajuste"].tolist())
157+ pct = paires["difference_pct"].to_numpy()
158+ jeunes = paires[paires["age_traite"] < 40]; vieux = paires[paires["age_traite"] >= 40]
159+ return {"n": r["n"], "moyenne": r["moyenne"], "mediane": r["mediane"], "ecart_type": r["ecart_type"],
160+ "erreur_type_moyenne": r["ecart_type"] / math.sqrt(r["n"]), "moyenne_pct": float(pct.mean()), "mediane_pct": float(np.median(pct)),
161+ "ecart_type_pct": float(pct.std(ddof=1)), "ic95_pct": (float(pct.mean() - 1.96 * pct.std(ddof=1) / math.sqrt(len(pct))),
162+ float(pct.mean() + 1.96 * pct.std(ddof=1) / math.sqrt(len(pct)))),
163+ "n_jeunes": len(jeunes), "moyenne_pct_jeunes": float(jeunes["difference_pct"].mean()),
164+ "n_vieux": len(vieux), "moyenne_pct_vieux": float(vieux["difference_pct"].mean()),
165+ "pente_age": float(np.polyfit(paires["age_traite"], pct, 1)[0])}
166+
167+
168+def did(ventes: pd.DataFrame) -> dict:
169+ """Doubles différences sur les ventes H1 (îlot rezoné = traité ; reste de H1 = témoin). (a) 2 × 2 sur ln P ;
170+ (b) régression avec superficies, âge et tendance : ln P = a + b·traité + c·post + δ·traité×post + x'γ + ε."""
171+ h1 = ventes[ventes["zone_initiale"] == "H1"].copy()
172+ h1["lnP"] = np.log(h1["prix_vente"])
173+ g = h1.groupby(["rezone", "post_T0"])["lnP"].agg(["mean", "count"])
174+ m = {(bool(i), bool(j)): float(g.loc[(i, j), "mean"]) for i, j in g.index}
175+ n = {(bool(i), bool(j)): int(g.loc[(i, j), "count"]) for i, j in g.index}
176+ delta_2x2 = (m[(True, True)] - m[(True, False)]) - (m[(False, True)] - m[(False, False)])
177+ t_annees = np.array([(date.fromisoformat(s) - DATE_REF).days / 365.25 for s in h1["date_vente"]])
178+ X = np.column_stack([np.ones(len(h1)), h1["rezone"].astype(float), h1["post_T0"].astype(float),
179+ (h1["rezone"] & h1["post_T0"]).astype(float), np.log(h1["superficie_habitable_m2"] / 135.0),
180+ np.log(h1["superficie_terrain_m2"] / 600.0), h1["age_vente"].astype(float), t_annees])
181+ y = h1["lnP"].to_numpy()
182+ beta, *_ = np.linalg.lstsq(X, y, rcond=None)
183+ resid = y - X @ beta; nobs, k = X.shape
184+ s2 = resid @ resid / (nobs - k); cov = s2 * np.linalg.inv(X.T @ X); se = np.sqrt(np.diag(cov))
185+ noms = ["const", "traite", "post", "traite_x_post", "ln_hab", "ln_terr", "age", "tendance"]
186+ return {"moyennes_lnP": m, "effectifs": n, "delta_2x2": delta_2x2, "delta_2x2_pct": math.exp(delta_2x2) - 1.0,
187+ "coef": dict(zip(noms, beta.tolist())), "se": dict(zip(noms, se.tolist())),
188+ "delta_reg": float(beta[3]), "se_delta": float(se[3]), "delta_reg_pct": math.exp(float(beta[3])) - 1.0,
189+ "n_obs": int(nobs), "sigma_resid": float(math.sqrt(s2))}
190+
191+
192+def prime_theorique_moyenne(ventes: pd.DataFrame) -> float:
193+ """Moyenne de la prime injectée sur les ventes traitées (vérité terrain du générateur)."""
194+ tr = ventes[ventes["traite_post"]]
195+ return float(np.mean([math.exp(prime_rezonage(a)) - 1.0 for a in tr["age_vente"]]))
196+
197+
198+# ----------------------------------------------------------------------------- outils du chapitre (règles, options, droits acquis)
199+
200+def densite_admissible(s_terrain: float, s_min_par_logement: float, logements_max_grille: int, cos_max: float, s_min_lot: float) -> dict:
201+ """Nombre de logements et aire de plancher admissibles d'après une grille des usages (lecture de la grille)."""
202+ conforme_lot = s_terrain >= s_min_lot
203+ n_densite = math.floor(s_terrain / s_min_par_logement)
204+ n = max(0, min(n_densite, logements_max_grille))
205+ return {"lot_conforme": conforme_lot, "logements_par_densite": n_densite, "logements_admissibles": n, "aire_plancher_max_m2": cos_max * s_terrain}
206+
207+
208+def contribution_parcs(valeur_site: float, superficie_site: float, taux: float = 0.10) -> dict:
209+ """Contribution pour fins de parcs (LAU 117.4 al. 1) : au plus 10 % de la superficie et de la valeur du site."""
210+ return {"terrain_max_m2": taux * superficie_site, "somme_max": taux * valeur_site, "taux": taux}
211+
212+
213+def valeur_residuelle_redeveloppement(n_logements: int, s_logement_m2: float, prix_neuf_m2: float, cout_constr_m2: float,
214+ taux_indirects: float, demolition: float, marge_promoteur: float, financement: float,
215+ taux_parcs: float = 0.10, charge_inclusion: float = 0.0) -> dict:
216+ """Valeur résiduelle du terrain redéveloppé : V_L = [GDV − C_dir − C_ind − démolition − marge − financement − inclusion] / (1 + taux_parcs).
217+ La contribution pour fins de parcs est proportionnelle à la valeur du site : V_L·taux_parcs, d'où la division."""
218+ gdv = n_logements * s_logement_m2 * prix_neuf_m2
219+ c_dir = n_logements * s_logement_m2 * cout_constr_m2
220+ c_ind = taux_indirects * c_dir
221+ marge = marge_promoteur * gdv
222+ fin = financement * gdv
223+ reste = gdv - c_dir - c_ind - demolition - marge - fin - charge_inclusion
224+ v_l = reste / (1.0 + taux_parcs)
225+ return {"gdv": gdv, "couts_directs": c_dir, "couts_indirects": c_ind, "demolition": demolition, "marge": marge, "financement": fin,
226+ "inclusion": charge_inclusion, "reste_avant_parcs": reste, "contribution_parcs": v_l * taux_parcs, "valeur_residuelle": v_l}
227+
228+
229+def prime_option(v_actuel: float, v_redev: float) -> float:
230+ """Prime d'option de redéveloppement = max(0, V_redev − V_actuel)."""
231+ return max(0.0, v_redev - v_actuel)
232+
233+
234+def valeur_attendue_rezonage(v_actuel: float, v_redev: float, p: float, delai_annees: float, r: float, cout_demande: float = 0.0) -> float:
235+ """Valeur attendue d'un immeuble dont le rezonage (ou le PPCMOI) est probable : V = V_act + p·max(0, V_redev − V_act)·(1+r)^−T − c."""
236+ return v_actuel + p * prime_option(v_actuel, v_redev) * (1.0 + r) ** (-delai_annees) - cout_demande
237+
238+
239+def grille_attendue(v_actuel: float, v_redev: float, probs, delais, r: float) -> np.ndarray:
240+ return np.array([[valeur_attendue_rezonage(v_actuel, v_redev, p, T, r) - v_actuel for T in delais] for p in probs])
241+
242+
243+def valeur_surplus_derogatoire(delta_noi: float, R: float, lam: float) -> float:
244+ """Surplus de revenu protégé par droits acquis, extinction au taux de risque λ : V_Δ = ΔRNE / (R + λ)."""
245+ return delta_noi / (R + lam)
246+
247+
248+def valeur_surplus_horizon(delta_noi: float, R: float, lam: float, T: float) -> float:
249+ """Même surplus mais sur un horizon fini T (annuités continues) : V_Δ(T) = ΔRNE·[1 − e^{−(R+λ)T}]/(R+λ)."""
250+ return delta_noi * (1.0 - math.exp(-(R + lam) * T)) / (R + lam)
251+
252+
253+def valeur_attendue_zone_agricole(v_agricole: float, v_si_autorise: float, p: float, delai_annees: float, r: float, cout_demande: float) -> float:
254+ """Zone agricole : V = p·V_autorisé·(1+r)^−T + (1−p)·V_agricole − coût de la demande."""
255+ return p * v_si_autorise * (1.0 + r) ** (-delai_annees) + (1.0 - p) * v_agricole - cout_demande
256+
257+
258+# ----------------------------------------------------------------------------- exemple intégral (paramètres déclarés)
259+
260+EXEMPLE = {
261+ # lecture de la grille (zone H2 fictive de Val-Fictif)
262+ "grille": {"s_min_lot": 450.0, "s_min_par_logement": 150.0, "logements_max": 4, "cos_max": 0.60, "marge_avant": 6.0, "marge_lat": 2.0, "marge_arr": 9.0, "hauteur_max_etages": 3},
263+ # lot témoin de l'exemple : maison 1958 de 95 m² sur 600 m², îlot rezoné
264+ "lot": {"superficie_terrain_m2": 600.0, "superficie_habitable_m2": 95.0, "annee": 1958, "v_actuel": 380000.0},
265+ # redéveloppement en quadruplex : 4 × 90 m² (hypothèses, cf. encadré synthétique)
266+ "redev": {"n_logements": 4, "s_logement_m2": 90.0, "prix_neuf_m2": 5800.0, "cout_constr_m2": 2900.0, "taux_indirects": 0.12,
267+ "demolition": 30000.0, "marge_promoteur": 0.15, "financement": 0.03, "taux_parcs": 0.10},
268+ "option": {"r": 0.08, "probs": [0.25, 0.50, 0.75, 1.00], "delais": [0.5, 1.0, 2.0, 3.0], "cout_demande": 8000.0},
269+ # duplex dérogatoire protégé par droits acquis dans une zone H1
270+ "droits_acquis": {"loyer_mensuel_logement": 1450.0, "ratio_depenses_duplex": 0.35, "loyer_mensuel_maison": 2300.0, "ratio_depenses_maison": 0.30,
271+ "R": 0.055, "lambdas": [0.0, 0.01, 0.02, 0.05], "v_conforme": 430000.0, "horizon": 25.0},
272+ "parcs": {"valeur_site": 960000.0, "superficie_site": 2400.0},
273+ "agricole": {"superficie_ha": 5.0, "v_ha_agricole": 15000.0, "v_lot_residentiel": 90000.0, "superficie_lot_ha": 0.5, "p": 0.30, "delai": 1.5, "r": 0.06, "cout_demande": 5000.0},
274+}
275+
276+
277+def exemple_complet(ventes: pd.DataFrame | None = None) -> dict:
278+ E = EXEMPLE
279+ g = densite_admissible(E["lot"]["superficie_terrain_m2"], E["grille"]["s_min_par_logement"], E["grille"]["logements_max"], E["grille"]["cos_max"], E["grille"]["s_min_lot"])
280+ rd = valeur_residuelle_redeveloppement(**E["redev"])
281+ v_act = E["lot"]["v_actuel"]
282+ prime = prime_option(v_act, rd["valeur_residuelle"])
283+ G = grille_attendue(v_act, rd["valeur_residuelle"], E["option"]["probs"], E["option"]["delais"], E["option"]["r"])
284+ da = E["droits_acquis"]
285+ noi_dup = 2 * 12 * da["loyer_mensuel_logement"] * (1 - da["ratio_depenses_duplex"])
286+ noi_mai = 12 * da["loyer_mensuel_maison"] * (1 - da["ratio_depenses_maison"])
287+ d_noi = noi_dup - noi_mai
288+ v_delta = {lam: valeur_surplus_derogatoire(d_noi, da["R"], lam) for lam in da["lambdas"]}
289+ v_delta_T = {lam: valeur_surplus_horizon(d_noi, da["R"], lam, da["horizon"]) for lam in da["lambdas"]}
290+ pc = contribution_parcs(E["parcs"]["valeur_site"], E["parcs"]["superficie_site"])
291+ ag = E["agricole"]
292+ v_agr = ag["superficie_ha"] * ag["v_ha_agricole"]
293+ v_aut = ag["v_lot_residentiel"] + (ag["superficie_ha"] - ag["superficie_lot_ha"]) * ag["v_ha_agricole"]
294+ v_att_agr = valeur_attendue_zone_agricole(v_agr, v_aut, ag["p"], ag["delai"], ag["r"], ag["cout_demande"])
295+ out = {"grille": g, "redev": rd, "prime_option": prime, "prime_option_pct": prime / v_act, "grille_attendue": G,
296+ "exemple_p75_T2": valeur_attendue_rezonage(v_act, rd["valeur_residuelle"], 0.75, 2.0, E["option"]["r"], E["option"]["cout_demande"]),
297+ "noi_duplex": noi_dup, "noi_maison": noi_mai, "delta_noi": d_noi, "v_delta": v_delta, "v_delta_T": v_delta_T,
298+ "v_droits_acquis_lam02": da["v_conforme"] + v_delta[0.02], "parcs": pc, "v_agricole": v_agr, "v_si_autorise": v_aut, "v_attendue_agricole": v_att_agr}
299+ if ventes is not None:
300+ paires = apparier(ventes); out["paires"] = paires; out["resume_paires"] = resume_paires(paires); out["did"] = did(ventes)
301+ out["prime_theorique_moyenne"] = prime_theorique_moyenne(ventes)
302+ return out
303+
304+
305+def charger_ou_generer(ecrire: bool = True) -> tuple[pd.DataFrame, pd.DataFrame]:
306+ lots = generer_lots(); ventes = generer_ventes(lots)
307+ if ecrire:
308+ d = ROOT / "data/synthetic"; d.mkdir(parents=True, exist_ok=True)
309+ lots.to_csv(d / "ch08_zonage_lots.csv", index=False); ventes.to_csv(d / "ch08_zonage_ventes.csv", index=False)
310+ return lots, ventes
311+
312+
313+def main(ecrire: bool = True) -> dict:
314+ lots, ventes = charger_ou_generer(ecrire)
315+ res = exemple_complet(ventes)
316+ f = lambda x: f"{x:,.0f}".replace(",", " ")
317+ print("=== Ch. 8 — DONNÉES SYNTHÉTIQUES (graine 20261031) ===")
318+ print(f"Lots : {len(lots)} ; par zone : {lots['zone_initiale'].value_counts().to_dict()} ; rezonés H1→H2 : {int(lots['rezone'].sum())}")
319+ print(f"Ventes : {len(ventes)} ; H1 : {int((ventes['zone_initiale']=='H1').sum())} ; traitées (îlot rezoné, après T0) : {int(ventes['traite_post'].sum())}")
320+ rp = res["resume_paires"]
321+ print(f"\n--- Paires (traité vs témoin ajusté) : n = {rp['n']} ---")
322+ print(f"Différence moyenne {f(rp['moyenne'])} $ (médiane {f(rp['mediane'])} $, écart-type {f(rp['ecart_type'])} $, erreur-type {f(rp['erreur_type_moyenne'])} $)")
323+ print(f"En % du témoin ajusté : moyenne {rp['moyenne_pct']*100:.2f} %, médiane {rp['mediane_pct']*100:.2f} %, IC95 [{rp['ic95_pct'][0]*100:.2f} ; {rp['ic95_pct'][1]*100:.2f}]")
324+ print(f"Maisons < 40 ans : n = {rp['n_jeunes']}, +{rp['moyenne_pct_jeunes']*100:.2f} % ; ≥ 40 ans : n = {rp['n_vieux']}, +{rp['moyenne_pct_vieux']*100:.2f} % ; pente {rp['pente_age']*100:.3f} pt/an")
325+ print(f"Prime théorique moyenne injectée : {res['prime_theorique_moyenne']*100:.2f} %")
326+ d = res["did"]
327+ print(f"\n--- Doubles différences (H1, n = {d['n_obs']}) ---")
328+ for k, v in d["moyennes_lnP"].items():
329+ print(f" ln P moyen traité={k[0]} post={k[1]} : {v:.4f} (n = {d['effectifs'][k]})")
330+ print(f"δ (2×2) = {d['delta_2x2']:.4f} → {d['delta_2x2_pct']*100:.2f} % ; δ (régression) = {d['delta_reg']:.4f} (e.-t. {d['se_delta']:.4f}) → {d['delta_reg_pct']*100:.2f} % ; σ résiduel {d['sigma_resid']:.4f}")
331+ print(f" coefficients : " + ", ".join(f"{k} {v:.4f}" for k, v in d["coef"].items()))
332+ g = res["grille"]
333+ print(f"\n--- Lecture de la grille H2 (lot 600 m²) : lot conforme {g['lot_conforme']}, logements par densité {g['logements_par_densite']}, admissibles {g['logements_admissibles']}, plancher max {g['aire_plancher_max_m2']:.0f} m²")
334+ rd = res["redev"]
335+ print(f"--- Redéveloppement en quadruplex : GDV {f(rd['gdv'])} ; directs {f(rd['couts_directs'])} ; indirects {f(rd['couts_indirects'])} ; démolition {f(rd['demolition'])} ; marge {f(rd['marge'])} ; financement {f(rd['financement'])}")
336+ print(f" reste avant parcs {f(rd['reste_avant_parcs'])} ; contribution parcs {f(rd['contribution_parcs'])} ; valeur résiduelle {f(rd['valeur_residuelle'])} ; V_actuel {f(EXEMPLE['lot']['v_actuel'])} ; prime d'option {f(res['prime_option'])} ({res['prime_option_pct']*100:.1f} %)")
337+ print(" grille attendue (prime espérée, $) lignes p = " + str(EXEMPLE["option"]["probs"]) + " ; colonnes T = " + str(EXEMPLE["option"]["delais"]))
338+ for p, row in zip(EXEMPLE["option"]["probs"], res["grille_attendue"]):
339+ print(" p = %.2f : " % p + " ".join(f(x) for x in row))
340+ print(f" exemple p = 0,75, T = 2, coût 8 000 $ : V = {f(res['exemple_p75_T2'])}")
341+ print(f"--- Droits acquis : RNE duplex {f(res['noi_duplex'])} ; RNE maison {f(res['noi_maison'])} ; ΔRNE {f(res['delta_noi'])}")
342+ for lam in EXEMPLE["droits_acquis"]["lambdas"]:
343+ print(f" λ = {lam:.2f} : V_Δ = {f(res['v_delta'][lam])} (horizon 25 ans : {f(res['v_delta_T'][lam])})")
344+ print(f" V avec droits acquis (λ = 2 %) = {f(res['v_droits_acquis_lam02'])}")
345+ print(f"--- Parcs : terrain max {res['parcs']['terrain_max_m2']:.0f} m² ou somme max {f(res['parcs']['somme_max'])}")
346+ print(f"--- Zone agricole : V agricole {f(res['v_agricole'])} ; V si autorisé {f(res['v_si_autorise'])} ; V attendue {f(res['v_attendue_agricole'])}")
347+ return {"lots": lots, "ventes": ventes, **res}
348+
349+
350+if __name__ == "__main__":
351+ main()
added analysis/python/cases/ch10_controle_rapport.py +215 −0
@@ -0,0 +1,215 @@
1+#!/usr/bin/env python3
2+"""Chapitre 10 — contrôle automatique de la présence des douze éléments du contenu minimal d'un rapport écrit
3+(NPP de l'OEAQ, norme 2, règle 2.3) dans un texte, et calculs auxiliaires du chapitre (probabilité de détection
4+d'une lacune par révisions indépendantes, incidence d'une hypothèse extraordinaire, échéances réglementaires).
5+
6+Les quatre extraits de rapports analysés ici sont FICTIFS, rédigés pour le traité (aucun immeuble, aucun client,
7+aucun évaluateur réel). Le contrôle est lexical : il détecte la présence de formulations caractéristiques, pas la
8+qualité du contenu — limite discutée au chapitre.
9+
10+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch10_controle_rapport.py"""
11+from __future__ import annotations
12+import re, sys, pathlib, unicodedata, datetime as dt
13+from dataclasses import dataclass
14+
15+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
16+ROOT = pathlib.Path(__file__).resolve().parents[3]
17+
18+# ----------------------------------------------------------------------------------------------------------------
19+# 1. Les douze éléments de la règle 2.3 (paraphrase ; source : NPP OEAQ, norme 2, règle 2.3, éléments 1 à 12)
20+# ----------------------------------------------------------------------------------------------------------------
21+@dataclass(frozen=True)
22+class Element:
23+ no: int
24+ cle: str
25+ libelle: str
26+ strict: tuple # motifs caractéristiques (lexique strict)
27+ souple: tuple # motifs supplémentaires, plus larges (lexique souple = strict + souple)
28+
29+def _n(s: str) -> str:
30+ """Normalise : minuscules, sans accents, espaces simples, apostrophes droites."""
31+ s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode("ascii")
32+ s = s.replace("’", "'").lower()
33+ return re.sub(r"\s+", " ", s)
34+
35+ELEMENTS: list[Element] = [
36+ Element(1, "identification", "Identification physique et légale de l'immeuble",
37+ (r"\blot\s+\d", r"cadastre du quebec", r"designation cadastrale", r"identification (physique|legale)"),
38+ (r"\badresse\b", r"\bsitue au\b", r"matricule")),
39+ Element(2, "droits", "Droits de propriété évalués",
40+ (r"pleine propriete", r"droits? (de propriete )?evalues?", r"droit (reel )?evalue", r"fief simple"),
41+ (r"\busufruit\b", r"emphyteo", r"indivis", r"copropriete divise")),
42+ Element(3, "but_fin", "But (valeur recherchée) et fin (usage prévu) de l'évaluation",
43+ (r"\bbut de l'evaluation", r"\bfin de l'evaluation", r"aux fins (de|d')", r"usage prevu du rapport"),
44+ (r"destinataire", r"financement hypothecaire", r"\bmandat\b")),
45+ Element(4, "definition_valeur", "Définition de la valeur recherchée",
46+ (r"valeur marchande", r"definition de la valeur", r"valeur recherchee", r"prix (sincere )?le plus probable"),
47+ (r"\bvaleur\b.{0,40}\bdefini", r"valeur d'investissement", r"valeur reelle")),
48+ Element(5, "date", "Date d'évaluation (date effective de l'opinion)",
49+ (r"date d'evaluation", r"date effective", r"en date du \d", r"\bau \d{1,2}(er)? [a-z]+ \d{4}\b"),
50+ (r"\b(janvier|fevrier|mars|avril|mai|juin|juillet|aout|septembre|octobre|novembre|decembre) \d{4}\b",)),
51+ Element(6, "etendue", "Étendue du travail (recherches, vérifications, inspection)",
52+ (r"etendue (du travail|des recherches|de la mission)", r"portee du mandat", r"travaux (effectues|realises)",
53+ r"inspect(e|ee|ion)"),
54+ (r"\bvisite\b", r"recherches? effectuee", r"verifi(e|cation)")),
55+ Element(7, "reserves_hypotheses", "Réserves limitant la portée de l'acte et hypothèses de base (dont extraordinaires)",
56+ (r"hypothese", r"\breserves?\b", r"condition(s)? limitative"),
57+ (r"\bpresum", r"\bsuppos", r"sous reserve")),
58+ Element(8, "raisonnement", "Traitement de l'information et raisonnement conduisant à la conclusion",
59+ (r"methode (de comparaison|du cout|du revenu|de la parite|des revenus)", r"\bajustements?\b", r"reconciliation",
60+ r"raisonnement", r"analyse (des|de) (ventes|donnees|marche)"),
61+ (r"\bcomparables?\b", r"taux (global|de capitalisation)", r"\bconclu(sion|ons)\b")),
62+ Element(9, "umpp", "Usage le meilleur et le plus profitable (PHMU)",
63+ (r"usage le meilleur et le plus profitable", r"plus haute et meilleure utilisation", r"\bumpp\b", r"\bphmu\b"),
64+ (r"usage optimal", r"meilleur usage")),
65+ Element(10, "rejet_methode", "Explication du rejet d'une méthode traditionnelle",
66+ (r"methode[^.]{0,80}(ecartee|rejetee|non retenue|non appliquee|n'a pas ete (retenue|appliquee))",
67+ r"rejet de la methode", r"(ecartee|rejetee|non retenue)[^.]{0,40}methode"),
68+ (r"n'a pas ete (utilisee|employee)", r"\bnon pertinente\b")),
69+ Element(11, "conformite", "Conformité à la substance de la norme 1 et écarts aux directives",
70+ (r"conform(e|ement|ite) aux normes de pratique", r"normes de pratique professionnelle",
71+ r"ecart[^.]{0,60}directive", r"directive[^.]{0,60}ecart"),
72+ (r"\bnormes?\b", r"\boeaq\b", r"ordre des evaluateurs")),
73+ Element(12, "attestation", "Attestation signée et datée",
74+ (r"j'atteste", r"\batteste(nt)? par la presente", r"\battestation\b"),
75+ (r"au mieux de (ma|notre) connaissance", r"\bsign(e|ee|ature)\b")),
76+]
77+
78+def controler(texte: str, lexique: str = "strict") -> dict:
79+ """Retourne, pour chaque élément, (détecté: bool, motif ayant déclenché la détection ou None)."""
80+ t = _n(texte)
81+ res = {}
82+ for e in ELEMENTS:
83+ motifs = e.strict + (e.souple if lexique == "souple" else ())
84+ hit = None
85+ for m in motifs:
86+ if re.search(m, t):
87+ hit = m; break
88+ res[e.cle] = (hit is not None, hit)
89+ return res
90+
91+def taux_conformite(res: dict) -> float:
92+ """Équation (10.2) : proportion des douze éléments détectés."""
93+ return sum(1 for d, _ in res.values() if d) / len(ELEMENTS)
94+
95+def manquants(res: dict) -> list[int]:
96+ return [e.no for e in ELEMENTS if not res[e.cle][0]]
97+
98+def p_detection(d: float, n: int) -> float:
99+ """Probabilité qu'au moins une de n révisions indépendantes, chacune de puissance d, relève une lacune."""
100+ return 1.0 - (1.0 - d) ** n
101+
102+def incidence_hypothese(v_sous_h: float, v_sans_h: float) -> tuple[float, float]:
103+ """Incidence d'une hypothèse extraordinaire : écart absolu et relatif (base : valeur sans l'hypothèse)."""
104+ return v_sous_h - v_sans_h, (v_sous_h - v_sans_h) / v_sans_h
105+
106+def esperance_sous_incertitude(p: float, v_sous_h: float, v_sans_h: float) -> float:
107+ return p * v_sous_h + (1 - p) * v_sans_h
108+
109+def fin_conservation(dernier_service: dt.date, annees: int = 5) -> dt.date:
110+ """Règlement sur la tenue des dossiers (C-26, r. 133, art. 6) : au moins 5 ans après le dernier service."""
111+ return dernier_service.replace(year=dernier_service.year + annees)
112+
113+def bilan_formation(heures_total: float, heures_ethique: float, report_anterieur: float = 0.0) -> dict:
114+ """Règlement sur la formation continue (C-26, r. 127.1) : 30 h par période de 2 ans dont 3 h éthique/déontologie/normes ;
115+ report maximal de 5 h vers la période suivante."""
116+ requis_total, requis_ethique, report_max = 30.0, 3.0, 5.0
117+ dispo = heures_total + min(report_anterieur, report_max)
118+ deficit_total = max(0.0, requis_total - dispo)
119+ deficit_ethique = max(0.0, requis_ethique - heures_ethique)
120+ report_suivant = min(report_max, max(0.0, dispo - requis_total))
121+ return {"deficit_total": deficit_total, "deficit_ethique": deficit_ethique, "report_suivant": report_suivant,
122+ "conforme": deficit_total == 0 and deficit_ethique == 0}
123+
124+# ----------------------------------------------------------------------------------------------------------------
125+# 2. Extraits FICTIFS de rapports (rédigés pour le traité ; immeubles, parties et signataires inexistants)
126+# ----------------------------------------------------------------------------------------------------------------
127+EXTRAITS = {
128+"A": """EXTRAIT FICTIF A — « Rapport abrégé »
129+Objet : résidence unifamiliale à étages située au 123, rue des Érables (adresse fictive), secteur Hull, Gatineau.
130+Mandat : estimer la valeur de la propriété pour le compte de l'institution prêteuse, aux fins de financement hypothécaire.
131+Description : cottage de deux étages construit en 1998, superficie habitable de 165 m², terrain de 620 m², garage simple,
132+sous-sol aménagé. Le bâtiment est en bon état général selon la visite extérieure effectuée.
133+Analyse : trois ventes comparables du secteur ont été retenues et ajustées pour la superficie, l'âge et le garage.
134+Les prix ajustés s'établissent entre 538 000 $ et 571 000 $. Par la méthode de comparaison, nous estimons la valeur
135+à 555 000 $. Ce montant est valable pour une période de six mois.
136+Signature : Évaluateur fictif, É.A.""",
137+
138+"B": """EXTRAIT FICTIF B — « Rapport narratif, section Réserves et conclusion »
139+Identification : lot 0 000 000 du cadastre du Québec (numéro fictif), circonscription foncière de Hull, bâtiment résidentiel.
140+Droits évalués : pleine propriété, libre de toute charge autre que celles mentionnées au certificat de localisation.
141+But et fin : estimer la valeur marchande, telle que définie aux Normes de pratique professionnelle de l'OEAQ, en date
142+du 1er septembre 2026, aux fins d'une décision d'achat par le client.
143+Usage le meilleur et le plus profitable : l'usage résidentiel unifamilial actuel est conforme au zonage et demeure
144+l'usage optimal du site tel qu'aménagé.
145+Hypothèses : nous présumons que le permis de construction pour l'agrandissement arrière a été obtenu et que les travaux
146+sont conformes au Code. La méthode du coût a également été appliquée.
147+Conclusion : la valeur marchande est de 612 000 $. Le présent rapport est conforme aux normes de pratique professionnelle.
148+J'atteste par la présente, au mieux de ma connaissance, que les faits énoncés sont vrais et exacts.""",
149+
150+"C": """EXTRAIT FICTIF C — « Rapport de mise à jour »
151+Le présent rapport de mise à jour se rapporte au rapport narratif complet portant sur l'immeuble de douze logements
152+situé au 456, avenue Fictive, Montréal (lot 0 000 001 du cadastre du Québec, numéro fictif), préparé par notre firme
153+pour le même client en date du 1er mars 2026. Les réserves et hypothèses de base du rapport original demeurent applicables.
154+L'inspection a été mise à jour le 10 octobre 2026 ; aucun changement physique n'a été constaté.
155+Les loyers en vigueur ont augmenté de 3,1 % et le taux global de capitalisation retenu passe de 5,25 % à 5,40 %.
156+Par la méthode du revenu, l'indication de valeur en date du 15 octobre 2026 est de 2 410 000 $, comparativement à
157+2 450 000 $ au rapport original. La méthode du coût, écartée au rapport original pour un immeuble de 1965, demeure
158+non retenue. Évaluateur fictif, É.A.""",
159+
160+"D": """EXTRAIT FICTIF D — « Sommaire d'un rapport narratif complet »
161+Identification physique et légale : lot 0 000 002 du cadastre du Québec (numéro fictif), 789, rue Imaginaire, Québec.
162+Droits évalués : pleine propriété. But et fin de l'évaluation : estimer la valeur marchande aux fins d'un règlement
163+successoral ; le rapport est destiné au liquidateur. Définition de la valeur : la valeur marchande est définie comme
164+le prix sincère le plus probable, conformément aux Normes de pratique professionnelle de l'OEAQ. Date d'évaluation :
165+1er septembre 2026. Étendue du travail : inspection complète de l'immeuble le 28 août 2026, recherche de titres,
166+analyse de quatorze ventes de triplex. Réserves et hypothèses de base : voir l'annexe ; aucune hypothèse extraordinaire.
167+Usage le meilleur et le plus profitable : usage résidentiel locatif actuel. Raisonnement : méthode de comparaison
168+(cinq comparables, ajustements documentés) et méthode du revenu, réconciliation pondérée. La méthode du coût a été
169+écartée en raison de l'âge du bâtiment et de la difficulté de mesurer la dépréciation. Le rapport est conforme à la
170+substance de la norme 1 ; aucun écart aux directives. J'atteste par la présente, au mieux de ma connaissance et de ma
171+conviction, que les faits énoncés sont vrais et exacts. Signé et daté.""",
172+}
173+
174+def main(verbose: bool = True) -> dict:
175+ out = {"controle": {}, "taux": {}, "manquants": {}}
176+ for nom, txt in EXTRAITS.items():
177+ for lex in ("strict", "souple"):
178+ r = controler(txt, lex)
179+ out["controle"][(nom, lex)] = r
180+ out["taux"][(nom, lex)] = taux_conformite(r)
181+ out["manquants"][(nom, lex)] = manquants(r)
182+ # probabilités de détection (modèle de révisions indépendantes)
183+ grille_d = (0.5, 0.7, 0.9); grille_n = (1, 2, 3)
184+ out["p_detection"] = {(d, n): p_detection(d, n) for d in grille_d for n in grille_n}
185+ # incidence d'une hypothèse extraordinaire (chiffres FICTIFS de l'extrait B et de l'exercice 10.5)
186+ out["incidence_B"] = incidence_hypothese(612000, 575000)
187+ out["esperance_B"] = esperance_sous_incertitude(0.6, 612000, 575000)
188+ # échéances réglementaires (exercice 10.2)
189+ out["conservation"] = fin_conservation(dt.date(2026, 11, 15))
190+ out["formation"] = bilan_formation(heures_total=26, heures_ethique=2, report_anterieur=5)
191+ out["formation_b"] = bilan_formation(heures_total=33, heures_ethique=3, report_anterieur=0)
192+ if verbose:
193+ print("== Contrôle lexical des douze éléments (règle 2.3) — extraits FICTIFS")
194+ for nom in EXTRAITS:
195+ for lex in ("strict", "souple"):
196+ r = out["controle"][(nom, lex)]
197+ print(f" Extrait {nom} [{lex:6s}] : {sum(d for d,_ in r.values()):2d}/12 détectés "
198+ f"(taux {out['taux'][(nom, lex)]:.3f}) ; manquants : {out['manquants'][(nom, lex)]}")
199+ print("== Détail (lexique strict) :")
200+ for nom in EXTRAITS:
201+ r = out["controle"][(nom, "strict")]
202+ print(" ", nom, " ".join(f"{e.no}:{'X' if r[e.cle][0] else '.'}" for e in ELEMENTS))
203+ print("== Probabilité de détection d'une lacune, 1-(1-d)^n :")
204+ for d in grille_d:
205+ print(" d =", d, " ".join(f"n={n}: {out['p_detection'][(d,n)]:.3f}" for n in grille_n))
206+ dv, dr = out["incidence_B"]
207+ print(f"== Incidence de l'hypothèse extraordinaire (extrait B, fictif) : {dv:,.0f} $ ({dr*100:.2f} %) ; "
208+ f"espérance à p = 0,6 : {out['esperance_B']:,.0f} $")
209+ print("== Fin de conservation minimale d'un dossier clos le 2026-11-15 :", out["conservation"])
210+ print("== Bilan de formation continue (26 h dont 2 h éthique, report 5 h) :", out["formation"])
211+ print("== Bilan de formation continue (33 h dont 3 h éthique) :", out["formation_b"])
212+ return out
213+
214+if __name__ == "__main__":
215+ main()
added analysis/python/cases/ch10_matrice_normes.py +90 −0
@@ -0,0 +1,90 @@
1+#!/usr/bin/env python3
2+"""Chapitre 10 — statistiques descriptives de la matrice des dispositions normatives du domaine C
3+(sources/normes-C.csv : 100 dispositions vérifiées le 2026-10-31) et chronologie documentaire des cadres normatifs
4+(dates tirées de sources/registry-C.csv). Imprime les chiffres cités au chapitre 10.
5+
6+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/cases/ch10_matrice_normes.py"""
7+from __future__ import annotations
8+import sys, pathlib, datetime as dt
9+import pandas as pd
10+
11+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
12+ROOT = pathlib.Path(__file__).resolve().parents[3]
13+MATRICE = ROOT / "sources/normes-C.csv"
14+
15+# Regroupement des 17 domaines de la matrice en six familles (choix de présentation de l'auteur)
16+FAMILLES = {
17+ "OEAQ-NPP": "OEAQ — normes de pratique", "OEAQ-Déontologie": "OEAQ — déontologie",
18+ "OEAQ-Règlements": "OEAQ — règlements", "Code des professions": "OEAQ — règlements",
19+ "IVS": "Cadres hors Québec", "CUSPAP": "Cadres hors Québec", "USPAP": "Cadres hors Québec",
20+ "RICS": "Cadres hors Québec", "IAAO": "Cadres hors Québec",
21+ "Banque du Canada": "Finance et prudentiel", "BSIF": "Finance et prudentiel", "Finances Canada": "Finance et prudentiel",
22+ "SCHL": "Finance et prudentiel",
23+ "Statistique Canada": "Données de marché", "APCIQ": "Données de marché", "JLR": "Données de marché",
24+ "Teranet–BNC": "Données de marché",
25+}
26+
27+# Chronologie documentaire (source_id du registre C ; dates vérifiées le 2026-10-31).
28+# publication : date de publication connue (None si non documentée) ; effet : date d'entrée en vigueur ;
29+# fin : fin de validité connue (None = en vigueur au 2026-10-31) ; statut : en_vigueur | remplace | projet | reference
30+CADRES = [
31+ dict(cadre="IAAO — Standard on AVMs (rév. 2018)", source="SRC-C-026", publication=None, effet=dt.date(2018, 7, 1), fin=None, statut="reference", precision="mois"),
32+ dict(cadre="IAAO — Standard on Ratio Studies (2013)", source="SRC-C-024", publication=None, effet=dt.date(2013, 4, 1), fin=None, statut="reference", precision="mois"),
33+ dict(cadre="IAAO — Standard on Mass Appraisal (rév. 2025)", source="SRC-C-025", publication=None, effet=dt.date(2025, 1, 1), fin=None, statut="reference", precision="annee"),
34+ dict(cadre="USPAP 2024 (États-Unis)", source="SRC-C-021", publication=None, effet=dt.date(2024, 1, 1), fin=None, statut="en_vigueur", precision="jour", verification="partielle"),
35+ dict(cadre="CUSPAP 2024 (Canada)", source="SRC-C-020", publication=None, effet=dt.date(2024, 1, 1), fin=dt.date(2026, 3, 31), statut="remplace", precision="jour"),
36+ dict(cadre="NPP de l'OEAQ (Québec)", source="SRC-C-001", publication=None, effet=dt.date(2024, 2, 1), fin=None, statut="en_vigueur", precision="jour"),
37+ dict(cadre="IVS 2025 (IVSC)", source="SRC-C-014", publication=dt.date(2024, 1, 31), effet=dt.date(2025, 1, 31), fin=None, statut="en_vigueur", precision="jour"),
38+ dict(cadre="RICS Red Book Global (2025)", source="SRC-C-023", publication=dt.date(2024, 12, 11), effet=dt.date(2025, 1, 31), fin=None, statut="en_vigueur", precision="jour"),
39+ dict(cadre="CUSPAP 2026 (Canada)", source="SRC-C-018", publication=None, effet=dt.date(2026, 4, 1), fin=None, statut="en_vigueur", precision="jour"),
40+ dict(cadre="IVS 2028 — projet (exposure draft)", source="SRC-C-015", publication=dt.date(2026, 1, 1), effet=dt.date(2028, 1, 31), fin=None, statut="projet", precision="mois"),
41+]
42+DATE_REF = dt.date(2026, 10, 31)
43+
44+def charger() -> pd.DataFrame:
45+ m = pd.read_csv(MATRICE)
46+ m["famille"] = m["domaine"].map(FAMILLES)
47+ assert m["famille"].notna().all(), "domaine sans famille"
48+ m["n_chapitres"] = m["chapitres"].str.split(";").apply(len)
49+ m["ch10"] = m["chapitres"].str.contains("ch10")
50+ return m
51+
52+def statistiques(m: pd.DataFrame) -> dict:
53+ par_domaine = m["domaine"].value_counts()
54+ par_famille = m["famille"].value_counts()
55+ par_chapitre = m["chapitres"].str.split(";").explode().value_counts()
56+ oeaq = m["domaine"].str.startswith("OEAQ") | (m["domaine"] == "Code des professions")
57+ return {
58+ "n": len(m), "n_domaines": m["domaine"].nunique(), "n_chapitres_couverts": par_chapitre.size,
59+ "par_domaine": par_domaine, "par_famille": par_famille, "par_chapitre": par_chapitre,
60+ "n_oeaq": int(oeaq.sum()), "part_oeaq": float(oeaq.mean()),
61+ "n_ch10": int(m["ch10"].sum()), "part_ch10": float(m["ch10"].mean()),
62+ "ch10_par_domaine": m.loc[m["ch10"], "domaine"].value_counts(),
63+ "moy_chapitres_par_disposition": float(m["n_chapitres"].mean()),
64+ "max_chapitres_par_disposition": int(m["n_chapitres"].max()),
65+ "part_multi_chapitres": float((m["n_chapitres"] > 1).mean()),
66+ }
67+
68+def chronologie() -> pd.DataFrame:
69+ c = pd.DataFrame(CADRES)
70+ c["en_vigueur_2026_10_31"] = c.apply(lambda r: r["effet"] <= DATE_REF and (r["fin"] is None or r["fin"] >= DATE_REF) and r["statut"] != "projet", axis=1)
71+ c["delai_publication_effet_jours"] = c.apply(lambda r: (r["effet"] - r["publication"]).days if r["publication"] else None, axis=1)
72+ return c
73+
74+def main(verbose: bool = True) -> dict:
75+ m = charger(); st = statistiques(m); ch = chronologie()
76+ if verbose:
77+ print(f"== Matrice C : {st['n']} dispositions, {st['n_domaines']} domaines, {st['n_chapitres_couverts']} chapitres couverts")
78+ print("== Par famille :\n", st["par_famille"].to_string())
79+ print("== Par domaine :\n", st["par_domaine"].to_string())
80+ print(f"== Dispositions OEAQ + Code des professions : {st['n_oeaq']} ({st['part_oeaq']*100:.0f} %)")
81+ print(f"== Dispositions renvoyant au ch. 10 : {st['n_ch10']} ({st['part_ch10']*100:.0f} %) ; par domaine :\n", st["ch10_par_domaine"].to_string())
82+ print("== Dix chapitres les plus cités :\n", st["par_chapitre"].head(10).to_string())
83+ print(f"== Chapitres par disposition : moyenne {st['moy_chapitres_par_disposition']:.2f}, max {st['max_chapitres_par_disposition']}, "
84+ f"part multi-chapitres {st['part_multi_chapitres']*100:.0f} %")
85+ pd.set_option("display.width", 200)
86+ print("== Chronologie des cadres (registre C) :\n", ch[["cadre", "publication", "effet", "fin", "statut", "en_vigueur_2026_10_31", "delai_publication_effet_jours"]].to_string(index=False))
87+ return {"matrice": m, "stats": st, "chronologie": ch}
88+
89+if __name__ == "__main__":
90+ main()
added analysis/python/figures/ch01_figures.py +127 −0
@@ -0,0 +1,127 @@
1+#!/usr/bin/env python3
2+"""Figures du chapitre 1 (DONNÉES SYNTHÉTIQUES du cas 1, graine 20261031 ; simulations étiquetées).
3+Sorties (assets/figures/ch01/) :
4+ ch01-fig-prix-valeur.pdf nuage prix observé vs valeur estimée (modèle hédonique de référence), comparables du ch. 16 en évidence
5+ ch01-fig-residus.pdf distribution des écarts prix/valeur (résidus) et loi normale ajustée
6+ ch01-fig-recherche.pdf simulation du modèle de recherche : offres, prix acceptés, délais de vente
7+ ch01-fig-rosen.pdf schéma de l'équilibre hédonique de Rosen : enchères, offres et enveloppe des prix
8+ ch01-fig-sensibilite-sigma.pdf sensibilité : bornes de l'intervalle à 95 % et probabilité d'un écart > 10 % selon sigma
9+Exécuter depuis la racine : python3 analysis/python/figures/ch01_figures.py"""
10+import sys, pathlib, math
11+ROOT = pathlib.Path(__file__).resolve().parents[3]
12+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
13+import numpy as np
14+import matplotlib.pyplot as plt
15+from scipy.stats import norm
16+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars, fmt_pct
17+import ch01_valeur_prix as c1
18+
19+def fig_prix_valeur(t):
20+ m = t["modele"]; v = t["ventes"]
21+ pred = np.exp(m["X"].values @ m["beta"]); obs = v["prix_vente"].values
22+ comps = t["comparables"]
23+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.9))
24+ ax.scatter(pred, obs, s=9, color=palette.cat[0], alpha=0.45, linewidths=0, label="600 ventes synthétiques")
25+ lo, hi = 150_000, 1_000_000
26+ ax.plot([lo, hi], [lo, hi], color=palette.ink2, lw=1.0, ls="--", label="Prix = valeur estimée")
27+ ax.plot([lo, hi], [lo * 1.1, hi * 1.1], color=palette.grid, lw=0.8, ls=":"); ax.plot([lo, hi], [lo * 0.9, hi * 0.9], color=palette.grid, lw=0.8, ls=":")
28+ ax.text(hi / 1.1 * 0.97, hi * 0.985, "+10 %", ha="right", va="top", fontsize=7.5, color=palette.ink2)
29+ ax.text(hi * 0.985, hi * 0.9 * 0.985, "−10 %", ha="right", va="top", fontsize=7.5, color=palette.ink2)
30+ ax.scatter(comps["valeur_estimee"], comps["prix_vente"], s=34, facecolors="none", edgecolors=palette.cat[1], linewidths=1.3, label="Comparables du chapitre 16", zorder=5)
31+ for _, r in comps.iterrows():
32+ ax.annotate(r["id_vente"], (r["valeur_estimee"], r["prix_vente"]), xytext=(5, -9), textcoords="offset points", fontsize=7, color=palette.ink)
33+ p = t["pred"]
34+ ax.axvline(p["mediane"], color=palette.cat[2], lw=0.9, ls="-.")
35+ ax.text(p["mediane"] * 1.01, lo * 1.15, "valeur estimée du sujet\n" + fmt_dollars(p["mediane"]), fontsize=7.5, color=palette.ink, ha="left")
36+ ax.set_xlim(lo, hi); ax.set_ylim(lo, hi)
37+ ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)); ax.yaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars))
38+ ax.set_xlabel("Valeur estimée $\\hat V = \\exp(\\mathbf{x}'\\hat{\\beta})$ (médiane conditionnelle)"); ax.set_ylabel("Prix de vente observé $P$")
39+ ax.legend(loc="upper left", fontsize=7.5)
40+ return savefig(fig, "ch01/ch01-fig-prix-valeur.pdf")
41+
42+def fig_residus(t):
43+ res = t["modele"]["residus"]; sigma = t["modele"]["sigma"]
44+ ecarts = (np.exp(res) - 1) * 100
45+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.2))
46+ bins = np.arange(-30, 32, 2.5)
47+ ax.hist(ecarts, bins=bins, density=True, color=palette.cat[0], alpha=0.75, edgecolor=palette.surface, linewidth=0.5, label="Écarts observés (600 ventes)")
48+ x = np.linspace(-30, 30, 400)
49+ # densité de (exp(e) − 1)·100 avec e ~ N(0, σ²)
50+ dens = norm.pdf(np.log(1 + x / 100), 0, sigma) / (1 + x / 100) / 100
51+ ax.plot(x, dens, color=palette.cat[1], lw=1.6, label=f"Loi log-normale ajustée ($\\sigma$ = {sigma:.3f})")
52+ for q in (-10, 10):
53+ ax.axvline(q, color=palette.ink2, lw=0.8, ls="--")
54+ d = t["dispersion"]
55+ ax.text(-29, ax.get_ylim()[1] * 0.80, f"{d['part_dans_pm10pct']*100:.1f} % des ventes\nà ±10 % de la\nvaleur estimée", ha="left", fontsize=8, color=palette.ink)
56+ ax.set_xlabel("Écart entre le prix observé et la valeur estimée, $P/\\hat V - 1$ (%)"); ax.set_ylabel("Densité")
57+ ax.legend(loc="upper right", fontsize=7.5)
58+ return savefig(fig, "ch01/ch01-fig-residus.pdf")
59+
60+def fig_recherche(t):
61+ s = t["recherche"]; centre = t["pred"]["mediane"]; P = c1.PARAMS_RECHERCHE
62+ prix_rel = (s["prix"] / centre - 1) * 100; r_rel = (s["prix_reserve"] / centre - 1) * 100
63+ fig, (a1, a2) = plt.subplots(1, 2, figsize=(LARGEUR, 3.0), gridspec_kw={"width_ratios": [1.5, 1]})
64+ x = np.linspace(-25, 25, 500)
65+ a1.plot(x, norm.pdf(np.log(1 + x / 100), 0, P["sigma_o"]) / (1 + x / 100) / 100, color=palette.cat[0], lw=1.5, label="Offres reçues (toutes)")
66+ a1.hist(prix_rel, bins=np.arange(-5, 25, 1.0), density=True, color=palette.cat[1], alpha=0.7, edgecolor=palette.surface, linewidth=0.4, label="Prix acceptés (simulation)")
67+ a1.axvline(r_rel, color=palette.ink2, lw=0.9, ls="--")
68+ a1.text(r_rel + 7.5, a1.get_ylim()[1] * 0.62, f"prix de réserve\n+{r_rel:.1f} %", fontsize=7.5, color=palette.ink)
69+ a1.set_xlabel("Écart au centre des enchères $m$ (%)"); a1.set_ylabel("Densité"); a1.legend(loc="upper left", fontsize=7)
70+ delais = s["delai"]; mx = int(min(delais.max(), 24))
71+ vals = [np.mean(delais == k) for k in range(1, mx + 1)]
72+ a2.bar(range(1, mx + 1), vals, color=palette.cat[0], width=0.8)
73+ a2.axvline(s["delai_moyen"], color=palette.ink2, lw=0.9, ls="--")
74+ a2.text(s["delai_moyen"] + 0.4, max(vals) * 0.9, f"moyenne\n{s['delai_moyen']:.1f} mois", fontsize=7.5, color=palette.ink)
75+ a2.set_xlabel("Délai de vente (mois)"); a2.set_ylabel("Fréquence"); a2.set_xlim(0.3, mx + 0.7)
76+ fig.tight_layout(w_pad=1.5)
77+ return savefig(fig, "ch01/ch01-fig-recherche.pdf")
78+
79+def fig_rosen():
80+ """Schéma : fonctions d'enchère θ(z; u) concaves de deux acheteurs, fonctions d'offre φ(z; π) convexes de deux vendeurs,
81+ enveloppe p(z) tangente aux deux familles. Formes fonctionnelles déclarées (purement illustratives)."""
82+ z = np.linspace(0.5, 4.5, 400)
83+ p = 100 + 60 * np.log(z) # enveloppe (fonction de prix hédonique)
84+ dp = 60 / z
85+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.4))
86+ ax.plot(z, p, color=palette.ink, lw=2.0, label="Fonction de prix $p(z)$ (enveloppe)")
87+ for i, z0 in enumerate((1.3, 3.2)):
88+ p0, s0 = 100 + 60 * math.log(z0), 60 / z0
89+ theta = p0 + s0 * (z - z0) - 18 * (z - z0) ** 2 # enchère d'un acheteur : concave, tangente en z0
90+ phi = p0 + s0 * (z - z0) + 18 * (z - z0) ** 2 # offre d'un vendeur : convexe, tangente en z0
91+ mask = np.abs(z - z0) < 0.9
92+ ax.plot(z[mask], theta[mask], color=palette.cat[0], lw=1.3, ls="--", label="Enchères des acheteurs $\\theta(z; u)$" if i == 0 else None)
93+ ax.plot(z[mask], phi[mask], color=palette.cat[1], lw=1.3, ls=":", label="Offres des vendeurs $\\phi(z; \\pi)$" if i == 0 else None)
94+ ax.scatter([z0], [p0], color=palette.ink, s=22, zorder=5)
95+ ax.annotate(f"appariement {i+1}", (z0, p0), xytext=(8, -14), textcoords="offset points", fontsize=7.5, color=palette.ink)
96+ ax.set_xlabel("Quantité de l'attribut $z$ (p. ex. superficie)"); ax.set_ylabel("Prix")
97+ ax.set_yticks([]); ax.set_xticks([])
98+ ax.text(4.45, 100 + 60 * math.log(4.45) - 6, "pente = prix implicite $\\partial p/\\partial z$", ha="right", fontsize=7.5, color=palette.ink2)
99+ ax.legend(loc="lower right", fontsize=7.5)
100+ return savefig(fig, "ch01/ch01-fig-rosen.pdf")
101+
102+def fig_sensibilite(t):
103+ sig = np.linspace(0.02, 0.20, 100)
104+ fig, (a1, a2) = plt.subplots(1, 2, figsize=(LARGEUR, 3.0))
105+ a1.plot(sig, (np.exp(1.96 * sig) - 1) * 100, color=palette.cat[0], lw=1.6, label="Borne haute (+)")
106+ a1.plot(sig, (np.exp(-1.96 * sig) - 1) * 100, color=palette.cat[1], lw=1.6, label="Borne basse (−)")
107+ a1.fill_between(sig, (np.exp(-1.96 * sig) - 1) * 100, (np.exp(1.96 * sig) - 1) * 100, color=palette.cat[0], alpha=0.08)
108+ s0 = t["modele"]["sigma"]
109+ a1.axvline(s0, color=palette.ink2, lw=0.8, ls="--"); a1.text(s0 + 0.004, -35, f"cas 1 : $\\sigma$ = {s0:.3f}", fontsize=7.5, color=palette.ink)
110+ a1.set_xlabel("Dispersion $\\sigma$ (écart-type de $\\ln P$ à $\\mathbf{x}$ donné)"); a1.set_ylabel("Bornes de l'intervalle à 95 % (% de la valeur)")
111+ a1.legend(loc="upper left", fontsize=7.5)
112+ for d, col, lab in ((0.05, palette.cat[2], "> 5 %"), (0.10, palette.cat[0], "> 10 %"), (0.20, palette.cat[1], "> 20 %")):
113+ a2.plot(sig, [c1.proba_ecart(s, d) * 100 for s in sig], color=col, lw=1.6, label=f"écart {lab}")
114+ a2.axvline(s0, color=palette.ink2, lw=0.8, ls="--")
115+ a2.set_xlabel("Dispersion $\\sigma$"); a2.set_ylabel("Probabilité qu'un prix s'écarte de la valeur (%)")
116+ a2.legend(loc="upper left", fontsize=7.5)
117+ fig.tight_layout(w_pad=1.5)
118+ return savefig(fig, "ch01/ch01-fig-sensibilite-sigma.pdf")
119+
120+def main():
121+ setup()
122+ t = c1.tout()
123+ for f in (fig_prix_valeur(t), fig_residus(t), fig_recherche(t), fig_rosen(), fig_sensibilite(t)):
124+ print("OK", f)
125+
126+if __name__ == "__main__":
127+ main()
added analysis/python/figures/ch02_decomposition.py +42 −0
@@ -0,0 +1,42 @@
1+#!/usr/bin/env python3
2+"""Figure ch02 — cas 3 (DONNÉES SYNTHÉTIQUES) : (a) revenu net d'exploitation projeté avec les baux en place (droit du locateur)
3+et libre de baux (loyers de marché dès l'année 1) ; la surface entre les deux est le revenu cédé aux locataires ;
4+(b) valeur agrégée des baux pour les locataires (V_pleine − V_locateur) selon le nombre de logements reloués par an et le taux.
5+Sortie : assets/figures/ch02/ch02-fig-decomposition-droits.pdf"""
6+import sys, pathlib
7+ROOT = pathlib.Path(__file__).resolve().parents[3]
8+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
9+import numpy as np, matplotlib.pyplot as plt
10+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
11+import ch02_droits as cas
12+
13+
14+def main():
15+ setup()
16+ res = cas.main(verbose=False)
17+ base, sens = res["base"], res["sensibilite"]
18+ pc, pm = base["proj_contrat"], base["proj_marche"]
19+ fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.2), gridspec_kw={"width_ratios": [1.1, 1]})
20+ yrs = pc["exercice"].values
21+ a.plot(yrs, pm["rne"] / 1000, color=palette.cat[1], marker="s", ms=3, label="libre de baux (loyers de marché)")
22+ a.plot(yrs, pc["rne"] / 1000, color=palette.cat[0], marker="o", ms=3, label="baux en place (droit du locateur)")
23+ a.fill_between(yrs, pc["rne"] / 1000, pm["rne"] / 1000, where=pm["rne"] >= pc["rne"], color=palette.cat[1], alpha=0.18, lw=0)
24+ a.annotate("revenu cédé\naux locataires", xy=(yrs[1], (pc["rne"].iloc[1] + pm["rne"].iloc[1]) / 2000), xytext=(yrs[3] + 0.3, 96),
25+ fontsize=7.5, color=palette.ink, arrowprops=dict(arrowstyle="-", color=palette.ink2, lw=0.6))
26+ a.set_xlabel("Exercice"); a.set_ylabel("Revenu net d'exploitation (milliers de $)"); a.set_xticks(yrs[::2])
27+ a.legend(loc="upper left", fontsize=7.4); a.set_title("(a) RNE projeté, deux faisceaux de droits", loc="left")
28+ # (b) sensibilité
29+ for k, r in enumerate(cas.TAUX):
30+ s = sens[np.isclose(sens["taux"], r)].sort_values("rotation")
31+ b.plot((s["rotation"] * 12).round(0), s["V_locataires"] / 1000, marker="o", ms=3.5, color=palette.cat[k], label=f"$r$ = {r*100:.2f} %")
32+ b.set_yscale("log"); b.set_yticks([5, 10, 25, 50, 100, 250, 500]); b.set_yticklabels(["5", "10", "25", "50", "100", "250", "500"])
33+ b.set_xticks([0, 1, 2, 3, 6]); b.set_xlabel("Logements reloués au marché par an (sur 12)")
34+ b.set_ylabel("$V_{\\mathrm{pleine}} - V_{\\mathrm{locateur}}$ (milliers de dollars, éch. log.)")
35+ b.axvline(2, color=palette.ink2, lw=0.7, ls=":"); b.text(2.15, 200, "hypothèse\ndu cas 3", fontsize=7, color=palette.ink2)
36+ b.legend(loc="lower left", fontsize=7.4); b.set_title("(b) Valeur agrégée des baux pour les locataires", loc="left")
37+ fig.tight_layout(w_pad=2.2)
38+ out = savefig(fig, "ch02/ch02-fig-decomposition-droits.pdf"); print("OK", out)
39+
40+
41+if __name__ == "__main__":
42+ main()
added analysis/python/figures/ch02_ecart_logements.py +42 −0
@@ -0,0 +1,42 @@
1+#!/usr/bin/env python3
2+"""Figure ch02 — cas 3 (DONNÉES SYNTHÉTIQUES) : (a) ratio loyer contractuel / loyer de marché selon l'ancienneté du locataire,
3+avec la loi de génération déclarée (0,98^ancienneté) ; (b) valeur du bail pour le locataire selon la durée de maintien attendue,
4+r = 7,25 %, pour quatre logements. Sortie : assets/figures/ch02/ch02-fig-ecart-logements.pdf"""
5+import sys, pathlib
6+ROOT = pathlib.Path(__file__).resolve().parents[3]
7+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
8+import numpy as np, matplotlib.pyplot as plt
9+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
10+import ch02_droits as cas
11+
12+
13+def main():
14+ setup()
15+ res = cas.main(verbose=False)
16+ e = res["ecarts"]; r = res["d"]["hyp"]["taux_actualisation"]
17+ fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.1), gridspec_kw={"width_ratios": [1, 1.1]})
18+ # (a) ratio vs ancienneté
19+ x = np.linspace(0, 20, 100)
20+ a.plot(x, 0.98 ** x, color=palette.ink2, lw=1.0, ls="--", label="loi de génération $0{,}98^{\\,a}$")
21+ a.scatter(e["anciennete"], e["ratio_contrat_marche"], color=palette.cat[0], s=28, zorder=3, label="logements du cas 3")
22+ for anc, grp in e.groupby("anciennete"): # une étiquette par ancienneté (plusieurs logements partagent la même)
23+ lab = ", ".join(str(int(x)) for x in sorted(grp["logement"]))
24+ a.annotate(lab, (anc, grp["ratio_contrat_marche"].mean()), xytext=(anc + 0.45, grp["ratio_contrat_marche"].mean() + 0.004), fontsize=6.8, color=palette.ink)
25+ a.set_xlabel("Ancienneté du locataire (années, au 1$^{\\rm er}$ sept. 2026)"); a.set_ylabel("Loyer contractuel / loyer de marché")
26+ a.set_ylim(0.62, 1.02); a.set_xlim(-0.5, 20); a.legend(loc="lower left")
27+ a.set_title("(a) Écart au marché et ancienneté", loc="left")
28+ # (b) valeur du bail pour le locataire selon n
29+ n = np.arange(1, 11)
30+ for k, lg in enumerate([103, 102, 302, 101]):
31+ row = e.loc[e["logement"] == lg].iloc[0]
32+ va = [cas.valeur_bail_locataire(row["ecart_annuel"], int(j), r) for j in n]
33+ b.plot(n, np.array(va) / 1000, marker="o", ms=3.5, color=palette.cat[k], label=f"log. {lg} ({row['ecart_mensuel']:.0f} $/mois)")
34+ b.set_xlabel("Durée de maintien dans les lieux $n$ (années)"); b.set_ylabel("Valeur du bail pour le locataire (milliers de $)")
35+ b.set_xticks(n); b.legend(loc="upper left", fontsize=7.2)
36+ b.set_title("(b) $\\sum_{t \\leq n} (L^m_t - L^c_t)/(1{,}0725)^t$", loc="left")
37+ fig.tight_layout(w_pad=2.2)
38+ out = savefig(fig, "ch02/ch02-fig-ecart-logements.pdf"); print("OK", out)
39+
40+
41+if __name__ == "__main__":
42+ main()
added analysis/python/figures/ch06_emphyteose.py +34 −0
@@ -0,0 +1,34 @@
1+#!/usr/bin/env python3
2+"""Figure ch06 — emphytéose sur un immeuble locatif dont le RNE est celui du cas 3 (DONNÉES SYNTHÉTIQUES, hypothèses déclarées) :
3+décomposition de la valeur de la pleine propriété entre le droit de l'emphytéote et le droit du propriétaire (rentes + reprise)
4+selon la durée restante, avec les seuils de 40 ans (D-15.1, art. 1) et de 50 ans (art. 1040 C.c.Q.).
5+Sortie : assets/figures/ch06/ch06-fig-emphyteose.pdf"""
6+import sys, pathlib
7+ROOT = pathlib.Path(__file__).resolve().parents[3]
8+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
9+import numpy as np, matplotlib.pyplot as plt
10+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
11+import ch06_demembrements as cas
12+
13+
14+def main():
15+ setup()
16+ noi = cas.rne_cas3()
17+ n = np.arange(1, 101)
18+ res = [cas.emphyteose(noi, cas.RENTE_EMPH, int(k)) for k in n]
19+ emph = np.array([r["V_emph"] for r in res]) / 1e6; rentes = np.array([r["V_rentes"] for r in res]) / 1e6
20+ reprise = np.array([r["V_reprise"] for r in res]) / 1e6; V = res[0]["V_pleine"] / 1e6
21+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.2))
22+ ax.stackplot(n, emph, rentes, reprise, colors=[palette.cat[0], palette.cat[1], palette.cat[2]], alpha=0.85,
23+ labels=["droit de l'emphytéote (RNE − rente)", "propriétaire : rentes actualisées", "propriétaire : reprise de l'immeuble au terme"])
24+ ax.axhline(V, color=palette.ink, lw=0.8); ax.text(99, V + 0.03, f"pleine propriété {fmt_dollars(V*1e6)}", ha="right", va="bottom", fontsize=7.5)
25+ for seuil, txt in ((40, "40 ans : bail assimilé à un transfert\n(D-15.1, art. 1)"), (50, "50 ans : copropriété divise\npossible (art. 1040 C.c.Q.)")):
26+ ax.axvline(seuil, color=palette.ink2, ls=":", lw=1); ax.text(seuil + 1, 0.12, txt, fontsize=7, color=palette.ink2, va="bottom")
27+ ax.set_xlabel("Durée restante de l'emphytéose (années)"); ax.set_ylabel("Valeur (millions de $)"); ax.set_xlim(1, 100); ax.set_ylim(0, V * 1.12)
28+ ax.legend(loc="upper left", fontsize=7.5, ncol=1, bbox_to_anchor=(0.0, 0.98)); ax.grid(axis="x", visible=False)
29+ fig.tight_layout()
30+ out = savefig(fig, "ch06/ch06-fig-emphyteose.pdf"); print("OK", out)
31+
32+
33+if __name__ == "__main__":
34+ main()
added analysis/python/figures/ch06_usufruit.py +39 −0
@@ -0,0 +1,39 @@
1+#!/usr/bin/env python3
2+"""Figure ch06 — usufruit viager sur la maison du cas 1 (DONNÉES SYNTHÉTIQUES, table de mortalité synthétique) :
3+(a) courbes de survie tp_x de la table synthétique pour trois âges ; (b) part de la nue-propriété NP/V selon l'âge de
4+l'usufruitier pour trois taux d'actualisation, comparée à l'approximation par un terme fixe égal à l'espérance de vie.
5+Sortie : assets/figures/ch06/ch06-fig-usufruit.pdf"""
6+import sys, pathlib
7+ROOT = pathlib.Path(__file__).resolve().parents[3]
8+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
9+import numpy as np, matplotlib.pyplot as plt
10+from traite.style import setup, palette, savefig, LARGEUR
11+import ch06_demembrements as cas
12+
13+
14+def main():
15+ setup()
16+ V = cas.V_ARRONDI
17+ fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.1), gridspec_kw={"width_ratios": [1, 1.15]})
18+ for i, x in enumerate((55, 65, 75)):
19+ t = np.arange(0, cas.OMEGA - x + 1)
20+ a.plot(x + t, [cas.survie(x, k) for k in t], color=palette.cat[i], label=f"usufruitier de {x} ans")
21+ a.set_xlabel("Âge atteint (années)"); a.set_ylabel("Probabilité de survie $_t p_x$"); a.set_ylim(0, 1.02); a.set_xlim(55, 110)
22+ a.legend(loc="lower left"); a.set_title("(a) Table de mortalité synthétique", loc="left")
23+ ages = np.arange(50, 91)
24+ for i, r in enumerate((0.04, 0.05, 0.06)):
25+ b.plot(ages, [cas.usufruit_viager(V, int(x), r)["part_NP"] for x in ages], color=palette.cat[i], label=f"viager, $r$ = {r*100:.0f} %")
26+ approx = []
27+ for x in ages:
28+ n = int(round(cas.esperance_vie(int(x))))
29+ approx.append(cas.usufruit_terme(V, n, 0.05)["part_NP"])
30+ b.plot(ages, approx, color=palette.ink2, ls="--", lw=1.2, label="terme fixe = espérance de vie, $r$ = 5 %")
31+ b.set_xlabel("Âge de l'usufruitier à la date d'évaluation"); b.set_ylabel("Nue-propriété / pleine propriété")
32+ b.set_ylim(0.2, 1.0); b.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, p: f"{y*100:.0f} %"))
33+ b.legend(loc="upper left", fontsize=7.5); b.set_title("(b) Part de la nue-propriété ($g$ = 1,5 %)", loc="left")
34+ fig.tight_layout(w_pad=2.0)
35+ out = savefig(fig, "ch06/ch06-fig-usufruit.pdf"); print("OK", out)
36+
37+
38+if __name__ == "__main__":
39+ main()
added analysis/python/figures/ch07_charges.py +33 −0
@@ -0,0 +1,33 @@
1+#!/usr/bin/env python3
2+"""Figure ch07 — répartition des charges communes annuelles entre les huit fractions (DONNÉES SYNTHÉTIQUES).
3+Sortie : assets/figures/ch07/ch07-fig-repartition-charges.pdf"""
4+import sys, pathlib
5+ROOT = pathlib.Path(__file__).resolve().parents[3]
6+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
7+import numpy as np, matplotlib.pyplot as plt
8+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
9+import ch07_servitudes_copro as cas
10+
11+def main():
12+ setup()
13+ qp = cas.quotes_parts(); A = cas.cotisation_seuil(); rep = cas.repartition_charges(A, qp)
14+ noms = [f"{n} ({s:.0f} m², q = {q:.4f})" for n, s, q in zip(qp["noms"], qp["superficies"], qp["q"])]
15+ blocs = [("Charges courantes (q_i × budget)", rep["par_fraction_courantes"], palette.cat[0]),
16+ ("Fonds de prévoyance (q_i × A*)", rep["par_fraction_fonds"], palette.cat[1]),
17+ ("Fonds d'auto assurance (q_i)", rep["par_fraction_auto"], palette.cat[2]),
18+ ("Stationnement à usage restreint (part égale)", rep["par_fraction_restreint"], palette.cat[3])]
19+ y = np.arange(len(noms)); gauche = np.zeros(len(noms))
20+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.4))
21+ for lab, vals, col in blocs:
22+ ax.barh(y, vals, left=gauche, color=col, height=0.66, label=lab); gauche += vals
23+ for yi, tot in zip(y, rep["par_fraction_total"]):
24+ ax.text(tot + 150, yi, f"{tot:,.0f} $/an".replace(",", " "), va="center", fontsize=7.5, color=palette.ink)
25+ ax.set_yticks(y); ax.set_yticklabels(noms); ax.invert_yaxis()
26+ ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)); ax.set_xlim(0, rep["par_fraction_total"].max() * 1.25)
27+ ax.set_xlabel("Charges annuelles de la fraction (dollars de 2026, données synthétiques)")
28+ ax.grid(axis="y", visible=False)
29+ ax.legend(loc="upper center", bbox_to_anchor=(0.45, -0.2), ncol=2, fontsize=7.5)
30+ out = savefig(fig, "ch07/ch07-fig-repartition-charges.pdf"); print("OK", out)
31+
32+if __name__ == "__main__":
33+ main()
added analysis/python/figures/ch07_fonds.py +31 −0
@@ -0,0 +1,31 @@
1+#!/usr/bin/env python3
2+"""Figure ch07 — trajectoire du fonds de prévoyance sur 25 ans selon trois cotisations (DONNÉES SYNTHÉTIQUES).
3+Sortie : assets/figures/ch07/ch07-fig-fonds-prevoyance.pdf"""
4+import sys, pathlib
5+ROOT = pathlib.Path(__file__).resolve().parents[3]
6+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
7+import numpy as np, matplotlib.pyplot as plt
8+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
9+import ch07_servitudes_copro as cas
10+
11+def main():
12+ setup()
13+ A = cas.cotisation_seuil(); regle = 0.005 * cas.VALEUR_RECONSTRUCTION
14+ t_act, t_regle, t_star = cas.trajectoire(cas.COTISATION_ACTUELLE), cas.trajectoire(regle), cas.trajectoire(A)
15+ ans = np.arange(0, cas.HORIZON + 1) + 2026
16+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.6))
17+ ax.bar(ans[1:], -t_star["D"][1:], color=palette.grid, width=0.7, label="Réparations majeures prévues (dépenses, indexées à 2,5 %)")
18+ ax.plot(ans, t_act["F"], color=palette.cat[3], lw=1.8, label=f"Cotisation actuelle : {cas.COTISATION_ACTUELLE:,.0f} $ (indexée)".replace(",", " "))
19+ ax.plot(ans, t_regle["F"], color=palette.cat[2], lw=1.8, ls="--", label=f"Règle du 0,5 % de la valeur de reconstruction : {regle:,.0f} $".replace(",", " "))
20+ ax.plot(ans, t_star["F"], color=palette.cat[0], lw=2.0, label=f"Cotisation de seuil A* : {A:,.0f} $ (indexée)".replace(",", " "))
21+ ax.axhline(0, color=palette.ink2, lw=0.8)
22+ ax.yaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars))
23+ ax.set_xlabel("Année"); ax.set_ylabel("Solde du fonds en fin d'année")
24+ ax.set_xlim(2025.5, 2051.5)
25+ ax.legend(loc="upper left", fontsize=7.2)
26+ ax.annotate("an 14 : fenêtres\n(solde A* = 0)", xy=(2039, 0), xytext=(2041.5, 120000), fontsize=7.5, color=palette.ink2,
27+ arrowprops=dict(arrowstyle="-", color=palette.ink2, lw=0.7))
28+ out = savefig(fig, "ch07/ch07-fig-fonds-prevoyance.pdf"); print("OK", out)
29+
30+if __name__ == "__main__":
31+ main()
added analysis/python/figures/ch07_paires.py +35 −0
@@ -0,0 +1,35 @@
1+#!/usr/bin/env python3
2+"""Figure ch07 — ventes appariées : prix sans et avec servitude de passage pour 12 paires (DONNÉES SYNTHÉTIQUES, graine 20261031).
3+Sortie : assets/figures/ch07/ch07-fig-paires-servitude.pdf"""
4+import sys, pathlib
5+ROOT = pathlib.Path(__file__).resolve().parents[3]
6+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
7+import numpy as np, matplotlib.pyplot as plt
8+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
9+import ch07_servitudes_copro as cas
10+
11+def main():
12+ setup()
13+ p = cas.paires_servitude()
14+ ordre = np.argsort(p["sans"])
15+ sans, avec = p["sans"][ordre], p["avec"][ordre]
16+ y = np.arange(len(sans))
17+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.6))
18+ for yi, s, a in zip(y, sans, avec):
19+ ax.plot([a, s], [yi, yi], color=palette.grid, lw=2.2, zorder=1)
20+ ax.scatter(sans, y, color=palette.cat[0], s=34, zorder=3, label="Lot sans servitude (fonds libre)")
21+ ax.scatter(avec, y, color=palette.cat[1], s=34, zorder=3, marker="D", label="Lot grevé d'une servitude de passage")
22+ for yi, s, a in zip(y, sans, avec):
23+ ax.text(s + 4000, yi, f"{a - s:+,.0f} $".replace(",", " "), va="center", ha="left", fontsize=7.5, color=palette.ink2)
24+ ax.set_yticks(y); ax.set_yticklabels([f"paire {int(i) + 1}" for i in ordre])
25+ ax.xaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars))
26+ ax.set_xlim(avec.min() - 15000, sans.max() + 60000)
27+ ax.set_xlabel("Prix de vente (dollars de 2026, données synthétiques)")
28+ ax.grid(axis="y", visible=False)
29+ ax.set_title((f"Différence moyenne {p['moyenne']:,.0f} \\$ (IC 95 % : {p['ic95'][0]:,.0f} à {p['ic95'][1]:,.0f} \\$), soit {p['effet_relatif_moyen']*100:.1f} % du prix").replace(",", " ").replace(".", ","),
30+ loc="left", fontsize=8.5)
31+ ax.legend(loc="lower right", fontsize=7.5)
32+ out = savefig(fig, "ch07/ch07-fig-paires-servitude.pdf"); print("OK", out)
33+
34+if __name__ == "__main__":
35+ main()
added analysis/python/figures/ch07_sensibilite.py +27 −0
@@ -0,0 +1,27 @@
1+#!/usr/bin/env python3
2+"""Figure ch07 — sensibilité de la cotisation de seuil A* à l'inflation des coûts et au rendement du fonds (DONNÉES SYNTHÉTIQUES).
3+Sortie : assets/figures/ch07/ch07-fig-sensibilite-cotisation.pdf"""
4+import sys, pathlib
5+ROOT = pathlib.Path(__file__).resolve().parents[3]
6+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
7+import matplotlib.pyplot as plt
8+from traite.style import setup, palette, savefig, LARGEUR, fmt_dollars
9+import ch07_servitudes_copro as cas
10+
11+def main():
12+ setup()
13+ s = cas.sensibilite_cotisation(); regle = 0.005 * cas.VALEUR_RECONSTRUCTION
14+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.2))
15+ for i, (r, row) in enumerate(s["grille"].items()):
16+ ax.plot([p * 100 for p in s["inflations"]], row, marker="o", color=palette.cat[i], label=f"rendement du fonds r = {r*100:.0f} %")
17+ ax.axhline(regle, color=palette.ink2, lw=0.9, ls=":")
18+ ax.text(1.52, regle + 250, "règle du 0,5 % de la valeur de reconstruction (21 000 $)", fontsize=7.5, color=palette.ink2)
19+ ax.axhline(cas.COTISATION_ACTUELLE, color=palette.cat[3], lw=0.9, ls=":")
20+ ax.text(1.52, cas.COTISATION_ACTUELLE + 250, "cotisation actuelle (12 000 $)", fontsize=7.5, color=palette.cat[3])
21+ ax.set_xlabel("Hausse annuelle des coûts de construction π (%)"); ax.set_ylabel("Cotisation de seuil A* (an 1)")
22+ ax.yaxis.set_major_formatter(plt.FuncFormatter(fmt_dollars)); ax.set_ylim(10000, 26000)
23+ ax.legend(loc="lower right", fontsize=7.5)
24+ out = savefig(fig, "ch07/ch07-fig-sensibilite-cotisation.pdf"); print("OK", out)
25+
26+if __name__ == "__main__":
27+ main()
added analysis/python/figures/ch08_carte_zones.py +42 −0
@@ -0,0 +1,42 @@
1+#!/usr/bin/env python3
2+"""Figure ch08 — carte synthétique des zones de « Val-Fictif » (DONNÉES SYNTHÉTIQUES) : 400 lots, cinq zones,
3+îlot rezoné H1 → H2 (hachures) et ventes de l'îlot postérieures au rezonage (points).
4+Sortie : assets/figures/ch08/ch08-fig-carte-zones.pdf"""
5+import sys, pathlib
6+ROOT = pathlib.Path(__file__).resolve().parents[3]
7+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
8+import matplotlib.pyplot as plt
9+from matplotlib.patches import Rectangle, Patch
10+from matplotlib.lines import Line2D
11+from traite.style import setup, palette, savefig, LARGEUR
12+import ch08_zonage as cas
13+
14+ZONES = {"H1": ("H1 — habitation unifamiliale", 0), "H2": ("H2 — habitation 2 à 4 logements", 1), "H3": ("H3 — habitation multifamiliale", 2),
15+ "C": ("C — commerce et usages mixtes", 3), "P": ("P — parc et institutionnel", 4)}
16+
17+
18+def main():
19+ setup()
20+ lots, ventes = cas.charger_ou_generer(ecrire=False)
21+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.9))
22+ for _, L in lots.iterrows():
23+ c = palette.cat[ZONES[L["zone_initiale"]][1]]
24+ ax.add_patch(Rectangle((L["x"], L["y"]), 1, 1, facecolor=c, edgecolor=palette.surface, linewidth=0.5, alpha=0.85))
25+ if L["rezone"]:
26+ ax.add_patch(Rectangle((L["x"], L["y"]), 1, 1, facecolor="none", edgecolor=palette.ink, hatch="////", linewidth=0.0))
27+ # contour de l'îlot rezoné
28+ ax.add_patch(Rectangle((7, 8), 3, 12, facecolor="none", edgecolor=palette.ink, linewidth=1.4))
29+ tr = ventes[ventes["traite_post"]].merge(lots[["id_lot", "x", "y"]], on="id_lot")
30+ ax.scatter(tr["x"] + 0.5, tr["y"] + 0.5, s=14, color=palette.ink, zorder=5, linewidths=0)
31+ ax.text(8.5, 20.35, "îlot rezoné H1 → H2\n(1er janv. 2025)", ha="center", va="bottom", fontsize=7.5, color=palette.ink)
32+ ax.text(10, 1.5, "boulevard (zone C)", ha="center", va="center", fontsize=7.5, color=palette.surface, fontweight="bold")
33+ ax.set_xlim(0, 20); ax.set_ylim(0, 22.2); ax.set_aspect("equal"); ax.axis("off")
34+ handles = [Patch(facecolor=palette.cat[i], label=lab) for lab, i in ZONES.values()]
35+ handles += [Patch(facecolor="none", edgecolor=palette.ink, hatch="////", label="lots rezonés (traités)"),
36+ Line2D([0], [0], marker="o", color="none", markerfacecolor=palette.ink, markersize=4, label="ventes de l'îlot après T0")]
37+ ax.legend(handles=handles, loc="center left", bbox_to_anchor=(1.01, 0.5), fontsize=7.5, frameon=False)
38+ savefig(fig, "ch08/ch08-fig-carte-zones.pdf")
39+
40+
41+if __name__ == "__main__":
42+ main()
added analysis/python/figures/ch08_option_sensibilite.py +39 −0
@@ -0,0 +1,39 @@
1+#!/usr/bin/env python3
2+"""Figure ch08 — sensibilité des effets de valeur du zonage (DONNÉES SYNTHÉTIQUES / hypothèses déclarées) :
3+(a) prime espérée d'un rezonage probable selon la probabilité p et le délai T (grille, en milliers de $) ;
4+(b) valeur du surplus de revenu protégé par droits acquis V_Δ = ΔRNE/(R+λ) selon le taux de risque λ, pour trois R.
5+Sortie : assets/figures/ch08/ch08-fig-option-sensibilite.pdf"""
6+import sys, pathlib
7+ROOT = pathlib.Path(__file__).resolve().parents[3]
8+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
9+import numpy as np, matplotlib.pyplot as plt
10+from traite.style import setup, palette, savefig, LARGEUR
11+import ch08_zonage as cas
12+
13+
14+def main():
15+ setup()
16+ res = cas.exemple_complet()
17+ E = cas.EXEMPLE
18+ probs = np.array([0.10, 0.25, 0.40, 0.50, 0.60, 0.75, 0.90, 1.00]); delais = np.array([0.5, 1.0, 1.5, 2.0, 3.0, 4.0])
19+ G = cas.grille_attendue(E["lot"]["v_actuel"], res["redev"]["valeur_residuelle"], probs, delais, E["option"]["r"]) / 1000
20+ fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.2), gridspec_kw={"width_ratios": [1.15, 1]})
21+ im = a.imshow(G, cmap=palette.cmap_seq(), aspect="auto", origin="lower")
22+ a.set_xticks(range(len(delais))); a.set_xticklabels([f"{t:g}" for t in delais]); a.set_yticks(range(len(probs))); a.set_yticklabels([f"{p*100:.0f} %" for p in probs])
23+ a.set_xlabel("Délai T avant l'autorisation (années)"); a.set_ylabel("Probabilité p d'obtenir le changement"); a.grid(False)
24+ for i in range(len(probs)):
25+ for j in range(len(delais)):
26+ a.text(j, i, f"{G[i, j]:.0f}", ha="center", va="center", fontsize=7, color="white" if G[i, j] > G.mean() else palette.ink)
27+ a.set_title("(a) prime espérée du rezonage (k$)", loc="left")
28+ lam = np.linspace(0, 0.08, 81); dn = res["delta_noi"]
29+ for k, R in enumerate([0.045, 0.055, 0.070]):
30+ b.plot(lam * 100, [cas.valeur_surplus_derogatoire(dn, R, l) / 1000 for l in lam], color=palette.cat[k], label=f"R = {R*100:.1f} %")
31+ b.plot(lam * 100, [cas.valeur_surplus_horizon(dn, 0.055, l, E["droits_acquis"]["horizon"]) / 1000 for l in lam], color=palette.cat[1], linestyle="--", label="R = 5,5 %, horizon 25 ans")
32+ b.set_xlabel("Taux de risque d'extinction λ (% par an)"); b.set_ylabel("V_Δ (milliers de $)")
33+ b.set_title("(b) surplus dérogatoire : ΔRNE = 3 300 $", loc="left"); b.legend(fontsize=7, loc="upper right")
34+ fig.tight_layout()
35+ savefig(fig, "ch08/ch08-fig-option-sensibilite.pdf")
36+
37+
38+if __name__ == "__main__":
39+ main()
added analysis/python/figures/ch08_rezonage_paires.py +46 −0
@@ -0,0 +1,46 @@
1+#!/usr/bin/env python3
2+"""Figure ch08 — effet d'un rezonage mesuré par ventes appariées et par doubles différences (DONNÉES SYNTHÉTIQUES).
3+(a) différence traité − témoin ajusté (% du témoin) selon l'âge de la maison, avec moyenne et médiane ;
4+(b) ln P moyen des ventes H1 traitées et témoins avant/après T0 (schéma 2 × 2) et contrefactuel.
5+Sortie : assets/figures/ch08/ch08-fig-rezonage-paires.pdf"""
6+import sys, pathlib
7+ROOT = pathlib.Path(__file__).resolve().parents[3]
8+sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases"))
9+import numpy as np, matplotlib.pyplot as plt
10+from traite.style import setup, palette, savefig, LARGEUR
11+import ch08_zonage as cas
12+
13+
14+def main():
15+ setup()
16+ lots, ventes = cas.charger_ou_generer(ecrire=False)
17+ paires = cas.apparier(ventes); rp = cas.resume_paires(paires); d = cas.did(ventes)
18+ fig, (a, b) = plt.subplots(1, 2, figsize=(LARGEUR, 3.1), gridspec_kw={"width_ratios": [1.25, 1]})
19+ x = paires["age_traite"].to_numpy(); y = paires["difference_pct"].to_numpy() * 100
20+ a.scatter(x, y, s=18, color=palette.cat[0], linewidths=0, zorder=3)
21+ xx = np.linspace(x.min(), x.max(), 50); pente, orig = np.polyfit(x, y, 1)
22+ a.plot(xx, orig + pente * xx, color=palette.cat[0], linewidth=1.2)
23+ a.plot(xx, (np.exp([cas.prime_rezonage(v) for v in xx]) - 1) * 100, color=palette.ink2, linestyle="--", linewidth=1.0)
24+ a.axhline(rp["moyenne_pct"] * 100, color=palette.cat[1], linewidth=1.0)
25+ a.axhline(rp["mediane_pct"] * 100, color=palette.cat[1], linewidth=1.0, linestyle=":")
26+ a.text(x.max(), rp["moyenne_pct"] * 100 + 1.2, f"moyenne {rp['moyenne_pct']*100:.1f} %", ha="right", fontsize=7.5, color=palette.ink)
27+ a.text(x.max(), rp["mediane_pct"] * 100 - 3.2, f"médiane {rp['mediane_pct']*100:.1f} %", ha="right", fontsize=7.5, color=palette.ink)
28+ a.text(x.max(), y.min() + 1.0, "tireté : prime injectée\n(vérité du générateur)", ha="right", fontsize=7, color=palette.ink2)
29+ a.set_xlabel("Âge de la maison traitée (années)"); a.set_ylabel("Traité − témoin ajusté (% du témoin)")
30+ a.set_title(f"(a) {rp['n']} paires appariées", loc="left")
31+ m = d["moyennes_lnP"]
32+ xs = [0, 1]
33+ b.plot(xs, [m[(False, False)], m[(False, True)]], marker="o", color=palette.cat[2], label="témoins (reste de H1)")
34+ b.plot(xs, [m[(True, False)], m[(True, True)]], marker="o", color=palette.cat[0], label="traités (îlot rezoné)")
35+ cf = m[(True, False)] + (m[(False, True)] - m[(False, False)])
36+ b.plot(xs, [m[(True, False)], cf], marker="o", markerfacecolor="none", color=palette.cat[0], linestyle="--", label="contrefactuel des traités")
37+ b.annotate("", xy=(1, m[(True, True)]), xytext=(1, cf), arrowprops=dict(arrowstyle="<->", color=palette.ink, lw=1.0))
38+ b.text(1.03, (m[(True, True)] + cf) / 2, f"δ = {d['delta_2x2']:.3f}\n(2 × 2)", fontsize=7.5, va="center", color=palette.ink)
39+ b.set_xticks(xs); b.set_xticklabels(["avant T0", "après T0"]); b.set_ylabel("ln P moyen (ventes H1)")
40+ b.set_xlim(-0.3, 1.5); b.set_title("(b) doubles différences", loc="left"); b.legend(loc="upper left", fontsize=7)
41+ fig.tight_layout()
42+ savefig(fig, "ch08/ch08-fig-rezonage-paires.pdf")
43+
44+
45+if __name__ == "__main__":
46+ main()
added analysis/python/figures/ch10_figures.py +124 −0
@@ -0,0 +1,124 @@
1+#!/usr/bin/env python3
2+"""Chapitre 10 — figures Matplotlib :
3+ ch10-fig-chronologie-cadres.pdf chronologie documentaire des cadres normatifs (dates du registre C) ;
4+ ch10-fig-matrice-normes.pdf statistiques descriptives de la matrice des dispositions (sources/normes-C.csv) ;
5+ ch10-fig-grille-controle.pdf grille de détection des douze éléments dans quatre extraits FICTIFS.
6+Usage (racine du dépôt) : PYTHONPATH=analysis/python python3 analysis/python/figures/ch10_figures.py"""
7+from __future__ import annotations
8+import sys, pathlib, datetime as dt
9+import matplotlib.pyplot as plt
10+import matplotlib.dates as mdates
11+from matplotlib.lines import Line2D
12+from matplotlib.patches import Patch
13+
14+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1]))
15+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))
16+from traite.style import setup, palette, savefig, LARGEUR
17+import ch10_matrice_normes as mn
18+import ch10_controle_rapport as cr
19+
20+def fig_chronologie(ch):
21+ fig, ax = plt.subplots(figsize=(LARGEUR, 4.0))
22+ ch = ch.copy()
23+ ch["ordre"] = ch["effet"].rank(method="first")
24+ ch = ch.sort_values("effet")
25+ col = {"en_vigueur": palette.cat[0], "remplace": palette.ink2, "projet": palette.cat[1], "reference": palette.cat[2]}
26+ fin_axe = dt.date(2028, 6, 30)
27+ labels = []
28+ for i, (_, r) in enumerate(ch.iterrows()):
29+ c = col[r["statut"]]
30+ fin = r["fin"] if r["fin"] is not None else (r["effet"] if r["statut"] == "projet" else fin_axe)
31+ ls = ":" if r["statut"] == "projet" else "-"
32+ if r["statut"] != "projet":
33+ ax.plot([r["effet"], fin], [i, i], color=c, lw=2.2, solid_capstyle="butt", alpha=0.9 if r["statut"] != "remplace" else 0.6)
34+ if r["publication"] is not None:
35+ ax.plot([r["publication"], r["effet"]], [i, i], color=c, lw=1.0, ls=ls)
36+ ax.plot(r["publication"], i, marker="o", mfc="white", mec=c, ms=5.5, mew=1.3)
37+ ax.plot(r["effet"], i, marker="o", color=c, ms=5.5)
38+ if r["statut"] == "projet":
39+ ax.plot(r["effet"], i, marker="o", mfc="white", mec=c, ms=5.5, mew=1.3)
40+ if r["fin"] is not None:
41+ ax.plot(r["fin"], i, marker="|", color=c, ms=9, mew=1.6)
42+ labels.append(r["cadre"] + (" *" if r.get("verification") == "partielle" else ""))
43+ ax.axvline(mn.DATE_REF, color=palette.ink2, lw=0.8, ls="--")
44+ ax.text(mn.DATE_REF, len(ch) - 0.4, " 31 oct. 2026", fontsize=7.5, color=palette.ink2, va="bottom")
45+ ax.set_yticks(range(len(ch))); ax.set_yticklabels(labels, fontsize=8)
46+ ax.tick_params(axis="y", length=0)
47+ ax.set_ylim(-0.7, len(ch) - 0.3)
48+ ax.set_xlim(dt.date(2012, 6, 1), fin_axe)
49+ ax.xaxis.set_major_locator(mdates.YearLocator(2)); ax.xaxis.set_major_formatter(mdates.DateFormatter("%Y"))
50+ ax.grid(axis="y", visible=False)
51+ for s in ("left",): ax.spines[s].set_visible(False)
52+ h = [Line2D([0], [0], color=col["en_vigueur"], lw=2.2, label="en vigueur au 31 oct. 2026"),
53+ Line2D([0], [0], color=col["remplace"], lw=2.2, alpha=0.6, label="remplacé"),
54+ Line2D([0], [0], color=col["reference"], lw=2.2, label="norme de référence (IAAO)"),
55+ Line2D([0], [0], color=col["projet"], lw=1.0, ls=":", marker="o", mfc="white", label="projet (date proposée)"),
56+ Line2D([0], [0], color=palette.ink2, lw=0, marker="o", mfc="white", mec=palette.ink2, label="publication → entrée en vigueur")]
57+ ax.legend(handles=h, loc="upper center", bbox_to_anchor=(0.5, -0.14), fontsize=7.5, ncol=3, frameon=False)
58+ ax.set_xlabel("Année")
59+ savefig(fig, "ch10/ch10-fig-chronologie-cadres.pdf")
60+
61+def fig_matrice(st):
62+ fig, (a1, a2) = plt.subplots(1, 2, figsize=(LARGEUR, 2.9), gridspec_kw={"width_ratios": [1.15, 1]})
63+ fam = st["par_famille"].sort_values()
64+ couleurs = [palette.cat[0] if f.startswith("OEAQ") else palette.ink2 for f in fam.index]
65+ a1.barh(fam.index, fam.values, color=couleurs, height=0.62)
66+ for y, v in enumerate(fam.values):
67+ a1.text(v + 0.6, y, str(v), va="center", fontsize=8, color=palette.ink)
68+ a1.set_xlabel("Dispositions (n = %d)" % st["n"]); a1.set_xlim(0, fam.max() * 1.18)
69+ a1.set_title("Par famille de sources", loc="left", fontsize=9)
70+ a1.grid(axis="y", visible=False)
71+ top = st["par_chapitre"].head(10).sort_values()
72+ couleurs2 = [palette.cat[0] if c == "ch10" else palette.ink2 for c in top.index]
73+ a2.barh([c.replace("ch", "ch. ") for c in top.index], top.values, color=couleurs2, height=0.62)
74+ for y, v in enumerate(top.values):
75+ a2.text(v + 0.6, y, str(v), va="center", fontsize=8, color=palette.ink)
76+ a2.set_xlabel("Renvois vers le chapitre"); a2.set_xlim(0, top.max() * 1.18)
77+ a2.set_title("Dix chapitres les plus visés", loc="left", fontsize=9)
78+ a2.grid(axis="y", visible=False)
79+ fig.tight_layout(w_pad=2.0)
80+ savefig(fig, "ch10/ch10-fig-matrice-normes.pdf")
81+
82+LIBELLES_COURTS = {1: "Identification physique et légale", 2: "Droits de propriété évalués", 3: "But et fin de l'évaluation",
83+ 4: "Définition de la valeur", 5: "Date d'évaluation", 6: "Étendue du travail", 7: "Réserves et hypothèses",
84+ 8: "Information et raisonnement", 9: "Usage le meilleur et le plus profitable", 10: "Rejet d'une méthode",
85+ 11: "Conformité et écarts aux directives", 12: "Attestation"}
86+
87+def fig_grille(out):
88+ extraits = list(cr.EXTRAITS.keys())
89+ fig, ax = plt.subplots(figsize=(LARGEUR, 3.4))
90+ for j, e in enumerate(cr.ELEMENTS):
91+ for i, nom in enumerate(extraits):
92+ strict = out["controle"][(nom, "strict")][e.cle][0]
93+ souple = out["controle"][(nom, "souple")][e.cle][0]
94+ if strict:
95+ ax.scatter(i, j, s=120, marker="s", color=palette.cat[0], zorder=3)
96+ elif souple:
97+ ax.scatter(i, j, s=120, marker="s", facecolor="white", edgecolor=palette.cat[1], linewidth=1.6, zorder=3)
98+ else:
99+ ax.scatter(i, j, s=40, marker="x", color=palette.ink2, linewidth=1.0, zorder=3)
100+ ax.set_xticks(range(len(extraits)))
101+ ax.set_xticklabels([f"Extrait {n}\n{out['taux'][(n,'strict')]*12:.0f}/12 (strict)" for n in extraits], fontsize=8)
102+ ax.set_yticks(range(len(cr.ELEMENTS)))
103+ ax.set_yticklabels([f"{e.no}. {LIBELLES_COURTS[e.no]}" for e in cr.ELEMENTS], fontsize=7.5)
104+ ax.invert_yaxis(); ax.set_xlim(-0.6, len(extraits) - 0.4)
105+ ax.grid(False)
106+ for s in ("left", "bottom"): ax.spines[s].set_visible(False)
107+ ax.tick_params(length=0)
108+ h = [Line2D([0], [0], marker="s", color=palette.cat[0], lw=0, ms=8, label="détecté (lexique strict)"),
109+ Line2D([0], [0], marker="s", mfc="white", mec=palette.cat[1], mew=1.6, lw=0, ms=8, label="détecté seulement par le lexique souple"),
110+ Line2D([0], [0], marker="x", color=palette.ink2, lw=0, ms=6, label="non détecté")]
111+ ax.legend(handles=h, loc="upper center", bbox_to_anchor=(0.5, -0.16), ncol=3, fontsize=7.5, frameon=False)
112+ savefig(fig, "ch10/ch10-fig-grille-controle.pdf")
113+
114+def main():
115+ setup()
116+ res = mn.main(verbose=False)
117+ out = cr.main(verbose=False)
118+ fig_chronologie(res["chronologie"])
119+ fig_matrice(res["stats"])
120+ fig_grille(out)
121+ print("figures du chapitre 10 écrites dans assets/figures/ch10/")
122+
123+if __name__ == "__main__":
124+ main()
added analysis/python/tests/test_ch01.py +169 −0
@@ -0,0 +1,169 @@
1+"""Chapitre 1 — vérification des identités et des chiffres cités dans le texte et les solutions (données synthétiques, cas 1)."""
2+import sys, pathlib, math
3+import numpy as np, pytest
4+from scipy.stats import norm
5+from scipy.optimize import brentq
6+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))
7+import ch01_valeur_prix as c1
8+
9+@pytest.fixture(scope="module")
10+def t():
11+ return c1.tout()
12+
13+# ---------------------------------------------------------------------------- modèle et dispersion
14+
15+def test_modele_et_dispersion(t):
16+ m, d = t["modele"], t["dispersion"]
17+ assert m["n"] == 600 and m["k"] == 16
18+ assert m["sigma"] == pytest.approx(0.0830, abs=0.0005)
19+ assert d["ecart_type_residus"] == pytest.approx(0.0820, abs=0.0005)
20+ assert m["r2"] == pytest.approx(0.853, abs=0.001) and m["r2_ajuste"] == pytest.approx(0.849, abs=0.001)
21+ assert d["part_dans_pm10pct"] == pytest.approx(0.745, abs=0.002) and d["part_dans_pm5pct"] == pytest.approx(0.448, abs=0.002)
22+ assert abs(d["asymetrie"]) < 0.15 and abs(d["aplatissement_exces"]) < 0.5
23+ assert abs(float(np.mean(m["residus"]))) < 1e-8 # MCO avec constante : résidus centrés
24+
25+def test_esperance_conditionnelle_sujet(t):
26+ p = t["pred"]; sigma = p["sigma"]
27+ assert p["mediane"] == pytest.approx(635821, abs=1)
28+ assert p["mediane"] == pytest.approx(math.exp(p["ln_pred"]), rel=1e-12)
29+ assert p["moyenne_lognormale"] == pytest.approx(p["mediane"] * math.exp(sigma ** 2 / 2), rel=1e-12)
30+ assert p["mode_lognormal"] == pytest.approx(p["mediane"] * math.exp(-sigma ** 2), rel=1e-12)
31+ assert p["facteur_duan"] == pytest.approx(1.00335, abs=0.00005)
32+ assert p["facteur_duan"] == pytest.approx(math.exp(sigma ** 2 / 2), abs=0.001) # Duan ≈ correction log-normale
33+ assert p["moyenne_duan"] == pytest.approx(637953, abs=1) and p["mode_lognormal"] == pytest.approx(631455, abs=1)
34+ assert p["ip_bas"] == pytest.approx(539682, abs=1) and p["ip_haut"] == pytest.approx(749087, abs=1)
35+ assert p["ip_bas"] < p["mode_lognormal"] < p["mediane"] < p["moyenne_lognormale"] < p["ip_haut"]
36+ # l'intervalle est symétrique en log autour de la prédiction
37+ assert math.log(p["ip_haut"]) - p["ln_pred"] == pytest.approx(p["ln_pred"] - math.log(p["ip_bas"]), rel=1e-9)
38+
39+def test_probabilites_ecart(t):
40+ sigma = t["modele"]["sigma"]
41+ assert c1.proba_ecart(sigma, 0.10) == pytest.approx(0.251, abs=0.002)
42+ assert c1.proba_ecart(sigma, 0.05) == pytest.approx(0.557, abs=0.002)
43+ assert c1.proba_ecart(sigma, 0.20) == pytest.approx(0.0281, abs=0.0005)
44+ assert c1.proba_ecart(sigma, 0.0) == pytest.approx(1.0)
45+ assert c1.proba_ecart(0.04, 0.10) < c1.proba_ecart(0.083, 0.10) < c1.proba_ecart(0.16, 0.10)
46+
47+# ---------------------------------------------------------------------------- principes
48+
49+def test_contributions(t):
50+ c = t["contrib"]; p = t["pred"]
51+ assert c.loc["garage", "contribution_dollars"] == pytest.approx(20291, abs=1)
52+ assert c.loc["sous_sol", "contribution_dollars"] == pytest.approx(13829, abs=1)
53+ assert c.loc["etages", "contribution_dollars"] == pytest.approx(18860, abs=1)
54+ assert c.loc["ln_S", "contribution_dollars"] == pytest.approx(53893, abs=1)
55+ assert c.loc["age", "contribution_dollars"] + c.loc["age2_100", "contribution_dollars"] == pytest.approx(-84497, abs=2)
56+ assert c.loc["mois", "contribution_dollars"] == pytest.approx(57385, abs=1)
57+ # identité : constante + somme des β_k x_k = ln V̂
58+ const = float(t["modele"]["beta"][0])
59+ assert const + c["beta_x"].sum() == pytest.approx(p["ln_pred"], abs=1e-9)
60+ # contribution par retrait : P̂ [1 − exp(−βx)]
61+ assert c.loc["garage", "contribution_dollars"] == pytest.approx(p["mediane"] * (1 - math.exp(-c.loc["garage", "beta_x"])), rel=1e-12)
62+ assert c.loc["garage", "effet_pct_unitaire"] == pytest.approx(0.0330, abs=0.0002)
63+ assert c.loc["piscine", "effet_pct_unitaire"] == pytest.approx(0.0267, abs=0.0002)
64+ assert c.loc["qualite", "effet_pct_unitaire"] == pytest.approx(0.0630, abs=0.0002)
65+
66+def test_rendements_decroissants(t):
67+ r = t["rendements"]; p = t["pred"]
68+ assert r["elasticite_S"] == pytest.approx(0.5391, abs=0.0005)
69+ assert r["prix_moyen_m2"] == pytest.approx(3853, abs=1) and r["prix_marginal_m2"] == pytest.approx(2077, abs=1)
70+ assert r["prix_marginal_m2"] == pytest.approx(r["elasticite_S"] * r["prix_moyen_m2"], rel=1e-12)
71+ assert r["gain_10m2"] == pytest.approx(20491, abs=1) and r["gain_10m2_suivants"] == pytest.approx(19958, abs=1)
72+ assert r["gain_10m2_suivants"] < r["gain_10m2"]
73+ assert r["gain_10m2"] + r["gain_10m2_suivants"] == pytest.approx(p["mediane"] * ((185 / 165) ** r["elasticite_S"] - 1), rel=1e-9)
74+
75+def test_anticipation(t):
76+ a = t["anticipation"]
77+ assert a["croissance_mensuelle"] == pytest.approx(0.003642, abs=0.00001)
78+ assert a["croissance_annuelle"] == pytest.approx(0.0446, abs=0.0002)
79+ assert a["croissance_annuelle"] == pytest.approx((1 + a["croissance_mensuelle"]) ** 12 - 1, rel=1e-12)
80+ assert a["mois_depuis_reference"] == pytest.approx(26.0, abs=0.1)
81+ assert a["facteur_cumule"] == pytest.approx(1.0992, abs=0.0002)
82+ assert a["valeur_a_la_reference"] == pytest.approx(578436, abs=1)
83+ assert a["valeur_role"] == 468000
84+
85+def test_conformite(t):
86+ c = t["conformite"]
87+ assert c.loc["Aylmer", "effet_pct"] == pytest.approx(0.0892, abs=0.0002) and c.loc["Aylmer", "valeur"] == pytest.approx(692518, abs=1)
88+ assert c.loc["Masson-Angers", "effet_pct"] == pytest.approx(-0.2314, abs=0.0002) and c.loc["Masson-Angers", "valeur"] == pytest.approx(488712, abs=1)
89+ assert c.loc["Buckingham", "valeur"] == pytest.approx(494999, abs=1) and c.loc["Gatineau", "valeur"] == pytest.approx(621798, abs=1)
90+ assert c.loc["Hull", "valeur"] == pytest.approx(t["pred"]["mediane"])
91+
92+def test_comparables_ch16(t):
93+ c = t["comparables"].set_index("id_vente")
94+ assert list(c.index) == ["G0574", "G0404", "G0468", "G0477", "G0575"]
95+ assert c.loc["G0574", "ecart_pct"] == pytest.approx(0.007, abs=0.001)
96+ assert c.loc["G0404", "ecart_pct"] == pytest.approx(-0.149, abs=0.001)
97+ assert c.loc["G0468", "ecart_pct"] == pytest.approx(-0.099, abs=0.001)
98+ assert c.loc["G0477", "ecart_pct"] == pytest.approx(-0.105, abs=0.001)
99+ assert c.loc["G0575", "ecart_pct"] == pytest.approx(-0.080, abs=0.001)
100+ assert c.loc["G0404", "valeur_estimee"] == pytest.approx(595030, abs=1)
101+ assert (c["ecart_pct"] == pytest.approx(np.exp(c["residu_log"].values) - 1, rel=1e-9)) if "residu_log" in c else True
102+
103+# ---------------------------------------------------------------------------- recherche
104+
105+def test_recherche(t):
106+ s = t["recherche"]; m = t["pred"]["mediane"]; P = c1.PARAMS_RECHERCHE
107+ assert s["prix_reserve"] / m == pytest.approx(1.0267, abs=0.0005)
108+ assert s["prix_moyen"] / m == pytest.approx(1.068, abs=0.003)
109+ assert s["cv_prix"] == pytest.approx(0.0317, abs=0.001)
110+ assert s["delai_moyen"] == pytest.approx(5.08, abs=0.1) and s["delai_theorique"] == pytest.approx(5.05, abs=0.02)
111+ assert s["delai_moyen"] == pytest.approx(s["delai_theorique"], rel=0.03)
112+ assert s["prix"].min() >= s["prix_reserve"] # aucun prix accepté sous le prix de réserve
113+ # point fixe : W = −c + δ [p E max(O, W) + (1 − p) W]
114+ r = s["prix_reserve"]; delta = 1 / (1 + P["taux_annuel"] / 12); c = P["c_mois"] * m
115+ z = np.linspace(-6, 6, 20001); w = norm.pdf(z); w /= w.sum(); O = m * np.exp(P["sigma_o"] * z)
116+ EmaxO = float(np.sum(w * np.maximum(O, r)))
117+ assert r == pytest.approx(-c + delta * (P["p_offre"] * EmaxO + (1 - P["p_offre"]) * r), rel=1e-4)
118+ tab = t["recherche_tableau"]
119+ assert tab.loc[0.002, "prix_reserve_sur_V"] == pytest.approx(1.0377, abs=0.0005) and tab.loc[0.002, "delai_moyen_mois"] == pytest.approx(6.22, abs=0.15)
120+ assert tab.loc[0.015, "prix_reserve_sur_V"] == pytest.approx(0.9869, abs=0.0005) and tab.loc[0.015, "delai_moyen_mois"] == pytest.approx(2.88, abs=0.1)
121+ assert tab["prix_reserve_sur_V"].is_monotonic_decreasing and tab["delai_moyen_mois"].is_monotonic_decreasing and tab["prix_moyen_sur_V"].is_monotonic_decreasing
122+
123+def test_phmu(t):
124+ p = t["phmu"]
125+ assert list(p["valeur_residuelle_terrain"]) == [170_000, 160_000, 140_000]
126+ assert p["valeur_residuelle_terrain"].idxmax() == "Unifamiliale (usage actuel)"
127+
128+def test_sensibilite_sigma(t):
129+ s = t["sensibilite"]
130+ assert s.loc[0.04, "ip95_bas_pct"] == pytest.approx(-0.0754, abs=0.0003) and s.loc[0.04, "ip95_haut_pct"] == pytest.approx(0.0816, abs=0.0003)
131+ assert s.loc[0.083, "ip95_bas_pct"] == pytest.approx(-0.150, abs=0.001) and s.loc[0.083, "ip95_haut_pct"] == pytest.approx(0.177, abs=0.001)
132+ assert s.loc[0.16, "ip95_haut_pct"] == pytest.approx(0.368, abs=0.001) and s.loc[0.16, "proba_ecart_10pct"] == pytest.approx(0.551, abs=0.002)
133+ assert s.loc[0.04, "proba_ecart_10pct"] == pytest.approx(0.0172, abs=0.0005)
134+ assert s.loc[0.083, "moyenne_sur_mediane"] == pytest.approx(0.0035, abs=0.0001) and s.loc[0.16, "moyenne_sur_mediane"] == pytest.approx(0.0129, abs=0.0001)
135+ assert list(s["n_pour_se_2pct"]) == [16, 36, 68, 142, 251]
136+
137+# ---------------------------------------------------------------------------- solutions des exercices
138+
139+def test_solutions_exercices():
140+ P = 635821.26
141+ # ex. 1.2 : contribution d'un ajout = P [exp(β) − 1]
142+ assert P * (math.exp(0.02633) - 1) == pytest.approx(16964, abs=2)
143+ assert P * (math.exp(0.03243) - 1) == pytest.approx(20958, abs=2)
144+ # ex. 1.3 : prix moyen, marginal, agrandissement de 25 m²
145+ b = 0.53907
146+ assert P / 165 == pytest.approx(3853, abs=1) and b * P / 165 == pytest.approx(2077, abs=1)
147+ assert P * ((190 / 165) ** b - 1) == pytest.approx(50241, abs=2)
148+ assert P * ((190 / 165) ** b - 1) / 25 == pytest.approx(2010, abs=1)
149+ # ex. 1.4 : log-normale σ = 0,12, médiane 500 000
150+ s, med = 0.12, 500_000
151+ assert med * math.exp(s ** 2 / 2) == pytest.approx(503613, abs=1) and med * math.exp(-s ** 2) == pytest.approx(492852, abs=1)
152+ assert med * math.exp(-1.96 * s) == pytest.approx(395206, abs=1) and med * math.exp(1.96 * s) == pytest.approx(632581, abs=1)
153+ assert c1.proba_ecart(s, 0.10) == pytest.approx(0.427, abs=0.001)
154+ # ex. 1.5 : Gordon et coût d'usage
155+ assert 24_000 / (0.06 - 0.02) == pytest.approx(600_000)
156+ assert 600_000 * (0.045 + 0.01 + 0.015 - 0.02) == pytest.approx(30_000) and 600_000 * (0.06 + 0.01 + 0.015 - 0.02) == pytest.approx(39_000)
157+ # ex. 1.6 : prix de réserve avec offres uniformes sur [0,9 ; 1,1], c = 0,005, δ = 1
158+ a, bb = 0.9, 1.1
159+ def fixe(c):
160+ return brentq(lambda r: -c + r * (r - a) / (bb - a) + (bb ** 2 - r ** 2) / (2 * (bb - a)) - r, a, bb)
161+ r1 = fixe(0.005); assert r1 == pytest.approx(1.0553, abs=0.0001)
162+ assert (bb - a) / (bb - r1) == pytest.approx(4.47, abs=0.01) and (r1 + bb) / 2 == pytest.approx(1.0776, abs=0.0001)
163+ r2 = fixe(0.02); assert r2 == pytest.approx(1.0106, abs=0.0001) and (bb - a) / (bb - r2) == pytest.approx(2.24, abs=0.01)
164+ assert r1 == pytest.approx(1.1 - math.sqrt(0.4 * 0.005), abs=1e-9) # forme fermée : r = b − sqrt(2c(b−a))
165+ # ex. 1.7 : ancrage, λ par MCO sans constante
166+ x = np.array([40, -30, 25, 60, -20, 15.0]); y = np.array([18, -12, 9, 27, -10, 6.0])
167+ lam = float(x @ y / (x @ x)); assert lam == pytest.approx(0.437, abs=0.001)
168+ assert lam * 50 == pytest.approx(21.9, abs=0.05)
169+ assert c1.estimation_ancree(600_000, 650_000, lam) == pytest.approx(600_000 + lam * 50_000)
added analysis/python/tests/test_ch10.py +94 −0
@@ -0,0 +1,94 @@
1+"""Chapitre 10 — vérification des chiffres cités dans le texte, les tableaux et les solutions."""
2+import sys, pathlib, datetime as dt
3+import pytest
4+sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))
5+import ch10_controle_rapport as cr
6+import ch10_matrice_normes as mn
7+
8+@pytest.fixture(scope="module")
9+def out():
10+ return cr.main(verbose=False)
11+
12+@pytest.fixture(scope="module")
13+def mat():
14+ return mn.main(verbose=False)
15+
16+# --- contrôle lexical des douze éléments (tableau 10.7, figure grille) -------------------------------------------
17+def test_douze_elements_definis():
18+ assert [e.no for e in cr.ELEMENTS] == list(range(1, 13))
19+ assert len({e.cle for e in cr.ELEMENTS}) == 12
20+
21+def test_extrait_complet_et_lacunaires(out):
22+ assert out["manquants"][("D", "strict")] == [] and out["taux"][("D", "strict")] == 1.0
23+ assert out["manquants"][("A", "strict")] == [1, 2, 4, 5, 6, 7, 9, 10, 11, 12]
24+ assert out["manquants"][("A", "souple")] == [2, 4, 5, 7, 9, 10, 11]
25+ assert out["manquants"][("B", "strict")] == [6, 10]
26+ assert out["manquants"][("C", "strict")] == [2, 3, 4, 9, 11, 12]
27+ assert out["taux"][("A", "strict")] == pytest.approx(2 / 12)
28+ assert out["taux"][("B", "strict")] == pytest.approx(10 / 12)
29+ assert out["taux"][("C", "strict")] == pytest.approx(0.5)
30+
31+def test_lexique_souple_jamais_plus_severe(out):
32+ for nom in cr.EXTRAITS:
33+ s, l = out["manquants"][(nom, "strict")], out["manquants"][(nom, "souple")]
34+ assert set(l) <= set(s)
35+
36+def test_taux_conformite_identite():
37+ res = {e.cle: (e.no % 2 == 0, None) for e in cr.ELEMENTS}
38+ assert cr.taux_conformite(res) == pytest.approx(0.5)
39+ assert cr.manquants(res) == [1, 3, 5, 7, 9, 11]
40+
41+def test_controle_insensible_aux_accents_et_casse():
42+ r1 = cr.controler("DATE D'ÉVALUATION : 1er septembre 2026. J'ATTESTE par la présente.")
43+ r2 = cr.controler("date d'evaluation : 1er septembre 2026. j'atteste par la presente.")
44+ assert r1["date"][0] and r1["attestation"][0] and r1 == r2
45+
46+# --- probabilité de détection par révisions indépendantes (tableau 10.8) ---------------------------------------
47+def test_p_detection(out):
48+ assert cr.p_detection(0.5, 1) == 0.5 and cr.p_detection(0.5, 2) == 0.75 and cr.p_detection(0.5, 3) == 0.875
49+ assert cr.p_detection(0.7, 2) == pytest.approx(0.91) and cr.p_detection(0.7, 3) == pytest.approx(0.973)
50+ assert cr.p_detection(0.9, 2) == pytest.approx(0.99) and cr.p_detection(0.9, 3) == pytest.approx(0.999)
51+ assert cr.p_detection(0.0, 5) == 0.0 and cr.p_detection(1.0, 1) == 1.0
52+ vals = [cr.p_detection(0.7, n) for n in range(1, 6)]
53+ assert all(b > a for a, b in zip(vals, vals[1:]))
54+
55+# --- hypothèse extraordinaire : incidence et espérance (section 10.2, exercice 10.5) ---------------------------
56+def test_incidence_hypothese(out):
57+ dv, dr = out["incidence_B"]
58+ assert dv == 37000 and dr == pytest.approx(0.0643, abs=0.0001)
59+ assert out["esperance_B"] == pytest.approx(597200)
60+ assert cr.esperance_sous_incertitude(1.0, 612000, 575000) == 612000
61+ assert cr.esperance_sous_incertitude(0.0, 612000, 575000) == 575000
62+
63+# --- échéances réglementaires (exercice 10.2) -----------------------------------------------------------------
64+def test_conservation_et_formation(out):
65+ assert out["conservation"] == dt.date(2031, 11, 15)
66+ f = out["formation"]
67+ assert f["deficit_total"] == 0 and f["deficit_ethique"] == 1 and f["report_suivant"] == 1 and not f["conforme"]
68+ g = out["formation_b"]
69+ assert g["conforme"] and g["report_suivant"] == 3
70+ h = cr.bilan_formation(heures_total=20, heures_ethique=3, report_anterieur=8) # report plafonné à 5 h
71+ assert h["deficit_total"] == 5 and not h["conforme"]
72+
73+# --- statistiques de la matrice des dispositions (tableau 10.9, figure matrice) ---------------------------------
74+def test_matrice_normes(mat):
75+ st = mat["stats"]
76+ assert st["n"] == 100 and st["n_domaines"] == 17 and st["n_chapitres_couverts"] == 28
77+ assert st["par_domaine"]["OEAQ-NPP"] == 15 and st["par_domaine"]["OEAQ-Déontologie"] == 14 and st["par_domaine"]["OEAQ-Règlements"] == 12
78+ assert st["par_domaine"]["IVS"] == 11 and st["par_domaine"]["CUSPAP"] == 8 and st["par_domaine"]["IAAO"] == 6
79+ assert st["par_famille"]["Cadres hors Québec"] == 27 and st["par_famille"]["Finance et prudentiel"] == 20
80+ assert st["n_oeaq"] == 43 and st["n_ch10"] == 51
81+ assert st["par_chapitre"]["ch10"] == 51 and st["par_chapitre"]["ch31"] == 15 and st["par_chapitre"]["ch20"] == 14
82+ assert st["moy_chapitres_par_disposition"] == pytest.approx(1.70) and st["max_chapitres_par_disposition"] == 4
83+ assert st["part_multi_chapitres"] == pytest.approx(0.51)
84+ assert st["par_famille"].sum() == 100
85+
86+def test_chronologie(mat):
87+ ch = mat["chronologie"].set_index("cadre")
88+ assert ch.loc["CUSPAP 2024 (Canada)", "en_vigueur_2026_10_31"] == False
89+ assert ch.loc["IVS 2028 — projet (exposure draft)", "en_vigueur_2026_10_31"] == False
90+ assert ch["en_vigueur_2026_10_31"].sum() == 8
91+ assert ch.loc["IVS 2025 (IVSC)", "delai_publication_effet_jours"] == 366
92+ assert ch.loc["RICS Red Book Global (2025)", "delai_publication_effet_jours"] == 51
93+ assert ch.loc["NPP de l'OEAQ (Québec)", "effet"] == dt.date(2024, 2, 1)
94+ assert ch.loc["CUSPAP 2026 (Canada)", "effet"] == dt.date(2026, 4, 1)
added assets/figures/ch01/ch01-fig-prix-valeur.pdf +0 −0

Binary file not shown.

added assets/figures/ch01/ch01-fig-recherche.pdf +0 −0

Binary file not shown.

added assets/figures/ch01/ch01-fig-residus.pdf +0 −0

Binary file not shown.

added assets/figures/ch01/ch01-fig-rosen.pdf +0 −0

Binary file not shown.

added assets/figures/ch01/ch01-fig-sensibilite-sigma.pdf +0 −0

Binary file not shown.

added assets/figures/ch02/ch02-fig-decomposition-droits.pdf +0 −0

Binary file not shown.

added assets/figures/ch02/ch02-fig-ecart-logements.pdf +0 −0

Binary file not shown.

added assets/figures/ch06/ch06-fig-emphyteose.pdf +0 −0

Binary file not shown.

added assets/figures/ch06/ch06-fig-usufruit.pdf +0 −0

Binary file not shown.

added assets/figures/ch07/ch07-fig-fonds-prevoyance.pdf +0 −0

Binary file not shown.

added assets/figures/ch07/ch07-fig-paires-servitude.pdf +0 −0

Binary file not shown.

added assets/figures/ch07/ch07-fig-repartition-charges.pdf +0 −0

Binary file not shown.

added assets/figures/ch07/ch07-fig-sensibilite-cotisation.pdf +0 −0

Binary file not shown.

added assets/figures/ch08/ch08-fig-carte-zones.pdf +0 −0

Binary file not shown.

added assets/figures/ch08/ch08-fig-option-sensibilite.pdf +0 −0

Binary file not shown.

added assets/figures/ch08/ch08-fig-rezonage-paires.pdf +0 −0

Binary file not shown.

added assets/figures/ch10/ch10-fig-chronologie-cadres.pdf +0 −0

Binary file not shown.

added assets/figures/ch10/ch10-fig-grille-controle.pdf +0 −0

Binary file not shown.

added assets/figures/ch10/ch10-fig-matrice-normes.pdf +0 −0

Binary file not shown.

added data/raw/gatineau_r994_2025/README.md +7 −0
@@ -0,0 +1,7 @@
1+# Règlement 994-2025 de la Ville de Gatineau — taux de taxes 2026 (extraction du 2026-10-31)
2+
3+- **Source** : Ville de Gatineau, *Règlement numéro 994-2025 décrétant l'imposition et le prélèvement des taxes foncières générales, spéciales et des compensations pour le budget de l'année 2026*, adopté le 18 décembre 2025 (avis de motion AM-2025-790 du 16 décembre 2025). PDF de 21 pages téléchargé le 2026-10-31 : https://docweb.gatineau.ca/Doc-Web/masson/documents/pdf/R%20994-2025.pdf (clé biblatex `gatineaureglement994`, source SRC-E-028 / SRC-09-001).
4+- **Statut** : document public municipal (droit d'auteur de la Ville de Gatineau) ; les taux sont des faits réglementaires cités avec attribution. Le PDF lui-même n'est pas redistribué dans le dépôt.
5+- **Fichier** : `annexe1_taux_voisinage.csv` — les 1 092 paires (unité de voisinage, taux 2026 par 1 000 $ d'évaluation) de l'annexe I (« Taxe générale résidentielle — Taux des unités de voisinage »), extraites par `pdftotext -layout` puis expression régulière `([A-Z]{1,2}\d{2,3})\s+(\d,\d{3})` ; décimale convertie en point. Un code d'unité apparaît deux fois dans l'annexe (1 091 codes distincts pour 1 092 lignes) ; les deux lignes sont conservées telles quelles.
6+- **Autres taux lus dans le règlement (par 1 000 $ d'évaluation)** : taux de base 5,224 (art. 1) ; non résidentiel 29,372 (art. 2) ; industriel 26,120 (art. 3) ; terrains vagues desservis 20,896 (art. 4) ; agricole 5,090 et forestier 5,090 (art. 5-6) ; résidentiel (catégorie résiduelle autre que six logements ou plus) : taux de l'unité de voisinage, annexe I (art. 7) ; sous-catégorie des six logements ou plus 6,964 (art. 8) ; fonds dédié aux infrastructures 0,579 inclus dans les taux (art. 10) ; taxe spéciale transport en commun (STO) 1,417 (art. 11) ; taxe spéciale service de la dette 0,830 (art. 12) ; crédits aqueduc 0,386 et égout 0,523 pour les immeubles non raccordés (art. 13-14) ; taxe spéciale ex-Ville de Hull 0,036 (art. 15, 1°) ; compensation matières résiduelles 178,83 $ par logement et 59,61 $ par chambre locative (art. 16) ; eau au compteur 0,983 $/m³ au-delà de 300 m³ (art. 18) ; piscine 80 $ (art. 19).
7+- **Statistiques calculées** (`analysis/python/cases/ch09_ratios.py`) : minimum 1,613 ; maximum 7,544 ; médiane 5,232 ; moyenne 5,377.
added data/raw/gatineau_r994_2025/annexe1_taux_voisinage.csv +1093 −0
@@ -0,0 +1,1093 @@
1+unite_voisinage,taux_2026_par_1000
2+A701,5.650
3+AJ11,4.722
4+AL11,5.571
5+A800,4.061
6+AJ12,5.356
7+AL13,5.020
8+A999,7.544
9+AJ13,4.998
10+AL14,4.893
11+AA05,5.582
12+AJ18,5.030
13+AL15,4.713
14+AA15,5.833
15+AJ19,5.101
16+AL18,4.722
17+AC02,5.644
18+AJ20,5.037
19+AL21,4.962
20+AC04,5.920
21+AJ21,4.987
22+AL22,5.549
23+AC07,4.547
24+AJ22,5.008
25+AL23,5.096
26+AC11,5.259
27+AJ23,5.720
28+AL25,5.688
29+AC12,4.919
30+AJ24,5.041
31+AL26,5.407
32+AC18,4.061
33+AJ27,4.965
34+AL27,5.444
35+AC19,4.061
36+AJ28,4.886
37+AL28,4.061
38+AD08,4.730
39+AJ29,4.897
40+AL29,5.404
41+AD09,5.594
42+AJ30,5.085
43+AL30,5.265
44+AD10,4.061
45+AJ32,4.682
46+AL32,5.530
47+AD11,7.544
48+AJ34,5.102
49+AL33,5.412
50+AD12,7.544
51+AJ35,5.750
52+AL34,4.934
53+AD19,4.742
54+AJ36,4.988
55+AL35,5.398
56+AD35,4.061
57+AJ37,5.062
58+AL37,5.925
59+AD56,7.175
60+AJ39,5.070
61+AL40,5.133
62+AD57,6.057
63+AJ40,4.891
64+AL41,4.814
65+AD60,7.360
66+AJ41,4.891
67+AL42,5.463
68+AG06,6.646
69+AJ42,5.268
70+AL43,6.677
71+AI70,4.061
72+AJ43,5.212
73+AL44,5.575
74+AI71,4.061
75+AJ44,4.803
76+AL45,5.390
77+AI72,5.213
78+AJ45,5.166
79+AL47,5.299
80+AJ00,4.624
81+AJ46,4.983
82+AL51,5.278
83+AJ01,4.822
84+AJ47,5.170
85+AM01,4.923
86+AJ02,5.468
87+AJ48,5.672
88+AM02,5.311
89+AJ03,4.837
90+AL02,5.276
91+AM04,5.818
92+AJ04,4.780
93+AL03,4.599
94+AM05,5.369
95+AJ05,4.547
96+AL04,4.476
97+AM06,4.950
98+AJ06,5.490
99+AL07,6.366
100+AM08,5.286
101+AJ08,4.832
102+AL08,5.749
103+AM11,5.581
104+AJ09,4.744
105+AL09,5.534
106+AM13,5.406
107+AJ10,5.196
108+AL10,5.152
109+AM15,4.420
110+AM16,5.981
111+AO42,5.483
112+AR75,5.428
113+AM20,4.987
114+AO43,5.054
115+AR76,5.769
116+AM21,5.876
117+AO44,5.431
118+AR77,4.621
119+AM23,4.682
120+AO45,7.049
121+AR78,5.114
122+AM24,5.624
123+AO46,5.597
124+AR79,4.960
125+AM25,4.971
126+AO47,5.320
127+AR80,4.897
128+AM26,5.274
129+AO48,4.988
130+AR81,4.699
131+AM27,5.229
132+AO49,7.264
133+AR82,5.098
134+AO02,4.858
135+AO50,5.404
136+AR83,5.221
137+AO03,4.126
138+AO51,6.772
139+AR84,4.701
140+AO04,5.500
141+AO52,6.379
142+AR85,5.167
143+AO05,5.199
144+AO53,5.298
145+AR86,5.301
146+AO06,5.847
147+AP16,6.472
148+AR87,4.640
149+AO07,6.020
150+AP20,5.624
151+AR88,5.548
152+AO09,4.761
153+AP21,6.427
154+AR89,5.151
155+AO10,5.573
156+AP22,4.907
157+AU01,5.913
158+AO11,4.341
159+AP42,4.061
160+AU02,4.713
161+AO12,4.448
162+AP91,6.157
163+AU03,5.314
164+AO14,4.959
165+AP94,6.625
166+AU04,4.966
167+AO15,6.165
168+AP95,7.529
169+AU05,4.992
170+AO16,6.785
171+AQ90,5.159
172+AU06,4.826
173+AO17,4.901
174+AQ91,4.299
175+AU07,5.176
176+AO18,5.963
177+AR50,4.883
178+AU08,4.770
179+AO19,5.050
180+AR51,4.740
181+AU09,6.266
182+AO20,5.298
183+AR52,4.061
184+AU10,5.246
185+AO21,4.879
186+AR53,4.903
187+AU11,5.286
188+AO22,6.464
189+AR54,4.670
190+AU12,5.007
191+AO23,5.026
192+AR55,4.177
193+AU13,5.020
194+AO24,5.726
195+AR56,5.396
196+AU14,4.875
197+AO25,4.922
198+AR57,4.522
199+AU15,5.126
200+AO26,4.771
201+AR58,4.563
202+AU16,4.962
203+AO27,4.699
204+AR59,5.116
205+AU17,5.000
206+AO28,4.528
207+AR60,4.579
208+AU18,4.932
209+AO29,4.863
210+AR61,5.056
211+AU19,5.320
212+AO30,5.227
213+AR62,4.912
214+AU20,4.781
215+AO31,5.275
216+AR63,4.599
217+AU21,5.341
218+AO32,5.077
219+AR64,5.017
220+AU22,5.211
221+AO33,4.899
222+AR65,4.857
223+AU23,5.281
224+AO34,4.895
225+AR66,4.949
226+AU24,5.515
227+AO35,5.753
228+AR67,5.506
229+AU25,4.986
230+AO36,5.768
231+AR68,4.960
232+AU26,5.197
233+AO37,5.165
234+AR69,4.962
235+AU27,5.603
236+AO38,5.083
237+AR71,5.887
238+AU28,5.030
239+AO39,5.176
240+AR72,4.983
241+AU29,5.078
242+AO40,4.987
243+AR73,5.193
244+AU30,5.173
245+AO41,5.104
246+AR74,4.666
247+AU31,4.974
248+AU32,4.748
249+BA11,5.328
250+BR56,5.583
251+AU33,5.473
252+BA13,5.319
253+BU50,4.918
254+AU35,5.158
255+BA31,5.393
256+BU51,5.492
257+AU36,4.937
258+BC03,5.127
259+BU52,4.787
260+AU37,4.841
261+BC04,5.629
262+BU53,5.415
263+AU38,4.830
264+BC05,5.690
265+BU54,5.468
266+AU40,4.829
267+BC06,4.959
268+BU55,5.168
269+AU44,5.503
270+BC07,7.181
271+BU56,4.975
272+AU45,5.285
273+BC08,5.252
274+BU57,5.059
275+AU47,5.035
276+BC09,6.143
277+BU58,5.544
278+AU60,5.189
279+BD01,6.200
280+BU59,5.823
281+AU61,5.015
282+BD06,6.182
283+BU60,4.964
284+AU62,5.049
285+BD07,7.544
286+BU61,5.056
287+AU63,5.135
288+BE60,5.624
289+BU63,4.858
290+AU64,5.244
291+BF00,5.419
292+BU65,5.412
293+AU65,5.205
294+BG00,7.544
295+BU72,5.439
296+AU66,4.804
297+BG06,7.544
298+BU73,5.124
299+AU67,4.776
300+BI02,4.061
301+BU75,4.880
302+AU68,5.460
303+BJ01,5.082
304+BU81,4.895
305+AU87,5.049
306+BJ02,4.874
307+BU85,6.186
308+AV01,6.033
309+BJ03,5.129
310+BU86,4.962
311+AV51,5.810
312+BJ05,5.035
313+BX00,4.759
314+AV52,5.866
315+BJ06,5.170
316+BX02,4.856
317+AV53,5.952
318+BJ07,4.917
319+BX30,5.180
320+AX02,4.919
321+BJ09,5.514
322+BX32,4.943
323+AX03,5.083
324+BJ10,4.992
325+BZ88,7.544
326+AX04,4.755
327+BJ15,5.559
328+G203,4.926
329+AX08,4.673
330+BL00,4.978
331+G204,5.081
332+AX10,5.396
333+BL01,5.192
334+G501,5.090
335+AX31,5.050
336+BL02,5.821
337+G503,5.395
338+AX34,4.502
339+BL40,4.689
340+G506,5.121
341+AX35,4.914
342+BL41,5.274
343+G507,5.030
344+AY50,4.721
345+BM03,5.577
346+G509,4.893
347+AY52,5.340
348+BM04,5.609
349+G510,5.173
350+AY55,4.988
351+BM05,5.650
352+G511,4.951
353+AY59,5.771
354+BM06,6.208
355+G512,4.978
356+AY70,7.544
357+BN06,4.954
358+G513,5.190
359+AY81,4.326
360+BO00,4.837
361+G514,5.030
362+AY83,7.544
363+BO01,5.159
364+G515,4.969
365+AY96,5.215
366+BP15,4.061
367+G516,5.326
368+AY97,5.244
369+BR50,4.464
370+G518,5.274
371+AY99,5.612
372+BR51,4.756
373+G520,5.080
374+B800,4.061
375+BR52,5.022
376+G521,5.470
377+B999,7.544
378+BR53,4.571
379+G522,5.912
380+BA02,5.241
381+BR54,5.054
382+G524,5.143
383+BA03,5.425
384+BR55,5.190
385+G525,5.394
386+G526,5.203
387+GC97,5.288
388+GJ18,4.703
389+G597,5.117
390+GC98,4.061
391+GJ19,5.034
392+G598,5.233
393+GC99,5.079
394+GJ20,5.016
395+G599,5.104
396+GD01,7.174
397+GJ21,4.720
398+G600,5.408
399+GD03,4.500
400+GJ23,4.328
401+G601,4.971
402+GD05,6.166
403+GJ24,5.135
404+G602,4.734
405+GD06,5.388
406+GJ25,4.823
407+G603,4.840
408+GD08,4.061
409+GJ28,4.856
410+G604,4.817
411+GD20,6.570
412+GJ29,5.072
413+G605,4.906
414+GD22,6.888
415+GJ31,4.672
416+G606,4.822
417+GD23,7.208
418+GJ37,4.730
419+G609,4.898
420+GD24,6.025
421+GJ39,6.424
422+G610,4.735
423+GD25,5.941
424+GJ40,4.862
425+G611,1.613
426+GD50,6.499
427+GJ41,4.609
428+G612,4.999
429+GD53,5.349
430+GJ42,4.889
431+G613,5.374
432+GD54,6.593
433+GJ43,4.454
434+G614,5.082
435+GD55,7.544
436+GJ44,5.252
437+G615,4.706
438+GD57,6.474
439+GJ46,4.936
440+G616,4.848
441+GE01,4.842
442+GJ47,5.136
443+G800,4.061
444+GE02,5.046
445+GJ48,4.593
446+G999,7.544
447+GE03,5.207
448+GJ49,4.771
449+GA01,5.446
450+GE04,4.205
451+GL02,5.083
452+GA03,5.620
453+GE99,5.088
454+GL13,5.082
455+GA04,5.431
456+GF00,5.541
457+GJ31,4.937
458+GA06,5.745
459+GI02,6.155
460+GL33,4.767
461+GA11,5.391
462+GI40,5.020
463+GL34,5.094
464+GA14,5.131
465+GI70,6.246
466+GL35,5.448
467+GA21,5.254
468+GI71,6.573
469+GL36,5.107
470+GA24,6.472
471+GI73,6.329
472+GL40,4.061
473+GC01,6.828
474+GJ00,4.849
475+GL50,4.691
476+GC02,5.385
477+GJ01,4.456
478+GL60,5.158
479+GC03,5.909
480+GJ02,4.893
481+GM00,6.185
482+GC04,5.943
483+GJ03,4.921
484+GM01,5.523
485+GC23,7.458
486+GJ04,4.831
487+GM02,5.867
488+GC37,7.544
489+GJ05,5.454
490+GM03,5.544
491+GC80,5.225
492+GJ06,5.036
493+GM05,5.829
494+GC81,5.763
495+GJ07,5.289
496+GM10,5.697
497+GC82,7.143
498+GJ08,5.306
499+GM11,6.534
500+GC85,5.376
501+GJ09,4.566
502+GM12,5.613
503+GC86,5.050
504+GJ10,4.864
505+GM13,5.955
506+GC87,5.244
507+GJ11,4.685
508+GM14,5.876
509+GC88,5.372
510+GJ12,4.977
511+GM15,5.738
512+GC89,6.479
513+GJ13,4.688
514+GM16,5.231
515+GC91,5.218
516+GJ15,5.159
517+GM17,5.359
518+GC94,5.622
519+GJ16,5.038
520+GM18,5.974
521+GC95,6.323
522+GJ17,4.925
523+GM19,6.091
524+GM20,5.602
525+GO26,6.609
526+GP38,4.061
527+GM21,5.484
528+GO27,5.616
529+GP39,4.207
530+GM22,5.564
531+GO28,5.764
532+GP53,5.934
533+GM23,5.657
534+GO29,7.025
535+GP56,7.544
536+GM24,5.352
537+GO31,4.610
538+GP88,4.061
539+GM25,5.604
540+GO32,5.555
541+GP89,6.164
542+GM26,5.475
543+GO34,5.278
544+GP98,5.983
545+GM28,5.201
546+GO35,4.799
547+GQ90,5.108
548+GM30,5.549
549+GO36,5.212
550+GR50,5.447
551+GM31,5.311
552+GO37,4.661
553+GR51,4.740
554+GM32,4.995
555+GO38,5.174
556+GR52,5.012
557+GM33,5.413
558+GO39,6.541
559+GR53,5.070
560+GM34,4.925
561+GO40,6.552
562+GR54,5.263
563+GM35,5.088
564+GO42,4.489
565+GR55,4.762
566+GM36,5.408
567+GO43,5.155
568+GR56,5.067
569+GM37,5.510
570+GO44,5.965
571+GR57,5.166
572+GM38,4.923
573+GO47,5.818
574+GR58,5.128
575+GM39,5.520
576+GO49,5.877
577+GR59,4.743
578+GM44,5.239
579+GO51,5.487
580+GR60,5.585
581+GM46,5.073
582+GO53,6.016
583+GR61,6.784
584+GM49,5.491
585+GO54,5.082
586+GR62,5.229
587+GN00,6.736
588+GO55,4.677
589+GR63,4.875
590+GN47,7.544
591+GO56,5.522
592+GR64,5.515
593+GN64,7.544
594+GO57,6.081
595+GR65,4.993
596+GN71,7.544
597+GO58,5.320
598+GR66,4.797
599+GO01,5.336
600+GO59,5.469
601+GR67,5.119
602+GO02,4.531
603+GO60,6.222
604+GR68,5.028
605+GO03,4.801
606+GO61,5.837
607+GR69,5.431
608+GO04,5.332
609+GO62,4.502
610+GR70,4.857
611+GO05,4.739
612+GO63,5.125
613+GR71,4.751
614+GO06,4.723
615+GO64,5.114
616+GR72,5.157
617+GO07,4.850
618+GO65,6.212
619+GR73,4.996
620+GO08,4.678
621+GO66,5.729
622+GR74,5.167
623+GO09,5.883
624+GO68,4.828
625+GR75,4.875
626+GO11,5.459
627+GO69,5.174
628+GR76,5.115
629+GO12,4.849
630+GO95,5.631
631+GR77,5.208
632+GO13,5.253
633+GO96,5.005
634+GR78,5.060
635+GO16,4.565
636+GO97,4.836
637+GR79,4.673
638+GO17,5.299
639+GO98,4.934
640+GR80,4.874
641+GO19,4.600
642+GO99,5.852
643+GR81,4.834
644+GO20,4.991
645+GP01,6.786
646+GR82,5.022
647+GO21,4.510
648+GP07,4.061
649+GU01,5.165
650+GO22,5.645
651+GP08,5.746
652+GU02,5.238
653+GO23,5.475
654+GP23,7.544
655+GU03,5.298
656+GO24,4.990
657+GP32,4.061
658+GU04,5.188
659+GO25,5.602
660+GP37,7.544
661+GU05,5.239
662+GU06,4.786
663+GU62,5.824
664+GX22,5.120
665+GU07,5.256
666+GU63,5.475
667+GX24,4.978
668+GU08,5.212
669+GU64,5.206
670+GX26,4.818
671+GU10,5.155
672+GU65,5.578
673+GX28,5.249
674+GU11,5.039
675+GU66,5.064
676+GX29,5.094
677+GU12,4.815
678+GU67,5.338
679+GX31,6.114
680+GU13,5.782
681+GU68,5.059
682+GX32,4.898
683+GU14,5.119
684+GU69,5.580
685+GX34,4.932
686+GU15,5.043
687+GU70,4.978
688+GX37,5.973
689+GU16,4.846
690+GU71,5.697
691+GX40,4.940
692+GU18,5.002
693+GU72,4.900
694+GX41,5.116
695+GU19,5.019
696+GU73,5.555
697+GX42,4.888
698+GU20,5.350
699+GU74,5.083
700+GX43,5.979
701+GU21,4.909
702+GU75,5.395
703+GX45,5.953
704+GU23,5.700
705+GU76,5.014
706+GX46,5.040
707+GU24,5.410
708+GU77,5.411
709+GX48,5.495
710+GU25,5.418
711+GU78,5.005
712+GY51,6.719
713+GU26,4.946
714+GU79,5.473
715+GY66,6.029
716+GU27,5.442
717+GU80,5.326
718+GY70,6.643
719+GU28,5.307
720+GU82,5.007
721+GY72,6.099
722+GU29,4.982
723+GU83,5.351
724+GY74,5.553
725+GU30,5.283
726+GU84,4.730
727+GY99,7.544
728+GU32,5.406
729+GU86,5.081
730+GZ02,6.276
731+GU34,5.014
732+GU87,4.647
733+GZ88,7.544
734+GU35,5.163
735+GU89,5.127
736+H999,7.544
737+GU36,5.559
738+GU90,5.202
739+HC02,7.520
740+GU37,5.018
741+GU91,5.106
742+HC05,5.096
743+GU38,5.215
744+GU92,5.270
745+HC16,5.900
746+GU39,5.299
747+GU93,5.454
748+HC21,6.101
749+GU40,5.251
750+GU95,5.195
751+HC22,5.322
752+GU41,5.270
753+GU96,5.291
754+HC30,7.544
755+GU42,5.375
756+GU97,5.005
757+HC31,5.173
758+GU44,4.496
759+GU98,4.989
760+HC34,6.747
761+GU45,5.435
762+GU99,4.989
763+HC37,7.144
764+GU46,5.547
765+GV00,5.843
766+HC38,5.887
767+GU47,5.256
768+GV50,6.849
769+HC39,7.544
770+GU48,5.063
771+GV52,5.714
772+HC42,6.415
773+GU49,4.837
774+GV53,5.451
775+HC43,5.786
776+GU51,5.033
777+GV56,5.711
778+HC46,5.755
779+GU52,5.314
780+GV58,5.766
781+HC47,5.980
782+GU53,5.136
783+GV60,5.706
784+HC68,5.276
785+GU56,4.974
786+GX01,5.771
787+HC71,7.544
788+GU57,5.052
789+GX02,4.873
790+HC72,7.544
791+GU58,5.184
792+GX06,4.999
793+HC81,5.256
794+GU60,5.081
795+GX20,5.212
796+HC83,5.422
797+GU61,5.492
798+GX21,5.256
799+HC84,5.189
800+HC85,5.242
801+HN30,4.292
802+HO45,4.888
803+HC86,4.610
804+HN88,5.416
805+HO46,4.672
806+HC89,4.691
807+HN89,5.412
808+HO47,5.708
809+HC91,5.668
810+HO01,6.010
811+HO48,4.998
812+HC92,5.248
813+HO02,5.700
814+HO49,4.621
815+HC95,5.202
816+HO03,4.645
817+HO51,4.156
818+HD10,7.544
819+HO04,6.297
820+HO52,5.949
821+HG04,5.488
822+HO05,5.693
823+HO54,5.586
824+HJ00,5.190
825+HO06,4.829
826+HO55,6.240
827+HJ02,4.764
828+HO07,5.738
829+HO56,6.075
830+HJ04,4.939
831+HO08,6.385
832+HO57,4.578
833+HJ05,5.336
834+HO09,5.846
835+HO58,5.328
836+HJ19,5.324
837+HO10,6.813
838+HO59,4.975
839+HJ28,4.850
840+HO11,6.386
841+HO60,5.627
842+HJ29,5.251
843+HO12,5.652
844+HO61,5.382
845+HJ31,5.304
846+HO13,4.716
847+HO62,5.894
848+HJ35,5.405
849+HO14,4.590
850+HO63,5.829
851+HM01,5.465
852+HO15,4.662
853+HO64,5.570
854+HM03,5.353
855+HO17,5.237
856+HO65,4.800
857+HM05,6.307
858+HO18,5.204
859+HO67,6.519
860+HM06,5.389
861+HO19,4.470
862+HO68,5.849
863+HM09,5.582
864+HO20,4.602
865+HO69,6.975
866+HM11,5.123
867+HO21,5.029
868+HO70,4.503
869+HM16,5.288
870+HO22,4.925
871+HO71,5.987
872+HM17,4.894
873+HO23,4.640
874+HO72,5.059
875+HM18,5.574
876+HO24,4.890
877+HO73,6.322
878+HM21,5.263
879+HO25,5.006
880+HO74,5.278
881+HM22,5.519
882+HO26,5.046
883+HO75,4.586
884+HM24,5.282
885+HO27,5.265
886+HO76,4.776
887+HM25,5.498
888+HO28,5.114
889+HO77,7.071
890+HM26,5.607
891+HO29,5.153
892+HO78,5.638
893+HM29,5.462
894+HO30,5.813
895+HO79,5.306
896+HM30,5.874
897+HO31,5.508
898+HO80,6.973
899+HM31,6.156
900+HO32,5.325
901+HO82,5.315
902+HM34,5.249
903+HO33,4.727
904+HO84,4.946
905+HM37,5.301
906+HO35,4.795
907+HO86,6.264
908+HM41,4.672
909+HO36,5.038
910+HO87,7.340
911+HM43,4.516
912+HO37,4.632
913+HO88,6.115
914+HM46,5.120
915+HO38,4.759
916+HP05,5.493
917+HM47,4.998
918+HO39,6.787
919+HP19,5.585
920+HM48,5.499
921+HO40,6.242
922+HP21,6.273
923+HM49,5.054
924+HO41,5.408
925+HP24,6.722
926+HN07,4.110
927+HO42,6.595
928+HP29,7.388
929+HN14,5.139
930+HO43,5.913
931+HP31,6.625
932+HN23,7.544
933+HO44,4.286
934+HP32,6.625
935+HP38,6.625
936+HU75,5.877
937+MC02,5.861
938+HP39,7.518
939+HU77,5.380
940+MC04,5.847
941+HP42,4.061
942+HU78,5.736
943+MC05,6.404
944+HP83,6.715
945+HV50,7.350
946+MC09,5.937
947+HP94,5.823
948+HX03,5.637
949+MC16,5.649
950+HR50,5.138
951+HX04,5.498
952+MD51,5.425
953+HR52,5.350
954+HX07,5.793
955+MD56,7.544
956+HR53,5.697
957+HX09,6.665
958+MD67,7.544
959+HR54,5.109
960+HX11,5.523
961+ME00,5.017
962+HR55,5.187
963+HX14,5.559
964+ME51,5.219
965+HR56,5.212
966+HX19,5.619
967+ME52,5.549
968+HR57,5.332
969+HX21,5.489
970+MG01,4.097
971+HR58,5.688
972+HX24,5.264
973+MI02,4.061
974+HR61,5.373
975+HX34,6.638
976+MJ02,5.933
977+HR63,4.984
978+HX61,5.480
979+MJ03,5.291
980+HR64,5.038
981+HX65,5.707
982+MJ05,4.741
983+HR66,6.585
984+HX67,5.682
985+MJ06,5.189
986+HR72,5.156
987+HX73,5.447
988+MJ08,5.094
989+HR80,5.011
990+HX77,5.151
991+MJ09,4.862
992+HR81,5.012
993+HX96,5.364
994+MJ10,5.020
995+HR82,4.952
996+HY52,5.884
997+MJ16,4.856
998+HR83,5.013
999+HY58,5.492
1000+MJ41,4.824
1001+HR84,5.289
1002+HY59,5.439
1003+MJ42,4.655
1004+HR85,4.851
1005+HY60,6.205
1006+MJ44,4.991
1007+HU01,5.071
1008+HY61,6.740
1009+ML01,5.153
1010+HU02,5.464
1011+HY62,5.803
1012+ML02,5.215
1013+HU06,5.690
1014+HY63,5.680
1015+ML06,5.622
1016+HU16,5.626
1017+HY67,6.237
1018+ML08,5.651
1019+HU17,5.714
1020+HY86,5.544
1021+ML09,5.732
1022+HU19,5.379
1023+HY87,7.097
1024+ML13,5.681
1025+HU21,5.420
1026+HY88,5.701
1027+ML40,5.821
1028+HU22,5.453
1029+HY89,7.544
1030+ML41,5.386
1031+HU33,5.556
1032+HY91,5.681
1033+ML42,5.574
1034+HU36,5.547
1035+HY98,4.582
1036+ML44,5.516
1037+HU39,4.960
1038+HZ88,7.544
1039+ML46,5.544
1040+HU42,5.230
1041+M300,4.992
1042+MM03,5.565
1043+HU45,5.207
1044+M301,5.226
1045+MM06,5.466
1046+HU47,5.305
1047+M309,6.883
1048+MN90,5.420
1049+HU48,5.445
1050+M402,5.557
1051+MO01,5.433
1052+HU49,5.201
1053+M800,4.061
1054+MO02,5.325
1055+HU50,5.533
1056+M999,7.544
1057+MO03,4.871
1058+HU51,6.127
1059+MX01,5.857
1060+MP15,4.061
1061+HU52,5.335
1062+MA02,5.395
1063+MP30,5.930
1064+HU64,5.529
1065+MA04,5.516
1066+MR50,4.521
1067+HU70,5.105
1068+MC00,6.164
1069+MR51,4.710
1070+HU72,5.173
1071+MC01,7.544
1072+MR52,4.526
1073+MU01,4.966
1074+MU15,4.851
1075+MX02,5.003
1076+MU02,5.344
1077+MU26,5.311
1078+MX03,5.353
1079+MU05,5.094
1080+MU27,5.196
1081+MX04,5.194
1082+MU08,4.651
1083+MU38,4.963
1084+MX10,5.108
1085+MU09,5.226
1086+MU39,5.147
1087+MX33,5.155
1088+MU10,4.954
1089+MU40,5.043
1090+MX37,5.268
1091+MU14,5.150
1092+MX00,4.942
1093+MZ88,7.544
modified data/synthetic/README.md +3 −0
@@ -64,3 +64,6 @@ Les relations entre variables (élasticités, primes de secteur, décote d'ancie
64 | loyers_schl.rmr_trois_rivieres.taux_inoccupation#tous | 0.027 | 2.7 | proportion | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | % → proportion | 64 | loyers_schl.rmr_trois_rivieres.taux_inoccupation#tous | 0.027 | 2.7 | proportion | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | % → proportion |
65 | loyers_schl.rmr_saguenay.loyer_moyen#2_chambres | 985 | 985 | $/mois | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | loyer moyen 2 ch. RMR saguenay (SCHL oct. 2025) | 65 | loyers_schl.rmr_saguenay.loyer_moyen#2_chambres | 985 | 985 | $/mois | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | loyer moyen 2 ch. RMR saguenay (SCHL oct. 2025) |
66 | loyers_schl.rmr_saguenay.taux_inoccupation#tous | 0.013 | 1.3 | proportion | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | % → proportion | 66 | loyers_schl.rmr_saguenay.taux_inoccupation#tous | 0.013 | 1.3 | proportion | SRC-E-007 | octobre 2025 (Enquête sur les logements locatifs, publiée 2025-12-11) | calibre | % → proportion |
67+
68+## Chapitre 8 — « Val-Fictif » (zonage) — ajouté le 2026-10-31
69+`ch08_zonage_lots.csv` (400 lots d'une grille 20 × 20, cinq zones H1/H2/H3/C/P, îlot de 36 lots rezoné H1 → H2 le 2025-01-01) et `ch08_zonage_ventes.csv` (482 ventes résidentielles 2023-01 → 2026-08) sont générés par `analysis/python/cases/ch08_zonage.py` (graine 20261031). Loi : ln P = ln(prix de base de la zone) + 0,50 ln(S_hab/S_type) + 0,15 ln(S_terr/600) − 0,004·âge + ln(1,03)^(années depuis 2024-07-01) + prime de rezonage [0,03 + 0,0015·max(âge − 20 ; 0)] pour les ventes de l'îlot postérieures au rezonage + ε, σ = 0,07 ; prix arrondis à 500 $. Municipalité, zones, lots et prix sont entièrement fictifs (aucune calibration sur une municipalité réelle ; ordres de grandeur du cas 1 repris pour la zone H1).
added data/synthetic/ch08_zonage_lots.csv +401 −0
@@ -0,0 +1,401 @@
1+id_lot,x,y,zone_initiale,zone_apres_T0,rezone,superficie_terrain_m2,superficie_habitable_m2,age_2026,logements
2+L0000,0,0,C,C,False,743.0,,58,0
3+L0001,1,0,C,C,False,709.0,,36,0
4+L0002,2,0,C,C,False,793.0,,16,0
5+L0003,3,0,C,C,False,793.0,,54,0
6+L0004,4,0,C,C,False,757.0,,53,0
7+L0005,5,0,C,C,False,812.0,,43,0
8+L0006,6,0,C,C,False,796.0,,10,0
9+L0007,7,0,C,C,False,821.0,,51,0
10+L0008,8,0,C,C,False,708.0,,34,0
11+L0009,9,0,C,C,False,711.0,,33,0
12+L0010,10,0,C,C,False,740.0,,10,0
13+L0011,11,0,C,C,False,837.0,,60,0
14+L0012,12,0,C,C,False,714.0,,52,0
15+L0013,13,0,C,C,False,809.0,,5,0
16+L0014,14,0,C,C,False,778.0,,34,0
17+L0015,15,0,C,C,False,696.0,,50,0
18+L0016,16,0,C,C,False,768.0,,33,0
19+L0017,17,0,C,C,False,752.0,,57,0
20+L0018,18,0,C,C,False,775.0,,38,0
21+L0019,19,0,C,C,False,780.0,,50,0
22+L0100,0,1,C,C,False,716.0,,21,0
23+L0101,1,1,C,C,False,750.0,,38,0
24+L0102,2,1,C,C,False,785.0,,55,0
25+L0103,3,1,C,C,False,804.0,,30,0
26+L0104,4,1,C,C,False,724.0,,56,0
27+L0105,5,1,C,C,False,744.0,,5,0
28+L0106,6,1,C,C,False,742.0,,38,0
29+L0107,7,1,C,C,False,789.0,,24,0
30+L0108,8,1,C,C,False,749.0,,55,0
31+L0109,9,1,C,C,False,753.0,,43,0
32+L0110,10,1,C,C,False,698.0,,6,0
33+L0111,11,1,C,C,False,766.0,,58,0
34+L0112,12,1,C,C,False,817.0,,38,0
35+L0113,13,1,C,C,False,721.0,,18,0
36+L0114,14,1,C,C,False,832.0,,9,0
37+L0115,15,1,C,C,False,707.0,,26,0
38+L0116,16,1,C,C,False,711.0,,8,0
39+L0117,17,1,C,C,False,775.0,,19,0
40+L0118,18,1,C,C,False,701.0,,27,0
41+L0119,19,1,C,C,False,797.0,,21,0
42+L0200,0,2,C,C,False,733.0,,21,0
43+L0201,1,2,C,C,False,803.0,,16,0
44+L0202,2,2,C,C,False,724.0,,43,0
45+L0203,3,2,C,C,False,837.0,,38,0
46+L0204,4,2,C,C,False,792.0,,27,0
47+L0205,5,2,C,C,False,738.0,,58,0
48+L0206,6,2,C,C,False,767.0,,38,0
49+L0207,7,2,C,C,False,758.0,,60,0
50+L0208,8,2,C,C,False,748.0,,24,0
51+L0209,9,2,C,C,False,769.0,,12,0
52+L0210,10,2,C,C,False,805.0,,23,0
53+L0211,11,2,C,C,False,771.0,,59,0
54+L0212,12,2,C,C,False,719.0,,21,0
55+L0213,13,2,C,C,False,735.0,,14,0
56+L0214,14,2,C,C,False,802.0,,46,0
57+L0215,15,2,C,C,False,768.0,,56,0
58+L0216,16,2,C,C,False,776.0,,23,0
59+L0217,17,2,C,C,False,743.0,,57,0
60+L0218,18,2,C,C,False,782.0,,55,0
61+L0219,19,2,C,C,False,835.0,,26,0
62+L0300,0,3,P,P,False,590.0,,52,0
63+L0301,1,3,P,P,False,629.0,,45,0
64+L0302,2,3,P,P,False,669.0,,24,0
65+L0303,3,3,P,P,False,563.0,,20,0
66+L0304,4,3,H1,H1,False,614.0,167.0,36,1
67+L0305,5,3,H1,H1,False,571.0,160.0,73,1
68+L0306,6,3,H1,H1,False,600.0,147.0,27,1
69+L0307,7,3,H1,H1,False,562.0,145.0,39,1
70+L0308,8,3,H1,H1,False,645.0,192.0,65,1
71+L0309,9,3,H1,H1,False,611.0,147.0,63,1
72+L0310,10,3,H1,H1,False,629.0,169.0,57,1
73+L0311,11,3,H1,H1,False,647.0,150.0,41,1
74+L0312,12,3,H1,H1,False,574.0,117.0,39,1
75+L0313,13,3,H1,H1,False,638.0,150.0,56,1
76+L0314,14,3,H1,H1,False,663.0,119.0,28,1
77+L0315,15,3,H1,H1,False,650.0,101.0,73,1
78+L0316,16,3,H1,H1,False,629.0,115.0,52,1
79+L0317,17,3,H1,H1,False,588.0,117.0,71,1
80+L0318,18,3,H1,H1,False,567.0,121.0,49,1
81+L0319,19,3,H1,H1,False,636.0,102.0,15,1
82+L0400,0,4,P,P,False,655.0,,5,0
83+L0401,1,4,P,P,False,572.0,,29,0
84+L0402,2,4,P,P,False,653.0,,28,0
85+L0403,3,4,P,P,False,567.0,,20,0
86+L0404,4,4,H1,H1,False,552.0,118.0,43,1
87+L0405,5,4,H1,H1,False,559.0,164.0,16,1
88+L0406,6,4,H1,H1,False,591.0,152.0,25,1
89+L0407,7,4,H1,H1,False,597.0,103.0,14,1
90+L0408,8,4,H1,H1,False,601.0,207.0,24,1
91+L0409,9,4,H1,H1,False,576.0,151.0,50,1
92+L0410,10,4,H1,H1,False,554.0,177.0,47,1
93+L0411,11,4,H1,H1,False,559.0,181.0,44,1
94+L0412,12,4,H1,H1,False,617.0,130.0,13,1
95+L0413,13,4,H1,H1,False,628.0,135.0,14,1
96+L0414,14,4,H1,H1,False,670.0,124.0,36,1
97+L0415,15,4,H1,H1,False,562.0,134.0,43,1
98+L0416,16,4,H1,H1,False,582.0,113.0,30,1
99+L0417,17,4,H1,H1,False,599.0,115.0,33,1
100+L0418,18,4,H1,H1,False,626.0,146.0,52,1
101+L0419,19,4,H1,H1,False,649.0,114.0,35,1
102+L0500,0,5,P,P,False,617.0,,59,0
103+L0501,1,5,P,P,False,652.0,,44,0
104+L0502,2,5,P,P,False,637.0,,9,0
105+L0503,3,5,P,P,False,571.0,,54,0
106+L0504,4,5,H1,H1,False,569.0,133.0,70,1
107+L0505,5,5,H1,H1,False,661.0,113.0,50,1
108+L0506,6,5,H1,H1,False,578.0,142.0,63,1
109+L0507,7,5,H1,H1,False,619.0,115.0,26,1
110+L0508,8,5,H1,H1,False,587.0,149.0,36,1
111+L0509,9,5,H1,H1,False,607.0,143.0,17,1
112+L0510,10,5,H1,H1,False,598.0,119.0,55,1
113+L0511,11,5,H1,H1,False,614.0,133.0,10,1
114+L0512,12,5,H1,H1,False,567.0,155.0,63,1
115+L0513,13,5,H1,H1,False,609.0,130.0,65,1
116+L0514,14,5,H1,H1,False,646.0,163.0,67,1
117+L0515,15,5,H1,H1,False,569.0,148.0,25,1
118+L0516,16,5,H1,H1,False,635.0,125.0,73,1
119+L0517,17,5,H1,H1,False,574.0,128.0,58,1
120+L0518,18,5,H1,H1,False,564.0,112.0,60,1
121+L0519,19,5,H1,H1,False,627.0,131.0,20,1
122+L0600,0,6,P,P,False,625.0,,36,0
123+L0601,1,6,P,P,False,666.0,,52,0
124+L0602,2,6,P,P,False,638.0,,26,0
125+L0603,3,6,P,P,False,642.0,,40,0
126+L0604,4,6,H1,H1,False,573.0,138.0,69,1
127+L0605,5,6,H1,H1,False,562.0,121.0,66,1
128+L0606,6,6,H1,H1,False,596.0,148.0,22,1
129+L0607,7,6,H1,H1,False,556.0,125.0,59,1
130+L0608,8,6,H1,H1,False,571.0,112.0,16,1
131+L0609,9,6,H1,H1,False,629.0,105.0,25,1
132+L0610,10,6,H1,H1,False,601.0,108.0,24,1
133+L0611,11,6,H1,H1,False,558.0,140.0,30,1
134+L0612,12,6,H1,H1,False,623.0,137.0,52,1
135+L0613,13,6,H1,H1,False,616.0,98.0,28,1
136+L0614,14,6,H1,H1,False,647.0,170.0,59,1
137+L0615,15,6,H1,H1,False,619.0,154.0,28,1
138+L0616,16,6,H1,H1,False,658.0,156.0,62,1
139+L0617,17,6,H1,H1,False,670.0,207.0,67,1
140+L0618,18,6,H1,H1,False,664.0,180.0,69,1
141+L0619,19,6,H1,H1,False,607.0,174.0,63,1
142+L0700,0,7,P,P,False,641.0,,21,0
143+L0701,1,7,P,P,False,592.0,,14,0
144+L0702,2,7,P,P,False,584.0,,48,0
145+L0703,3,7,P,P,False,669.0,,19,0
146+L0704,4,7,H1,H1,False,662.0,139.0,26,1
147+L0705,5,7,H1,H1,False,608.0,109.0,54,1
148+L0706,6,7,H1,H1,False,634.0,144.0,10,1
149+L0707,7,7,H1,H1,False,588.0,162.0,41,1
150+L0708,8,7,H1,H1,False,562.0,148.0,55,1
151+L0709,9,7,H1,H1,False,556.0,157.0,68,1
152+L0710,10,7,H1,H1,False,557.0,154.0,56,1
153+L0711,11,7,H1,H1,False,582.0,144.0,44,1
154+L0712,12,7,H1,H1,False,560.0,133.0,13,1
155+L0713,13,7,H1,H1,False,638.0,163.0,48,1
156+L0714,14,7,H1,H1,False,575.0,128.0,13,1
157+L0715,15,7,H1,H1,False,565.0,169.0,31,1
158+L0716,16,7,H1,H1,False,648.0,169.0,11,1
159+L0717,17,7,H1,H1,False,629.0,144.0,55,1
160+L0718,18,7,H1,H1,False,638.0,137.0,34,1
161+L0719,19,7,H1,H1,False,653.0,114.0,15,1
162+L0800,0,8,H1,H1,False,576.0,130.0,16,1
163+L0801,1,8,H1,H1,False,640.0,115.0,47,1
164+L0802,2,8,H1,H1,False,563.0,138.0,26,1
165+L0803,3,8,H1,H1,False,622.0,128.0,44,1
166+L0804,4,8,H1,H1,False,641.0,134.0,46,1
167+L0805,5,8,H1,H1,False,573.0,149.0,10,1
168+L0806,6,8,H1,H1,False,595.0,155.0,71,1
169+L0807,7,8,H1,H2,True,567.0,121.0,40,1
170+L0808,8,8,H1,H2,True,652.0,136.0,11,1
171+L0809,9,8,H1,H2,True,671.0,146.0,49,1
172+L0810,10,8,H2,H2,False,668.0,247.0,55,2
173+L0811,11,8,H2,H2,False,743.0,220.0,26,4
174+L0812,12,8,H2,H2,False,756.0,243.0,8,3
175+L0813,13,8,H2,H2,False,799.0,199.0,59,4
176+L0814,14,8,H2,H2,False,795.0,217.0,51,4
177+L0815,15,8,H3,H3,False,1051.0,412.0,46,8
178+L0816,16,8,H3,H3,False,1075.0,457.0,58,4
179+L0817,17,8,H3,H3,False,1116.0,667.0,57,4
180+L0818,18,8,H3,H3,False,1062.0,366.0,60,7
181+L0819,19,8,H3,H3,False,1059.0,484.0,12,8
182+L0900,0,9,H1,H1,False,557.0,122.0,19,1
183+L0901,1,9,H1,H1,False,643.0,161.0,14,1
184+L0902,2,9,H1,H1,False,597.0,119.0,54,1
185+L0903,3,9,H1,H1,False,641.0,124.0,27,1
186+L0904,4,9,H1,H1,False,629.0,137.0,52,1
187+L0905,5,9,H1,H1,False,615.0,117.0,67,1
188+L0906,6,9,H1,H1,False,603.0,148.0,10,1
189+L0907,7,9,H1,H2,True,657.0,154.0,34,1
190+L0908,8,9,H1,H2,True,552.0,184.0,50,1
191+L0909,9,9,H1,H2,True,565.0,160.0,35,1
192+L0910,10,9,H2,H2,False,673.0,255.0,41,4
193+L0911,11,9,H2,H2,False,737.0,223.0,24,2
194+L0912,12,9,H2,H2,False,734.0,249.0,49,3
195+L0913,13,9,H2,H2,False,722.0,212.0,11,3
196+L0914,14,9,H2,H2,False,795.0,237.0,11,2
197+L0915,15,9,H3,H3,False,1167.0,556.0,20,4
198+L0916,16,9,H3,H3,False,1136.0,548.0,51,7
199+L0917,17,9,H3,H3,False,1095.0,504.0,26,4
200+L0918,18,9,H3,H3,False,1187.0,578.0,47,5
201+L0919,19,9,H3,H3,False,1037.0,483.0,13,4
202+L1000,0,10,H1,H1,False,564.0,142.0,49,1
203+L1001,1,10,H1,H1,False,613.0,128.0,24,1
204+L1002,2,10,H1,H1,False,577.0,152.0,12,1
205+L1003,3,10,H1,H1,False,567.0,128.0,24,1
206+L1004,4,10,H1,H1,False,599.0,103.0,72,1
207+L1005,5,10,H1,H1,False,628.0,139.0,19,1
208+L1006,6,10,H1,H1,False,667.0,127.0,31,1
209+L1007,7,10,H1,H2,True,575.0,140.0,23,1
210+L1008,8,10,H1,H2,True,582.0,166.0,30,1
211+L1009,9,10,H1,H2,True,595.0,149.0,40,1
212+L1010,10,10,H2,H2,False,737.0,213.0,69,3
213+L1011,11,10,H2,H2,False,708.0,247.0,69,2
214+L1012,12,10,H2,H2,False,745.0,182.0,6,3
215+L1013,13,10,H2,H2,False,767.0,176.0,50,3
216+L1014,14,10,H2,H2,False,739.0,240.0,56,4
217+L1015,15,10,H3,H3,False,1103.0,511.0,30,4
218+L1016,16,10,H3,H3,False,1077.0,574.0,58,7
219+L1017,17,10,H3,H3,False,1091.0,546.0,11,8
220+L1018,18,10,H3,H3,False,1161.0,490.0,55,4
221+L1019,19,10,H3,H3,False,1054.0,570.0,42,4
222+L1100,0,11,H1,H1,False,651.0,106.0,70,1
223+L1101,1,11,H1,H1,False,628.0,104.0,59,1
224+L1102,2,11,H1,H1,False,581.0,147.0,36,1
225+L1103,3,11,H1,H1,False,566.0,156.0,68,1
226+L1104,4,11,H1,H1,False,589.0,150.0,40,1
227+L1105,5,11,H1,H1,False,603.0,147.0,22,1
228+L1106,6,11,H1,H1,False,606.0,106.0,62,1
229+L1107,7,11,H1,H2,True,632.0,123.0,31,1
230+L1108,8,11,H1,H2,True,669.0,122.0,25,1
231+L1109,9,11,H1,H2,True,622.0,148.0,55,1
232+L1110,10,11,H2,H2,False,771.0,202.0,67,4
233+L1111,11,11,H2,H2,False,667.0,200.0,35,4
234+L1112,12,11,H2,H2,False,721.0,189.0,56,3
235+L1113,13,11,H2,H2,False,719.0,225.0,24,3
236+L1114,14,11,H2,H2,False,729.0,181.0,6,2
237+L1115,15,11,H3,H3,False,1202.0,484.0,21,8
238+L1116,16,11,H3,H3,False,1041.0,662.0,43,6
239+L1117,17,11,H3,H3,False,1087.0,491.0,55,4
240+L1118,18,11,H3,H3,False,1131.0,637.0,29,6
241+L1119,19,11,H3,H3,False,1173.0,393.0,16,7
242+L1200,0,12,H1,H1,False,593.0,138.0,25,1
243+L1201,1,12,H1,H1,False,620.0,145.0,49,1
244+L1202,2,12,H1,H1,False,620.0,106.0,40,1
245+L1203,3,12,H1,H1,False,597.0,162.0,64,1
246+L1204,4,12,H1,H1,False,592.0,186.0,61,1
247+L1205,5,12,H1,H1,False,650.0,134.0,27,1
248+L1206,6,12,H1,H1,False,553.0,124.0,73,1
249+L1207,7,12,H1,H2,True,554.0,102.0,42,1
250+L1208,8,12,H1,H2,True,588.0,120.0,59,1
251+L1209,9,12,H1,H2,True,625.0,165.0,31,1
252+L1210,10,12,H2,H2,False,705.0,249.0,52,2
253+L1211,11,12,H2,H2,False,768.0,262.0,21,4
254+L1212,12,12,H2,H2,False,699.0,206.0,51,2
255+L1213,13,12,H2,H2,False,784.0,232.0,63,2
256+L1214,14,12,H2,H2,False,778.0,157.0,27,3
257+L1215,15,12,H3,H3,False,1190.0,553.0,9,7
258+L1216,16,12,H3,H3,False,1147.0,564.0,40,8
259+L1217,17,12,H3,H3,False,1126.0,407.0,59,5
260+L1218,18,12,H3,H3,False,1095.0,460.0,37,7
261+L1219,19,12,H3,H3,False,1065.0,690.0,60,8
262+L1300,0,13,H1,H1,False,652.0,124.0,71,1
263+L1301,1,13,H1,H1,False,579.0,108.0,50,1
264+L1302,2,13,H1,H1,False,556.0,88.0,19,1
265+L1303,3,13,H1,H1,False,594.0,144.0,15,1
266+L1304,4,13,H1,H1,False,634.0,138.0,38,1
267+L1305,5,13,H1,H1,False,659.0,108.0,39,1
268+L1306,6,13,H1,H1,False,560.0,152.0,32,1
269+L1307,7,13,H1,H2,True,636.0,161.0,39,1
270+L1308,8,13,H1,H2,True,555.0,125.0,18,1
271+L1309,9,13,H1,H2,True,593.0,161.0,29,1
272+L1310,10,13,H2,H2,False,681.0,204.0,70,3
273+L1311,11,13,H2,H2,False,685.0,223.0,16,3
274+L1312,12,13,H2,H2,False,686.0,267.0,38,3
275+L1313,13,13,H2,H2,False,792.0,254.0,28,3
276+L1314,14,13,H2,H2,False,700.0,185.0,55,3
277+L1315,15,13,H3,H3,False,1177.0,446.0,52,5
278+L1316,16,13,H3,H3,False,1182.0,509.0,24,6
279+L1317,17,13,H3,H3,False,1083.0,629.0,43,7
280+L1318,18,13,H3,H3,False,1035.0,565.0,10,8
281+L1319,19,13,H3,H3,False,1031.0,668.0,53,8
282+L1400,0,14,H1,H1,False,631.0,94.0,18,1
283+L1401,1,14,H1,H1,False,603.0,85.0,25,1
284+L1402,2,14,H1,H1,False,627.0,135.0,55,1
285+L1403,3,14,H1,H1,False,644.0,174.0,55,1
286+L1404,4,14,H1,H1,False,661.0,95.0,74,1
287+L1405,5,14,H1,H1,False,640.0,116.0,55,1
288+L1406,6,14,H1,H1,False,662.0,108.0,47,1
289+L1407,7,14,H1,H2,True,613.0,137.0,51,1
290+L1408,8,14,H1,H2,True,667.0,159.0,25,1
291+L1409,9,14,H1,H2,True,671.0,118.0,55,1
292+L1410,10,14,H2,H2,False,687.0,230.0,6,2
293+L1411,11,14,H2,H2,False,713.0,205.0,35,3
294+L1412,12,14,H2,H2,False,768.0,246.0,64,4
295+L1413,13,14,H2,H2,False,797.0,233.0,49,2
296+L1414,14,14,H2,H2,False,708.0,212.0,54,2
297+L1415,15,14,H3,H3,False,1201.0,448.0,12,5
298+L1416,16,14,H3,H3,False,1187.0,585.0,53,8
299+L1417,17,14,H3,H3,False,1051.0,569.0,11,7
300+L1418,18,14,H3,H3,False,1122.0,507.0,11,4
301+L1419,19,14,H3,H3,False,1179.0,583.0,19,8
302+L1500,0,15,H1,H1,False,573.0,106.0,44,1
303+L1501,1,15,H1,H1,False,637.0,138.0,27,1
304+L1502,2,15,H1,H1,False,638.0,147.0,36,1
305+L1503,3,15,H1,H1,False,614.0,161.0,13,1
306+L1504,4,15,H1,H1,False,654.0,136.0,48,1
307+L1505,5,15,H1,H1,False,594.0,97.0,45,1
308+L1506,6,15,H1,H1,False,600.0,154.0,52,1
309+L1507,7,15,H1,H2,True,617.0,169.0,70,1
310+L1508,8,15,H1,H2,True,659.0,181.0,10,1
311+L1509,9,15,H1,H2,True,602.0,112.0,53,1
312+L1510,10,15,H2,H2,False,729.0,265.0,16,2
313+L1511,11,15,H2,H2,False,749.0,200.0,43,3
314+L1512,12,15,H2,H2,False,777.0,284.0,29,4
315+L1513,13,15,H2,H2,False,764.0,221.0,41,2
316+L1514,14,15,H2,H2,False,686.0,307.0,54,2
317+L1515,15,15,H3,H3,False,1075.0,625.0,5,7
318+L1516,16,15,H3,H3,False,1161.0,503.0,40,7
319+L1517,17,15,H3,H3,False,1055.0,577.0,17,6
320+L1518,18,15,H3,H3,False,1100.0,525.0,60,5
321+L1519,19,15,H3,H3,False,1034.0,512.0,51,4
322+L1600,0,16,H1,H1,False,667.0,159.0,53,1
323+L1601,1,16,H1,H1,False,563.0,142.0,30,1
324+L1602,2,16,H1,H1,False,612.0,124.0,23,1
325+L1603,3,16,H1,H1,False,651.0,158.0,41,1
326+L1604,4,16,H1,H1,False,641.0,152.0,15,1
327+L1605,5,16,H1,H1,False,616.0,139.0,49,1
328+L1606,6,16,H1,H1,False,569.0,131.0,75,1
329+L1607,7,16,H1,H2,True,562.0,129.0,45,1
330+L1608,8,16,H1,H2,True,613.0,170.0,31,1
331+L1609,9,16,H1,H2,True,624.0,120.0,41,1
332+L1610,10,16,H2,H2,False,761.0,207.0,49,2
333+L1611,11,16,H2,H2,False,772.0,233.0,12,4
334+L1612,12,16,H2,H2,False,677.0,209.0,17,3
335+L1613,13,16,H2,H2,False,682.0,181.0,47,4
336+L1614,14,16,H2,H2,False,664.0,168.0,34,3
337+L1615,15,16,H3,H3,False,1155.0,363.0,8,5
338+L1616,16,16,H3,H3,False,1052.0,526.0,16,8
339+L1617,17,16,H3,H3,False,1024.0,512.0,44,5
340+L1618,18,16,H3,H3,False,1055.0,507.0,33,7
341+L1619,19,16,H3,H3,False,1184.0,552.0,5,7
342+L1700,0,17,H1,H1,False,606.0,152.0,11,1
343+L1701,1,17,H1,H1,False,572.0,169.0,57,1
344+L1702,2,17,H1,H1,False,653.0,185.0,57,1
345+L1703,3,17,H1,H1,False,589.0,154.0,29,1
346+L1704,4,17,H1,H1,False,647.0,123.0,38,1
347+L1705,5,17,H1,H1,False,601.0,132.0,18,1
348+L1706,6,17,H1,H1,False,572.0,168.0,15,1
349+L1707,7,17,H1,H2,True,559.0,147.0,53,1
350+L1708,8,17,H1,H2,True,587.0,132.0,51,1
351+L1709,9,17,H1,H2,True,664.0,135.0,36,1
352+L1710,10,17,H2,H2,False,798.0,236.0,28,2
353+L1711,11,17,H2,H2,False,772.0,238.0,56,2
354+L1712,12,17,H2,H2,False,744.0,221.0,10,4
355+L1713,13,17,H2,H2,False,712.0,261.0,50,3
356+L1714,14,17,H2,H2,False,754.0,245.0,50,3
357+L1715,15,17,H3,H3,False,1130.0,473.0,50,8
358+L1716,16,17,H3,H3,False,1111.0,567.0,36,4
359+L1717,17,17,H3,H3,False,1120.0,575.0,26,7
360+L1718,18,17,H3,H3,False,1094.0,614.0,30,8
361+L1719,19,17,H3,H3,False,1017.0,577.0,41,5
362+L1800,0,18,H1,H1,False,554.0,156.0,31,1
363+L1801,1,18,H1,H1,False,620.0,136.0,37,1
364+L1802,2,18,H1,H1,False,661.0,104.0,33,1
365+L1803,3,18,H1,H1,False,641.0,133.0,56,1
366+L1804,4,18,H1,H1,False,635.0,127.0,15,1
367+L1805,5,18,H1,H1,False,620.0,150.0,71,1
368+L1806,6,18,H1,H1,False,601.0,105.0,53,1
369+L1807,7,18,H1,H2,True,597.0,127.0,56,1
370+L1808,8,18,H1,H2,True,664.0,175.0,26,1
371+L1809,9,18,H1,H2,True,660.0,159.0,39,1
372+L1810,10,18,H2,H2,False,710.0,207.0,25,4
373+L1811,11,18,H2,H2,False,719.0,246.0,35,4
374+L1812,12,18,H2,H2,False,770.0,226.0,34,4
375+L1813,13,18,H2,H2,False,782.0,253.0,27,2
376+L1814,14,18,H2,H2,False,781.0,198.0,25,4
377+L1815,15,18,H3,H3,False,1128.0,416.0,39,7
378+L1816,16,18,H3,H3,False,1203.0,579.0,42,6
379+L1817,17,18,H3,H3,False,1128.0,501.0,56,8
380+L1818,18,18,H3,H3,False,1003.0,390.0,50,7
381+L1819,19,18,H3,H3,False,1190.0,542.0,14,7
382+L1900,0,19,H1,H1,False,599.0,148.0,30,1
383+L1901,1,19,H1,H1,False,672.0,140.0,57,1
384+L1902,2,19,H1,H1,False,648.0,150.0,57,1
385+L1903,3,19,H1,H1,False,670.0,129.0,67,1
386+L1904,4,19,H1,H1,False,663.0,152.0,54,1
387+L1905,5,19,H1,H1,False,627.0,156.0,22,1
388+L1906,6,19,H1,H1,False,623.0,107.0,55,1
389+L1907,7,19,H1,H2,True,657.0,143.0,37,1
390+L1908,8,19,H1,H2,True,641.0,127.0,56,1
391+L1909,9,19,H1,H2,True,624.0,90.0,56,1
392+L1910,10,19,H2,H2,False,686.0,182.0,49,4
393+L1911,11,19,H2,H2,False,745.0,250.0,9,4
394+L1912,12,19,H2,H2,False,695.0,213.0,36,3
395+L1913,13,19,H2,H2,False,682.0,155.0,68,3
396+L1914,14,19,H2,H2,False,687.0,184.0,21,3
397+L1915,15,19,H3,H3,False,1207.0,564.0,16,8
398+L1916,16,19,H3,H3,False,1140.0,483.0,5,6
399+L1917,17,19,H3,H3,False,1130.0,604.0,39,8
400+L1918,18,19,H3,H3,False,1056.0,555.0,32,5
401+L1919,19,19,H3,H3,False,1089.0,403.0,12,4
added data/synthetic/ch08_zonage_ventes.csv +483 −0
@@ -0,0 +1,483 @@
1+id_vente,id_lot,zone_initiale,rezone,date_vente,post_T0,traite_post,superficie_habitable_m2,superficie_terrain_m2,age_vente,prix_vente
2+V0001,L0304,H1,False,2023-03-31,False,False,167.0,614.0,33,386000.0
3+V0002,L0305,H1,False,2025-03-22,True,False,160.0,571.0,72,362000.0
4+V0003,L0305,H1,False,2025-03-25,True,False,160.0,571.0,72,361000.0
5+V0004,L0306,H1,False,2024-09-26,False,False,147.0,600.0,25,387500.0
6+V0005,L0307,H1,False,2026-02-26,True,False,145.0,562.0,39,346500.0
7+V0006,L0308,H1,False,2025-12-31,True,False,192.0,645.0,64,406500.0
8+V0007,L0309,H1,False,2025-07-19,True,False,147.0,611.0,62,355000.0
9+V0008,L0309,H1,False,2026-03-17,True,False,147.0,611.0,63,370000.0
10+V0009,L0310,H1,False,2025-09-15,True,False,169.0,629.0,56,408000.0
11+V0010,L0310,H1,False,2026-07-02,True,False,169.0,629.0,57,409500.0
12+V0011,L0311,H1,False,2024-05-28,False,False,150.0,647.0,39,385000.0
13+V0012,L0311,H1,False,2024-07-04,False,False,150.0,647.0,39,374500.0
14+V0013,L0312,H1,False,2025-03-11,True,False,117.0,574.0,38,311500.0
15+V0014,L0312,H1,False,2026-05-24,True,False,117.0,574.0,39,361500.0
16+V0015,L0313,H1,False,2025-09-25,True,False,150.0,638.0,55,383500.0
17+V0016,L0314,H1,False,2023-01-12,False,False,119.0,663.0,25,363500.0
18+V0017,L0314,H1,False,2024-03-09,False,False,119.0,663.0,26,373500.0
19+V0018,L0315,H1,False,2024-01-10,False,False,101.0,650.0,71,242000.0
20+V0019,L0315,H1,False,2026-04-22,True,False,101.0,650.0,73,322000.0
21+V0020,L0316,H1,False,2025-05-21,True,False,115.0,629.0,51,354000.0
22+V0021,L0317,H1,False,2025-10-18,True,False,117.0,588.0,70,310000.0
23+V0022,L0318,H1,False,2026-08-23,True,False,121.0,567.0,49,370000.0
24+V0023,L0319,H1,False,2023-01-27,False,False,102.0,636.0,12,356000.0
25+V0024,L0404,H1,False,2025-02-23,True,False,118.0,552.0,42,308500.0
26+V0025,L0405,H1,False,2023-11-29,False,False,164.0,559.0,13,491000.0
27+V0026,L0405,H1,False,2023-12-21,False,False,164.0,559.0,13,394000.0
28+V0027,L0406,H1,False,2023-09-08,False,False,152.0,591.0,22,405500.0
29+V0028,L0406,H1,False,2024-11-19,False,False,152.0,591.0,23,398500.0
30+V0029,L0407,H1,False,2024-05-07,False,False,103.0,597.0,12,344500.0
31+V0030,L0408,H1,False,2025-06-03,True,False,207.0,601.0,23,479500.0
32+V0031,L0408,H1,False,2025-11-23,True,False,207.0,601.0,23,472000.0
33+V0032,L0409,H1,False,2024-04-01,False,False,151.0,576.0,48,382000.0
34+V0033,L0409,H1,False,2025-01-29,True,False,151.0,576.0,49,369000.0
35+V0034,L0410,H1,False,2025-08-22,True,False,177.0,554.0,46,389500.0
36+V0035,L0411,H1,False,2023-08-14,False,False,181.0,559.0,41,416500.0
37+V0036,L0411,H1,False,2025-07-29,True,False,181.0,559.0,43,429500.0
38+V0037,L0412,H1,False,2025-03-04,True,False,130.0,617.0,12,393000.0
39+V0038,L0412,H1,False,2026-08-26,True,False,130.0,617.0,13,417000.0
40+V0039,L0413,H1,False,2025-01-11,True,False,135.0,628.0,13,403000.0
41+V0040,L0413,H1,False,2025-07-18,True,False,135.0,628.0,13,401500.0
42+V0041,L0414,H1,False,2024-12-02,False,False,124.0,670.0,34,367000.0
43+V0042,L0415,H1,False,2024-08-13,False,False,134.0,562.0,41,389500.0
44+V0043,L0416,H1,False,2023-11-15,False,False,113.0,582.0,27,356500.0
45+V0044,L0416,H1,False,2025-08-22,True,False,113.0,582.0,29,375000.0
46+V0045,L0417,H1,False,2023-09-02,False,False,115.0,599.0,30,348500.0
47+V0046,L0417,H1,False,2025-07-20,True,False,115.0,599.0,32,349000.0
48+V0047,L0418,H1,False,2024-08-21,False,False,146.0,626.0,50,389000.0
49+V0048,L0418,H1,False,2025-10-13,True,False,146.0,626.0,51,371000.0
50+V0049,L0419,H1,False,2023-06-07,False,False,114.0,649.0,32,355000.0
51+V0050,L0419,H1,False,2025-05-07,True,False,114.0,649.0,34,372500.0
52+V0051,L0504,H1,False,2024-12-18,False,False,133.0,569.0,68,368500.0
53+V0052,L0504,H1,False,2025-05-06,True,False,133.0,569.0,69,332000.0
54+V0053,L0505,H1,False,2024-04-12,False,False,113.0,661.0,48,307500.0
55+V0054,L0506,H1,False,2023-09-16,False,False,142.0,578.0,60,321000.0
56+V0055,L0507,H1,False,2025-01-19,True,False,115.0,619.0,25,353000.0
57+V0056,L0508,H1,False,2023-03-06,False,False,149.0,587.0,33,369500.0
58+V0057,L0509,H1,False,2023-01-29,False,False,143.0,607.0,14,387500.0
59+V0058,L0509,H1,False,2026-03-30,True,False,143.0,607.0,17,412500.0
60+V0059,L0510,H1,False,2023-12-07,False,False,119.0,598.0,52,334500.0
61+V0060,L0511,H1,False,2025-04-29,True,False,133.0,614.0,9,435000.0
62+V0061,L0511,H1,False,2026-04-16,True,False,133.0,614.0,10,454500.0
63+V0062,L0512,H1,False,2023-08-16,False,False,155.0,567.0,60,312000.0
64+V0063,L0512,H1,False,2023-08-29,False,False,155.0,567.0,60,347000.0
65+V0064,L0513,H1,False,2023-03-11,False,False,130.0,609.0,62,304000.0
66+V0065,L0513,H1,False,2024-10-25,False,False,130.0,609.0,63,326000.0
67+V0066,L0514,H1,False,2023-12-18,False,False,163.0,646.0,64,324000.0
68+V0067,L0514,H1,False,2025-01-16,True,False,163.0,646.0,66,390500.0
69+V0068,L0515,H1,False,2023-09-07,False,False,148.0,569.0,22,394500.0
70+V0069,L0516,H1,False,2025-01-28,True,False,125.0,635.0,72,301000.0
71+V0070,L0517,H1,False,2024-12-03,False,False,128.0,574.0,56,330500.0
72+V0071,L0518,H1,False,2026-01-04,True,False,112.0,564.0,60,287500.0
73+V0072,L0518,H1,False,2026-02-18,True,False,112.0,564.0,60,373500.0
74+V0073,L0519,H1,False,2025-02-07,True,False,131.0,627.0,19,422500.0
75+V0074,L0604,H1,False,2023-11-03,False,False,138.0,573.0,66,318000.0
76+V0075,L0604,H1,False,2025-08-13,True,False,138.0,573.0,68,396000.0
77+V0076,L0605,H1,False,2024-10-07,False,False,121.0,562.0,64,315000.0
78+V0077,L0606,H1,False,2023-04-21,False,False,148.0,596.0,19,372500.0
79+V0078,L0607,H1,False,2025-11-13,True,False,125.0,556.0,58,315000.0
80+V0079,L0608,H1,False,2024-09-03,False,False,112.0,571.0,14,361000.0
81+V0080,L0608,H1,False,2024-09-18,False,False,112.0,571.0,14,307500.0
82+V0081,L0609,H1,False,2025-04-04,True,False,105.0,629.0,24,340500.0
83+V0082,L0610,H1,False,2023-08-17,False,False,108.0,601.0,21,336500.0
84+V0083,L0610,H1,False,2024-12-16,False,False,108.0,601.0,22,348000.0
85+V0084,L0611,H1,False,2024-08-22,False,False,140.0,558.0,28,380500.0
86+V0085,L0611,H1,False,2026-06-10,True,False,140.0,558.0,30,426500.0
87+V0086,L0612,H1,False,2025-05-23,True,False,137.0,623.0,51,411000.0
88+V0087,L0613,H1,False,2023-05-27,False,False,98.0,616.0,25,335500.0
89+V0088,L0613,H1,False,2023-11-23,False,False,98.0,616.0,25,301000.0
90+V0089,L0614,H1,False,2023-09-28,False,False,170.0,647.0,56,378500.0
91+V0090,L0615,H1,False,2023-06-03,False,False,154.0,619.0,25,419000.0
92+V0091,L0615,H1,False,2023-11-25,False,False,154.0,619.0,25,410500.0
93+V0092,L0616,H1,False,2024-07-28,False,False,156.0,658.0,60,397500.0
94+V0093,L0617,H1,False,2024-12-21,False,False,207.0,670.0,65,430000.0
95+V0094,L0618,H1,False,2023-11-14,False,False,180.0,664.0,66,360500.0
96+V0095,L0619,H1,False,2023-11-10,False,False,174.0,607.0,60,363000.0
97+V0096,L0619,H1,False,2025-11-06,True,False,174.0,607.0,62,336500.0
98+V0097,L0704,H1,False,2023-09-25,False,False,139.0,662.0,23,363500.0
99+V0098,L0704,H1,False,2024-03-31,False,False,139.0,662.0,24,408500.0
100+V0099,L0705,H1,False,2025-11-12,True,False,109.0,608.0,53,309500.0
101+V0100,L0706,H1,False,2024-01-09,False,False,144.0,634.0,8,433500.0
102+V0101,L0707,H1,False,2023-03-13,False,False,162.0,588.0,38,414000.0
103+V0102,L0707,H1,False,2024-05-23,False,False,162.0,588.0,39,432500.0
104+V0103,L0708,H1,False,2023-07-19,False,False,148.0,562.0,52,366500.0
105+V0104,L0708,H1,False,2024-12-03,False,False,148.0,562.0,53,358500.0
106+V0105,L0709,H1,False,2023-12-01,False,False,157.0,556.0,65,351500.0
107+V0106,L0710,H1,False,2025-09-04,True,False,154.0,557.0,55,327000.0
108+V0107,L0710,H1,False,2025-11-14,True,False,154.0,557.0,55,394500.0
109+V0108,L0711,H1,False,2024-02-22,False,False,144.0,582.0,42,373500.0
110+V0109,L0711,H1,False,2024-08-09,False,False,144.0,582.0,42,346000.0
111+V0110,L0712,H1,False,2025-03-21,True,False,133.0,560.0,12,446500.0
112+V0111,L0713,H1,False,2023-07-13,False,False,163.0,638.0,45,374000.0
113+V0112,L0714,H1,False,2023-03-17,False,False,128.0,575.0,10,422500.0
114+V0113,L0715,H1,False,2024-07-22,False,False,169.0,565.0,29,400500.0
115+V0114,L0716,H1,False,2026-02-03,True,False,169.0,648.0,11,500000.0
116+V0115,L0717,H1,False,2026-01-03,True,False,144.0,629.0,55,372000.0
117+V0116,L0718,H1,False,2024-01-18,False,False,137.0,638.0,32,385500.0
118+V0117,L0718,H1,False,2026-02-24,True,False,137.0,638.0,34,455000.0
119+V0118,L0719,H1,False,2025-11-08,True,False,114.0,653.0,14,427500.0
120+V0119,L0800,H1,False,2024-10-12,False,False,130.0,576.0,14,368000.0
121+V0120,L0800,H1,False,2025-02-18,True,False,130.0,576.0,15,393000.0
122+V0121,L0801,H1,False,2025-01-06,True,False,115.0,640.0,46,357500.0
123+V0122,L0801,H1,False,2025-09-20,True,False,115.0,640.0,46,371000.0
124+V0123,L0802,H1,False,2024-05-18,False,False,138.0,563.0,24,376500.0
125+V0124,L0802,H1,False,2026-02-03,True,False,138.0,563.0,26,435000.0
126+V0125,L0803,H1,False,2023-12-06,False,False,128.0,622.0,41,376000.0
127+V0126,L0803,H1,False,2024-08-23,False,False,128.0,622.0,42,383000.0
128+V0127,L0804,H1,False,2024-09-20,False,False,134.0,641.0,44,364500.0
129+V0128,L0804,H1,False,2024-10-25,False,False,134.0,641.0,44,390500.0
130+V0129,L0805,H1,False,2023-01-04,False,False,149.0,573.0,7,418000.0
131+V0130,L0805,H1,False,2023-06-12,False,False,149.0,573.0,7,406000.0
132+V0131,L0806,H1,False,2026-04-21,True,False,155.0,595.0,71,371000.0
133+V0132,L0807,H1,True,2026-04-25,True,True,121.0,567.0,40,380500.0
134+V0133,L0808,H1,True,2025-09-07,True,True,136.0,652.0,10,429000.0
135+V0134,L0809,H1,True,2024-02-16,False,False,146.0,671.0,47,405000.0
136+V0135,L0809,H1,True,2024-10-01,False,False,146.0,671.0,47,431000.0
137+V0136,L0810,H2,False,2025-09-28,True,False,247.0,668.0,54,524500.0
138+V0137,L0811,H2,False,2026-06-22,True,False,220.0,743.0,26,688000.0
139+V0138,L0812,H2,False,2023-06-26,False,False,243.0,756.0,5,595500.0
140+V0139,L0813,H2,False,2026-01-22,True,False,199.0,799.0,59,569500.0
141+V0140,L0813,H2,False,2026-05-20,True,False,199.0,799.0,59,508000.0
142+V0141,L0814,H2,False,2023-06-21,False,False,217.0,795.0,48,546500.0
143+V0142,L0814,H2,False,2024-06-20,False,False,217.0,795.0,49,586000.0
144+V0143,L0815,H3,False,2024-09-30,False,False,412.0,1051.0,44,915500.0
145+V0144,L0816,H3,False,2023-08-18,False,False,457.0,1075.0,55,855500.0
146+V0145,L0817,H3,False,2024-06-21,False,False,667.0,1116.0,55,1125500.0
147+V0146,L0818,H3,False,2026-07-22,True,False,366.0,1062.0,60,886000.0
148+V0147,L0819,H3,False,2026-03-24,True,False,484.0,1059.0,12,1252000.0
149+V0148,L0900,H1,False,2023-07-24,False,False,122.0,557.0,16,336000.0
150+V0149,L0900,H1,False,2025-01-05,True,False,122.0,557.0,18,349000.0
151+V0150,L0901,H1,False,2025-05-10,True,False,161.0,643.0,13,418500.0
152+V0151,L0902,H1,False,2025-09-15,True,False,119.0,597.0,53,330000.0
153+V0152,L0903,H1,False,2026-01-21,True,False,124.0,641.0,27,418500.0
154+V0153,L0904,H1,False,2024-11-28,False,False,137.0,629.0,50,386000.0
155+V0154,L0905,H1,False,2023-04-20,False,False,117.0,615.0,64,316000.0
156+V0155,L0906,H1,False,2023-12-15,False,False,148.0,603.0,7,432500.0
157+V0156,L0906,H1,False,2026-04-08,True,False,148.0,603.0,10,426000.0
158+V0157,L0907,H1,True,2023-01-29,False,False,154.0,657.0,31,384500.0
159+V0158,L0908,H1,True,2025-04-09,True,True,184.0,552.0,49,396500.0
160+V0159,L0909,H1,True,2024-12-06,False,False,160.0,565.0,33,386500.0
161+V0160,L0910,H2,False,2024-07-14,False,False,255.0,673.0,39,616500.0
162+V0161,L0911,H2,False,2025-12-25,True,False,223.0,737.0,23,618000.0
163+V0162,L0912,H2,False,2024-01-12,False,False,249.0,734.0,47,639500.0
164+V0163,L0912,H2,False,2024-08-13,False,False,249.0,734.0,47,570500.0
165+V0164,L0913,H2,False,2025-01-23,True,False,212.0,722.0,10,573500.0
166+V0165,L0914,H2,False,2023-11-18,False,False,237.0,795.0,8,635000.0
167+V0166,L0914,H2,False,2024-08-04,False,False,237.0,795.0,9,598000.0
168+V0167,L0915,H3,False,2024-05-03,False,False,556.0,1167.0,18,1251000.0
169+V0168,L0915,H3,False,2025-12-02,True,False,556.0,1167.0,19,1234500.0
170+V0169,L0916,H3,False,2024-03-16,False,False,548.0,1136.0,49,1198000.0
171+V0170,L0916,H3,False,2025-05-30,True,False,548.0,1136.0,50,1000500.0
172+V0171,L0917,H3,False,2023-07-31,False,False,504.0,1095.0,23,1110500.0
173+V0172,L0918,H3,False,2025-05-03,True,False,578.0,1187.0,46,1036000.0
174+V0173,L0919,H3,False,2024-10-25,False,False,483.0,1037.0,11,998000.0
175+V0174,L0919,H3,False,2026-08-21,True,False,483.0,1037.0,13,1211500.0
176+V0175,L1000,H1,False,2024-02-08,False,False,142.0,564.0,47,372500.0
177+V0176,L1000,H1,False,2026-08-27,True,False,142.0,564.0,49,373500.0
178+V0177,L1001,H1,False,2024-01-26,False,False,128.0,613.0,22,348500.0
179+V0178,L1002,H1,False,2025-02-20,True,False,152.0,577.0,11,443000.0
180+V0179,L1003,H1,False,2025-09-13,True,False,128.0,567.0,23,382000.0
181+V0180,L1003,H1,False,2026-07-15,True,False,128.0,567.0,24,407000.0
182+V0181,L1004,H1,False,2024-02-23,False,False,103.0,599.0,70,278500.0
183+V0182,L1004,H1,False,2025-12-08,True,False,103.0,599.0,71,281500.0
184+V0183,L1005,H1,False,2024-09-26,False,False,139.0,628.0,17,434000.0
185+V0184,L1005,H1,False,2026-01-14,True,False,139.0,628.0,19,462500.0
186+V0185,L1006,H1,False,2024-11-11,False,False,127.0,667.0,29,379000.0
187+V0186,L1007,H1,True,2025-06-22,True,True,140.0,575.0,22,410500.0
188+V0187,L1008,H1,True,2025-07-01,True,True,166.0,582.0,29,481000.0
189+V0188,L1009,H1,True,2025-03-21,True,True,149.0,595.0,39,403000.0
190+V0189,L1010,H2,False,2025-07-28,True,False,213.0,737.0,68,506000.0
191+V0190,L1011,H2,False,2023-01-10,False,False,247.0,708.0,66,438000.0
192+V0191,L1011,H2,False,2025-08-24,True,False,247.0,708.0,68,563500.0
193+V0192,L1012,H2,False,2025-07-02,True,False,182.0,745.0,5,597500.0
194+V0193,L1012,H2,False,2026-07-17,True,False,182.0,745.0,6,620500.0
195+V0194,L1013,H2,False,2023-09-13,False,False,176.0,767.0,47,418500.0
196+V0195,L1013,H2,False,2024-08-28,False,False,176.0,767.0,48,464500.0
197+V0196,L1014,H2,False,2023-04-09,False,False,240.0,739.0,53,534500.0
198+V0197,L1014,H2,False,2026-08-24,True,False,240.0,739.0,56,617500.0
199+V0198,L1015,H3,False,2025-07-06,True,False,511.0,1103.0,29,1275500.0
200+V0199,L1015,H3,False,2025-10-06,True,False,511.0,1103.0,29,1252500.0
201+V0200,L1016,H3,False,2024-03-15,False,False,574.0,1077.0,56,961500.0
202+V0201,L1017,H3,False,2025-01-27,True,False,546.0,1091.0,10,1135000.0
203+V0202,L1018,H3,False,2023-11-24,False,False,490.0,1161.0,52,1035500.0
204+V0203,L1018,H3,False,2024-11-28,False,False,490.0,1161.0,53,1123000.0
205+V0204,L1019,H3,False,2025-09-11,True,False,570.0,1054.0,41,1200500.0
206+V0205,L1019,H3,False,2025-09-30,True,False,570.0,1054.0,41,1190000.0
207+V0206,L1100,H1,False,2023-07-27,False,False,106.0,651.0,67,278000.0
208+V0207,L1100,H1,False,2026-05-03,True,False,106.0,651.0,70,324500.0
209+V0208,L1101,H1,False,2024-07-15,False,False,104.0,628.0,57,310500.0
210+V0209,L1101,H1,False,2025-05-16,True,False,104.0,628.0,58,302000.0
211+V0210,L1102,H1,False,2023-09-27,False,False,147.0,581.0,33,413000.0
212+V0211,L1103,H1,False,2025-02-27,True,False,156.0,566.0,67,356500.0
213+V0212,L1103,H1,False,2025-12-30,True,False,156.0,566.0,67,378500.0
214+V0213,L1104,H1,False,2024-07-22,False,False,150.0,589.0,38,359000.0
215+V0214,L1104,H1,False,2025-12-31,True,False,150.0,589.0,39,420500.0
216+V0215,L1105,H1,False,2026-03-11,True,False,147.0,603.0,22,440000.0
217+V0216,L1106,H1,False,2024-01-02,False,False,106.0,606.0,60,301000.0
218+V0217,L1107,H1,True,2024-01-08,False,False,123.0,632.0,29,376500.0
219+V0218,L1107,H1,True,2024-02-07,False,False,123.0,632.0,29,347500.0
220+V0219,L1108,H1,True,2023-06-28,False,False,122.0,669.0,22,357500.0
221+V0220,L1108,H1,True,2026-01-06,True,True,122.0,669.0,25,390000.0
222+V0221,L1109,H1,True,2024-09-15,False,False,148.0,622.0,53,400000.0
223+V0222,L1109,H1,True,2026-06-13,True,True,148.0,622.0,55,450500.0
224+V0223,L1110,H2,False,2024-03-12,False,False,202.0,771.0,65,498000.0
225+V0224,L1111,H2,False,2023-02-07,False,False,200.0,667.0,32,503500.0
226+V0225,L1111,H2,False,2026-06-22,True,False,200.0,667.0,35,542000.0
227+V0226,L1112,H2,False,2023-09-30,False,False,189.0,721.0,53,476000.0
228+V0227,L1112,H2,False,2024-08-01,False,False,189.0,721.0,54,487000.0
229+V0228,L1113,H2,False,2023-04-25,False,False,225.0,719.0,21,564500.0
230+V0229,L1113,H2,False,2024-04-14,False,False,225.0,719.0,22,625000.0
231+V0230,L1114,H2,False,2024-09-17,False,False,181.0,729.0,4,572000.0
232+V0231,L1115,H3,False,2024-03-25,False,False,484.0,1202.0,19,1005500.0
233+V0232,L1115,H3,False,2026-04-17,True,False,484.0,1202.0,21,1257000.0
234+V0233,L1116,H3,False,2024-05-13,False,False,662.0,1041.0,41,1342000.0
235+V0234,L1117,H3,False,2026-03-26,True,False,491.0,1087.0,55,1016500.0
236+V0235,L1118,H3,False,2023-12-17,False,False,637.0,1131.0,26,1207500.0
237+V0236,L1119,H3,False,2025-05-18,True,False,393.0,1173.0,15,1009500.0
238+V0237,L1200,H1,False,2025-07-12,True,False,138.0,593.0,24,462000.0
239+V0238,L1200,H1,False,2025-10-17,True,False,138.0,593.0,24,362500.0
240+V0239,L1201,H1,False,2024-05-15,False,False,145.0,620.0,47,344500.0
241+V0240,L1201,H1,False,2025-06-11,True,False,145.0,620.0,48,397500.0
242+V0241,L1202,H1,False,2025-07-15,True,False,106.0,620.0,39,276500.0
243+V0242,L1202,H1,False,2026-05-01,True,False,106.0,620.0,40,352500.0
244+V0243,L1203,H1,False,2023-01-18,False,False,162.0,597.0,61,344500.0
245+V0244,L1203,H1,False,2023-11-08,False,False,162.0,597.0,61,355000.0
246+V0245,L1204,H1,False,2026-01-30,True,False,186.0,592.0,61,405000.0
247+V0246,L1205,H1,False,2024-10-19,False,False,134.0,650.0,25,420500.0
248+V0247,L1205,H1,False,2025-08-04,True,False,134.0,650.0,26,352500.0
249+V0248,L1206,H1,False,2023-02-06,False,False,124.0,553.0,70,300500.0
250+V0249,L1206,H1,False,2023-11-01,False,False,124.0,553.0,70,322000.0
251+V0250,L1207,H1,True,2024-04-27,False,False,102.0,554.0,40,340000.0
252+V0251,L1207,H1,True,2026-02-24,True,True,102.0,554.0,42,337500.0
253+V0252,L1208,H1,True,2024-04-13,False,False,120.0,588.0,57,298500.0
254+V0253,L1209,H1,True,2025-08-25,True,True,165.0,625.0,30,391500.0
255+V0254,L1209,H1,True,2026-05-29,True,True,165.0,625.0,31,525500.0
256+V0255,L1210,H2,False,2024-04-26,False,False,249.0,705.0,50,524000.0
257+V0256,L1211,H2,False,2025-05-17,True,False,262.0,768.0,20,682500.0
258+V0257,L1212,H2,False,2024-04-18,False,False,206.0,699.0,49,566000.0
259+V0258,L1213,H2,False,2023-07-23,False,False,232.0,784.0,60,522500.0
260+V0259,L1214,H2,False,2023-02-10,False,False,157.0,778.0,24,446000.0
261+V0260,L1215,H3,False,2026-03-05,True,False,553.0,1190.0,9,1344500.0
262+V0261,L1216,H3,False,2023-11-30,False,False,564.0,1147.0,37,1021000.0
263+V0262,L1216,H3,False,2025-10-29,True,False,564.0,1147.0,39,1083000.0
264+V0263,L1217,H3,False,2024-03-29,False,False,407.0,1126.0,57,811500.0
265+V0264,L1217,H3,False,2026-05-08,True,False,407.0,1126.0,59,904000.0
266+V0265,L1218,H3,False,2025-06-06,True,False,460.0,1095.0,36,954500.0
267+V0266,L1218,H3,False,2025-12-18,True,False,460.0,1095.0,36,1185000.0
268+V0267,L1219,H3,False,2023-12-07,False,False,690.0,1065.0,57,1162000.0
269+V0268,L1219,H3,False,2025-01-15,True,False,690.0,1065.0,59,1170000.0
270+V0269,L1300,H1,False,2025-12-01,True,False,124.0,652.0,70,348000.0
271+V0270,L1300,H1,False,2026-06-04,True,False,124.0,652.0,71,401500.0
272+V0271,L1301,H1,False,2024-04-20,False,False,108.0,579.0,48,283500.0
273+V0272,L1301,H1,False,2024-05-01,False,False,108.0,579.0,48,367500.0
274+V0273,L1302,H1,False,2024-06-20,False,False,88.0,556.0,17,323000.0
275+V0274,L1303,H1,False,2023-11-03,False,False,144.0,594.0,12,425500.0
276+V0275,L1304,H1,False,2023-03-14,False,False,138.0,634.0,35,325500.0
277+V0276,L1304,H1,False,2023-08-07,False,False,138.0,634.0,35,354500.0
278+V0277,L1305,H1,False,2023-01-24,False,False,108.0,659.0,36,311500.0
279+V0278,L1305,H1,False,2023-02-09,False,False,108.0,659.0,36,351000.0
280+V0279,L1306,H1,False,2023-01-09,False,False,152.0,560.0,29,325000.0
281+V0280,L1306,H1,False,2023-01-24,False,False,152.0,560.0,29,404500.0
282+V0281,L1307,H1,True,2025-01-18,True,True,161.0,636.0,38,449000.0
283+V0282,L1307,H1,True,2025-10-24,True,True,161.0,636.0,38,471500.0
284+V0283,L1308,H1,True,2023-02-19,False,False,125.0,555.0,15,333000.0
285+V0284,L1308,H1,True,2024-10-13,False,False,125.0,555.0,16,369000.0
286+V0285,L1309,H1,True,2024-07-01,False,False,161.0,593.0,27,389500.0
287+V0286,L1309,H1,True,2026-03-09,True,True,161.0,593.0,29,478000.0
288+V0287,L1310,H2,False,2024-07-06,False,False,204.0,681.0,68,499000.0
289+V0288,L1311,H2,False,2023-05-29,False,False,223.0,685.0,13,626000.0
290+V0289,L1311,H2,False,2025-10-09,True,False,223.0,685.0,15,585000.0
291+V0290,L1312,H2,False,2024-09-01,False,False,267.0,686.0,36,600000.0
292+V0291,L1312,H2,False,2026-01-27,True,False,267.0,686.0,38,681500.0
293+V0292,L1313,H2,False,2025-07-11,True,False,254.0,792.0,27,658500.0
294+V0293,L1313,H2,False,2026-03-06,True,False,254.0,792.0,28,717500.0
295+V0294,L1314,H2,False,2025-04-05,True,False,185.0,700.0,54,419500.0
296+V0295,L1315,H3,False,2025-06-11,True,False,446.0,1177.0,51,984000.0
297+V0296,L1315,H3,False,2026-07-28,True,False,446.0,1177.0,52,1058000.0
298+V0297,L1316,H3,False,2023-10-20,False,False,509.0,1182.0,21,1204500.0
299+V0298,L1316,H3,False,2024-02-12,False,False,509.0,1182.0,22,1125500.0
300+V0299,L1317,H3,False,2023-04-06,False,False,629.0,1083.0,40,1202500.0
301+V0300,L1317,H3,False,2024-06-28,False,False,629.0,1083.0,41,1141500.0
302+V0301,L1318,H3,False,2023-04-30,False,False,565.0,1035.0,7,1429000.0
303+V0302,L1318,H3,False,2025-03-09,True,False,565.0,1035.0,9,1408500.0
304+V0303,L1319,H3,False,2023-10-15,False,False,668.0,1031.0,50,1216000.0
305+V0304,L1400,H1,False,2023-03-12,False,False,94.0,631.0,15,383000.0
306+V0305,L1401,H1,False,2024-03-11,False,False,85.0,603.0,23,304000.0
307+V0306,L1402,H1,False,2024-11-13,False,False,135.0,627.0,53,345500.0
308+V0307,L1402,H1,False,2026-08-31,True,False,135.0,627.0,55,347000.0
309+V0308,L1403,H1,False,2024-11-27,False,False,174.0,644.0,53,406500.0
310+V0309,L1403,H1,False,2026-04-16,True,False,174.0,644.0,55,411500.0
311+V0310,L1404,H1,False,2023-08-19,False,False,95.0,661.0,71,265000.0
312+V0311,L1404,H1,False,2025-02-06,True,False,95.0,661.0,73,237000.0
313+V0312,L1405,H1,False,2024-10-14,False,False,116.0,640.0,53,328500.0
314+V0313,L1406,H1,False,2023-03-12,False,False,108.0,662.0,44,293000.0
315+V0314,L1407,H1,True,2026-07-14,True,True,137.0,613.0,51,437000.0
316+V0315,L1408,H1,True,2025-05-27,True,True,159.0,667.0,24,489500.0
317+V0316,L1408,H1,True,2026-01-28,True,True,159.0,667.0,25,425500.0
318+V0317,L1409,H1,True,2024-03-28,False,False,118.0,671.0,53,321000.0
319+V0318,L1409,H1,True,2024-05-27,False,False,118.0,671.0,53,319500.0
320+V0319,L1410,H2,False,2023-08-25,False,False,230.0,687.0,3,656500.0
321+V0320,L1410,H2,False,2025-10-08,True,False,230.0,687.0,5,729000.0
322+V0321,L1411,H2,False,2026-06-17,True,False,205.0,713.0,35,541500.0
323+V0322,L1412,H2,False,2026-07-27,True,False,246.0,768.0,64,580000.0
324+V0323,L1413,H2,False,2024-09-19,False,False,233.0,797.0,47,503000.0
325+V0324,L1413,H2,False,2025-02-06,True,False,233.0,797.0,48,565000.0
326+V0325,L1414,H2,False,2023-09-06,False,False,212.0,708.0,51,450000.0
327+V0326,L1414,H2,False,2025-11-19,True,False,212.0,708.0,53,513000.0
328+V0327,L1415,H3,False,2025-08-28,True,False,448.0,1201.0,11,1147000.0
329+V0328,L1416,H3,False,2024-07-23,False,False,585.0,1187.0,51,1023000.0
330+V0329,L1416,H3,False,2026-02-06,True,False,585.0,1187.0,53,1103000.0
331+V0330,L1417,H3,False,2025-08-06,True,False,569.0,1051.0,10,1331000.0
332+V0331,L1418,H3,False,2024-01-17,False,False,507.0,1122.0,9,1208500.0
333+V0332,L1419,H3,False,2025-04-12,True,False,583.0,1179.0,18,1458000.0
334+V0333,L1419,H3,False,2025-09-22,True,False,583.0,1179.0,18,1194500.0
335+V0334,L1500,H1,False,2023-05-01,False,False,106.0,573.0,41,295500.0
336+V0335,L1500,H1,False,2026-05-20,True,False,106.0,573.0,44,325000.0
337+V0336,L1501,H1,False,2025-07-17,True,False,138.0,637.0,26,405500.0
338+V0337,L1502,H1,False,2025-08-03,True,False,147.0,638.0,35,424000.0
339+V0338,L1503,H1,False,2023-11-25,False,False,161.0,614.0,10,481500.0
340+V0339,L1504,H1,False,2023-03-05,False,False,136.0,654.0,45,314000.0
341+V0340,L1505,H1,False,2025-10-25,True,False,97.0,594.0,44,318500.0
342+V0341,L1506,H1,False,2026-06-17,True,False,154.0,600.0,52,337000.0
343+V0342,L1507,H1,True,2025-03-01,True,True,169.0,617.0,69,436000.0
344+V0343,L1507,H1,True,2026-03-31,True,True,169.0,617.0,70,395500.0
345+V0344,L1508,H1,True,2023-02-18,False,False,181.0,659.0,7,474000.0
346+V0345,L1508,H1,True,2025-09-24,True,True,181.0,659.0,9,475000.0
347+V0346,L1509,H1,True,2024-05-30,False,False,112.0,602.0,51,347500.0
348+V0347,L1509,H1,True,2024-06-18,False,False,112.0,602.0,51,323000.0
349+V0348,L1510,H2,False,2024-08-20,False,False,265.0,729.0,14,677000.0
350+V0349,L1510,H2,False,2024-08-27,False,False,265.0,729.0,14,716000.0
351+V0350,L1511,H2,False,2026-01-18,True,False,200.0,749.0,43,506500.0
352+V0351,L1512,H2,False,2026-08-31,True,False,284.0,777.0,29,721500.0
353+V0352,L1513,H2,False,2025-02-05,True,False,221.0,764.0,40,606000.0
354+V0353,L1514,H2,False,2024-12-02,False,False,307.0,686.0,52,631000.0
355+V0354,L1515,H3,False,2026-02-19,True,False,625.0,1075.0,5,1389000.0
356+V0355,L1516,H3,False,2023-10-08,False,False,503.0,1161.0,37,1060500.0
357+V0356,L1516,H3,False,2025-11-03,True,False,503.0,1161.0,39,1101000.0
358+V0357,L1517,H3,False,2023-08-06,False,False,577.0,1055.0,14,1176000.0
359+V0358,L1517,H3,False,2023-11-13,False,False,577.0,1055.0,14,1291000.0
360+V0359,L1518,H3,False,2026-01-10,True,False,525.0,1100.0,60,1008500.0
361+V0360,L1519,H3,False,2023-08-23,False,False,512.0,1034.0,48,1104000.0
362+V0361,L1600,H1,False,2023-05-17,False,False,159.0,667.0,50,356000.0
363+V0362,L1600,H1,False,2026-03-22,True,False,159.0,667.0,53,412500.0
364+V0363,L1601,H1,False,2025-01-05,True,False,142.0,563.0,29,391500.0
365+V0364,L1602,H1,False,2023-04-16,False,False,124.0,612.0,20,339500.0
366+V0365,L1603,H1,False,2024-01-11,False,False,158.0,651.0,39,323500.0
367+V0366,L1604,H1,False,2026-03-08,True,False,152.0,641.0,15,402500.0
368+V0367,L1605,H1,False,2023-01-08,False,False,139.0,616.0,46,355500.0
369+V0368,L1605,H1,False,2025-09-01,True,False,139.0,616.0,48,393500.0
370+V0369,L1606,H1,False,2023-02-27,False,False,131.0,569.0,72,326000.0
371+V0370,L1606,H1,False,2025-10-27,True,False,131.0,569.0,74,359000.0
372+V0371,L1607,H1,True,2023-10-23,False,False,129.0,562.0,42,338500.0
373+V0372,L1608,H1,True,2024-09-03,False,False,170.0,613.0,29,413000.0
374+V0373,L1608,H1,True,2026-02-17,True,True,170.0,613.0,31,472500.0
375+V0374,L1609,H1,True,2023-07-12,False,False,120.0,624.0,38,337500.0
376+V0375,L1609,H1,True,2025-05-17,True,True,120.0,624.0,40,353500.0
377+V0376,L1610,H2,False,2023-07-21,False,False,207.0,761.0,46,595000.0
378+V0377,L1610,H2,False,2023-10-25,False,False,207.0,761.0,46,520500.0
379+V0378,L1611,H2,False,2024-09-15,False,False,233.0,772.0,10,664000.0
380+V0379,L1612,H2,False,2023-02-06,False,False,209.0,677.0,14,555000.0
381+V0380,L1613,H2,False,2024-01-28,False,False,181.0,682.0,45,470000.0
382+V0381,L1613,H2,False,2026-01-07,True,False,181.0,682.0,47,429000.0
383+V0382,L1614,H2,False,2026-05-04,True,False,168.0,664.0,34,481000.0
384+V0383,L1615,H3,False,2024-09-20,False,False,363.0,1155.0,6,1149000.0
385+V0384,L1616,H3,False,2024-06-26,False,False,526.0,1052.0,14,1191000.0
386+V0385,L1617,H3,False,2024-07-02,False,False,512.0,1024.0,42,1137000.0
387+V0386,L1617,H3,False,2025-06-01,True,False,512.0,1024.0,43,1038000.0
388+V0387,L1618,H3,False,2024-03-01,False,False,507.0,1055.0,31,1076500.0
389+V0388,L1619,H3,False,2023-05-29,False,False,552.0,1184.0,2,1346500.0
390+V0389,L1619,H3,False,2023-08-02,False,False,552.0,1184.0,2,1227000.0
391+V0390,L1700,H1,False,2023-05-21,False,False,152.0,606.0,8,407000.0
392+V0391,L1701,H1,False,2023-04-11,False,False,169.0,572.0,54,411000.0
393+V0392,L1702,H1,False,2025-09-10,True,False,185.0,653.0,56,447500.0
394+V0393,L1702,H1,False,2026-05-29,True,False,185.0,653.0,57,420500.0
395+V0394,L1703,H1,False,2023-04-26,False,False,154.0,589.0,26,413000.0
396+V0395,L1703,H1,False,2024-09-10,False,False,154.0,589.0,27,363000.0
397+V0396,L1704,H1,False,2024-11-01,False,False,123.0,647.0,36,372500.0
398+V0397,L1705,H1,False,2026-05-27,True,False,132.0,601.0,18,464500.0
399+V0398,L1706,H1,False,2025-12-09,True,False,168.0,572.0,14,484000.0
400+V0399,L1706,H1,False,2026-08-16,True,False,168.0,572.0,15,466000.0
401+V0400,L1707,H1,True,2025-01-13,True,True,147.0,559.0,52,439000.0
402+V0401,L1707,H1,True,2025-09-07,True,True,147.0,559.0,52,418500.0
403+V0402,L1708,H1,True,2023-11-01,False,False,132.0,587.0,48,347000.0
404+V0403,L1708,H1,True,2024-07-02,False,False,132.0,587.0,49,352000.0
405+V0404,L1709,H1,True,2023-12-28,False,False,135.0,664.0,33,361000.0
406+V0405,L1710,H2,False,2025-11-07,True,False,236.0,798.0,27,602000.0
407+V0406,L1711,H2,False,2025-01-07,True,False,238.0,772.0,55,575000.0
408+V0407,L1711,H2,False,2025-05-25,True,False,238.0,772.0,55,500500.0
409+V0408,L1712,H2,False,2024-03-26,False,False,221.0,744.0,8,591500.0
410+V0409,L1713,H2,False,2025-04-02,True,False,261.0,712.0,49,585000.0
411+V0410,L1714,H2,False,2023-11-04,False,False,245.0,754.0,47,612500.0
412+V0411,L1715,H3,False,2024-11-07,False,False,473.0,1130.0,48,1146000.0
413+V0412,L1715,H3,False,2025-10-06,True,False,473.0,1130.0,49,1103000.0
414+V0413,L1716,H3,False,2024-05-08,False,False,567.0,1111.0,34,1266500.0
415+V0414,L1716,H3,False,2025-10-02,True,False,567.0,1111.0,35,1058000.0
416+V0415,L1717,H3,False,2025-04-30,True,False,575.0,1120.0,25,1216000.0
417+V0416,L1718,H3,False,2023-03-11,False,False,614.0,1094.0,27,1160500.0
418+V0417,L1718,H3,False,2026-05-19,True,False,614.0,1094.0,30,1301500.0
419+V0418,L1719,H3,False,2023-03-18,False,False,577.0,1017.0,38,1127000.0
420+V0419,L1719,H3,False,2024-07-22,False,False,577.0,1017.0,39,1107500.0
421+V0420,L1800,H1,False,2023-12-10,False,False,156.0,554.0,28,395500.0
422+V0421,L1800,H1,False,2026-02-15,True,False,156.0,554.0,31,415500.0
423+V0422,L1801,H1,False,2023-10-06,False,False,136.0,620.0,34,396000.0
424+V0423,L1802,H1,False,2023-12-28,False,False,104.0,661.0,30,342000.0
425+V0424,L1802,H1,False,2026-06-09,True,False,104.0,661.0,33,364500.0
426+V0425,L1803,H1,False,2025-09-07,True,False,133.0,641.0,55,393500.0
427+V0426,L1803,H1,False,2026-08-08,True,False,133.0,641.0,56,368000.0
428+V0427,L1804,H1,False,2023-02-11,False,False,127.0,635.0,12,399000.0
429+V0428,L1805,H1,False,2026-05-09,True,False,150.0,620.0,71,351000.0
430+V0429,L1806,H1,False,2025-02-21,True,False,105.0,601.0,52,292500.0
431+V0430,L1807,H1,True,2023-12-17,False,False,127.0,597.0,53,374000.0
432+V0431,L1807,H1,True,2024-09-22,False,False,127.0,597.0,54,351000.0
433+V0432,L1808,H1,True,2024-08-08,False,False,175.0,664.0,24,459000.0
434+V0433,L1808,H1,True,2025-02-24,True,True,175.0,664.0,25,549000.0
435+V0434,L1809,H1,True,2025-08-11,True,True,159.0,660.0,38,502500.0
436+V0435,L1810,H2,False,2024-06-18,False,False,207.0,710.0,23,606500.0
437+V0436,L1810,H2,False,2026-02-19,True,False,207.0,710.0,25,606000.0
438+V0437,L1811,H2,False,2025-12-15,True,False,246.0,719.0,34,602500.0
439+V0438,L1812,H2,False,2024-05-13,False,False,226.0,770.0,32,523500.0
440+V0439,L1812,H2,False,2025-02-02,True,False,226.0,770.0,33,624000.0
441+V0440,L1813,H2,False,2023-10-21,False,False,253.0,782.0,24,641000.0
442+V0441,L1813,H2,False,2025-07-16,True,False,253.0,782.0,26,564000.0
443+V0442,L1814,H2,False,2024-08-18,False,False,198.0,781.0,23,544000.0
444+V0443,L1815,H3,False,2023-04-07,False,False,416.0,1128.0,36,949500.0
445+V0444,L1816,H3,False,2023-10-25,False,False,579.0,1203.0,39,1132000.0
446+V0445,L1816,H3,False,2026-07-31,True,False,579.0,1203.0,42,1121000.0
447+V0446,L1817,H3,False,2024-07-02,False,False,501.0,1128.0,54,977500.0
448+V0447,L1817,H3,False,2024-09-15,False,False,501.0,1128.0,54,1035500.0
449+V0448,L1818,H3,False,2023-03-05,False,False,390.0,1003.0,47,785500.0
450+V0449,L1818,H3,False,2025-10-15,True,False,390.0,1003.0,49,907500.0
451+V0450,L1819,H3,False,2024-12-31,False,False,542.0,1190.0,12,1179000.0
452+V0451,L1819,H3,False,2025-01-27,True,False,542.0,1190.0,13,1277500.0
453+V0452,L1900,H1,False,2024-03-16,False,False,148.0,599.0,28,369500.0
454+V0453,L1900,H1,False,2024-11-05,False,False,148.0,599.0,28,358000.0
455+V0454,L1901,H1,False,2025-01-19,True,False,140.0,672.0,56,352500.0
456+V0455,L1902,H1,False,2023-01-29,False,False,150.0,648.0,54,351500.0
457+V0456,L1903,H1,False,2024-05-12,False,False,129.0,670.0,65,386000.0
458+V0457,L1903,H1,False,2025-07-12,True,False,129.0,670.0,66,384500.0
459+V0458,L1904,H1,False,2024-07-17,False,False,152.0,663.0,52,349500.0
460+V0459,L1904,H1,False,2024-10-02,False,False,152.0,663.0,52,335000.0
461+V0460,L1905,H1,False,2024-08-08,False,False,156.0,627.0,20,456000.0
462+V0461,L1906,H1,False,2025-04-29,True,False,107.0,623.0,54,332000.0
463+V0462,L1906,H1,False,2026-08-23,True,False,107.0,623.0,55,305500.0
464+V0463,L1907,H1,True,2024-11-13,False,False,143.0,657.0,35,386000.0
465+V0464,L1907,H1,True,2024-12-24,False,False,143.0,657.0,35,394500.0
466+V0465,L1908,H1,True,2024-04-27,False,False,127.0,641.0,54,327000.0
467+V0466,L1908,H1,True,2025-01-23,True,True,127.0,641.0,55,426000.0
468+V0467,L1909,H1,True,2025-04-26,True,True,90.0,624.0,55,343000.0
469+V0468,L1909,H1,True,2025-12-02,True,True,90.0,624.0,55,284000.0
470+V0469,L1910,H2,False,2026-05-07,True,False,182.0,686.0,49,497500.0
471+V0470,L1911,H2,False,2023-04-08,False,False,250.0,745.0,6,685500.0
472+V0471,L1912,H2,False,2024-08-20,False,False,213.0,695.0,34,553500.0
473+V0472,L1912,H2,False,2025-06-18,True,False,213.0,695.0,35,512000.0
474+V0473,L1913,H2,False,2025-07-14,True,False,155.0,682.0,67,412000.0
475+V0474,L1914,H2,False,2024-02-04,False,False,184.0,687.0,19,659500.0
476+V0475,L1914,H2,False,2024-02-13,False,False,184.0,687.0,19,539000.0
477+V0476,L1915,H3,False,2023-10-04,False,False,564.0,1207.0,13,1056000.0
478+V0477,L1916,H3,False,2025-09-06,True,False,483.0,1140.0,4,1322000.0
479+V0478,L1917,H3,False,2023-12-22,False,False,604.0,1130.0,36,1259500.0
480+V0479,L1917,H3,False,2026-03-31,True,False,604.0,1130.0,39,1172000.0
481+V0480,L1918,H3,False,2026-07-02,True,False,555.0,1056.0,32,1198500.0
482+V0481,L1919,H3,False,2024-04-18,False,False,403.0,1089.0,10,1151500.0
483+V0482,L1919,H3,False,2025-06-18,True,False,403.0,1089.0,11,1089500.0
modified manuscript/chapters/ch10.tex +220 −1
@@ -1,2 +1,221 @@
1 \chapter{Normes OEAQ, éthique et rapports}\label{ch:10} 1 \chapter{Normes OEAQ, éthique et rapports}\label{ch:10}
2−\stubchapter 2+
3+Les chapitres précédents ont établi ce qu'est la valeur, quels droits on évalue et dans quel cadre juridique et fiscal l'immeuble s'inscrit. Le présent chapitre traite d'une autre forme de contrainte, qui ne porte pas sur l'objet évalué mais sur celui qui évalue : les règles professionnelles qui encadrent l'acte d'évaluation au Québec, les devoirs déontologiques de l'évaluateur agréé, les cadres normatifs canadiens et internationaux avec lesquels la pratique québécoise dialogue, et la manière dont tout cela se traduit dans le rapport remis au client. Ces règles ne sont pas un ornement administratif : elles déterminent ce qu'un rapport doit contenir pour être recevable, ce qu'un évaluateur peut accepter comme mandat, ce qu'il doit divulguer et ce dont il répond. Le chapitre distingue soigneusement ce que la norme exige, ce que la loi prévoit, ce qui relève de l'usage et ce que l'on peut modéliser. Il comporte peu de calculs, mais il formalise quelques idées utiles : la conformité d'un rapport comme un taux de couverture d'une liste fermée d'éléments, l'incidence d'une hypothèse extraordinaire comme un écart de valeur, la probabilité qu'une lacune échappe à une chaîne de révisions. L'exemple intégral applique un contrôle automatique des douze éléments du contenu minimal à des extraits de rapports fictifs, et les exercices demandent au lecteur de réviser ces extraits d'un œil critique.
4+
5+\begin{objectifs}
6+\begin{itemize}
7+\item Décrire le système professionnel québécois de l'évaluation : titre réservé, champ d'exercice, conditions d'accès, obligations continues.
8+\item Distinguer, dans les Normes de pratique professionnelle de l'OEAQ, les règles coercitives des directives, la substance de l'évaluation (norme 1) de sa communication (norme 2), et les trois formes de rapport écrit.
9+\item Énumérer et appliquer les douze éléments du contenu minimal d'un rapport écrit, et formuler correctement une hypothèse extraordinaire avec son incidence sur la valeur.
10+\item Repérer les situations déontologiques sensibles (indépendance, conflit d'intérêts, double rôle, honoraires conditionnels, secret professionnel) et la conduite qu'exige le Code de déontologie.
11+\item Situer les NPP par rapport aux IVS 2025, au CUSPAP 2026, à l'USPAP 2024, au Red Book de la RICS et aux normes de l'IAAO, en connaissant l'état de vérification de chacun.
12+\item Organiser la révision et le contrôle de la qualité d'un rapport, et rédiger une conclusion de valeur dont le langage reflète fidèlement l'incertitude.
13+\item Expliquer le régime de responsabilité civile de l'évaluateur et le rôle de l'assurance responsabilité professionnelle.
14+\end{itemize}
15+\end{objectifs}
16+
17+\section{Le système professionnel québécois}\label{sec:10:systeme}
18+
19+\subsection{Un titre réservé, un champ d'exercice défini}
20+
21+Au Québec, l'évaluation immobilière est encadrée par le système professionnel que le Code des professions institue pour l'ensemble des ordres \parencite{codeProfessions}. L'Ordre des évaluateurs agréés du Québec (OEAQ), fondé en 1969, est l'un de ces ordres. La profession d'évaluateur est une profession à \motcle{titre réservé} et non à exercice exclusif : la loi réserve aux membres de l'Ordre les titres d'« évaluateur agréé » et d'« estimateur agréé », ainsi que les initiales « E.A. » et « C.App. » \parencite[art.~36~j)]{codeProfessions}, mais elle n'interdit pas à quiconque d'exprimer une opinion sur la valeur d'un immeuble. La conséquence pratique est importante : un courtier, un prêteur ou un logiciel peuvent produire une estimation, mais seul l'évaluateur agréé engage un titre professionnel, un code de déontologie, des normes de pratique, une inspection professionnelle et une assurance responsabilité obligatoire. C'est ce faisceau d'obligations, et non un monopole, qui fonde la valeur probante du rapport signé par un évaluateur agréé devant un tribunal, un prêteur ou une municipalité.
22+
23+Le champ d'exercice est décrit à l'article 37 j) du Code des professions : l'évaluateur agréé formule « une opinion dûment motivée de la valeur d'un bien ou d'un droit immobilier », et, en matière d'expropriation, de biens mobiliers ou immobiliers ; il détermine aussi la valeur des biens assujettis à l'évaluation foncière conformément à la Loi sur la fiscalité municipale \parencite[art.~37~j)]{codeProfessions}. Trois mots de cette définition structurent tout le chapitre. L'évaluateur produit une \emph{opinion}, c'est-à-dire un jugement professionnel et non un fait mesuré ; cette opinion est \emph{motivée}, c'est-à-dire qu'elle doit s'appuyer sur un raisonnement exposé et vérifiable ; elle porte sur la \emph{valeur} d'un bien ou d'un droit, ce qui renvoie aux bases de valeur du \cref{ch:2} et oblige à nommer celle qui est recherchée. L'évaluation foncière municipale, traitée au \cref{ch:9}, est expressément visée : c'est pourquoi les NPP comportent des normes propres au rôle d'évaluation, et pourquoi le Code de déontologie prévoit une règle particulière pour l'évaluateur qui confectionne un rôle et qui serait sollicité par un contribuable du même territoire.
24+
25+\begin{quebec}
26+L'OEAQ comptait \num{1188} membres au 31 mars 2025, dont 392 femmes et 796 hommes, auxquels s'ajoutaient 123 stagiaires et 535 étudiants inscrits ; 48 nouveaux permis avaient été délivrés durant l'exercice, et 7 radiations prononcées. Les membres exerçaient à 52,3 \% dans des firmes privées, à 19,7 \% dans des municipalités, à 18,1 \% dans les gouvernements et le secteur parapublic et à 3,2 \% dans des institutions financières ; les régions de Montréal (363 membres) et de la Capitale-Nationale (284) en regroupaient plus de la moitié \parencite[section~5, p.~59--61]{oeaqra2025}. Le rapport annuel 2024-2025 signale aussi que la couverture de l'assurance responsabilité professionnelle a été portée à \dollars{2000000} le 1\textsuperscript{er} avril 2025, qu'une modernisation des normes de pratique est en cours et qu'un projet de permis dédié aux évaluateurs résidentiels est à l'étude \parencite{oeaqra2025}. Ces chiffres, consultés le 31 octobre 2026, décrivent une profession de petite taille au regard des quelque \num{20000} ventes résidentielles trimestrielles du Québec, ce qui explique le recours croissant des prêteurs à des évaluations automatisées pour les dossiers simples et la place que l'inspection professionnelle accorde à la qualité des dossiers.
27+\end{quebec}
28+
29+\subsection{L'accès à la profession}
30+
31+Les conditions de délivrance du permis sont fixées par règlement de l'Ordre \parencite{permisoeaq}. Le candidat doit détenir un diplôme reconnu ou une équivalence, obtenir un certificat de stagiaire, accomplir un stage, suivre le cours de l'Ordre sur les normes et l'éthique, réussir l'examen d'admission et acquitter les frais, le tout dans un délai de cinq ans à compter du certificat de stagiaire \parencite[art.~1--2]{permisoeaq}. Le stage a une durée totale de 48 semaines, réparties en une ou plusieurs périodes d'au moins quatre semaines chacune, à raison d'au moins 35 heures par semaine \parencite[art.~8]{permisoeaq}. L'examen est annuel, comporte une partie orale et une partie écrite, porte sur le Code des professions, les règlements de l'Ordre, les normes de pratique, les méthodes et techniques d'évaluation, et exige la note minimale C \parencite[art.~23, 27, 29]{permisoeaq}. Le règlement situe l'examen « avant le premier dimanche de novembre », alors que le site de l'Ordre indique qu'il se tient au mois de septembre \parencite{oeaqdevenir} ; cette divergence entre le texte réglementaire et l'information administrative, relevée lors de la vérification des sources, est signalée ici parce qu'elle illustre une règle de méthode : lorsque deux sources officielles diffèrent, le texte réglementaire fait foi et l'écart est documenté.
32+
33+Trois diplômes universitaires québécois donnent accès au permis selon la page d'information de l'Ordre : le baccalauréat en administration des affaires de l'UQAM (concentration immobilier), le programme de gestion urbaine et immobilière de l'Université Laval et le programme de gestion immobilière et évaluation de l'UQO \parencite{oeaqdevenir}. Pour les diplômes obtenus hors du Québec, le règlement sur les normes d'équivalence exige un diplôme universitaire d'au moins \num{1350} heures, dont 585 heures réparties par domaine, et prévoit des équivalences de formation \parencite[art.~2, 4]{equivalenceoeaq}. Le \cref{tab:10:reglements} rassemble les règlements de l'Ordre que l'évaluateur rencontre dans sa pratique quotidienne, avec la disposition clé de chacun.
34+
35+\begin{table}[htbp]
36+\caption{Règlements de l'OEAQ encadrant la pratique (RLRQ, c. C-26) et dispositions clés}\label{tab:10:reglements}
37+\centering
38+\small
39+\begin{tabularx}{\textwidth}{lYY}
40+\toprule
41+Règlement & Objet & Disposition clé (paraphrase) \\
42+\midrule
43+r.~123 Code de déontologie & Devoirs envers le public, le client et la profession & Normes de pratique obligatoires (art.~4) ; indépendance (art.~17) ; conflits d'intérêts (art.~19) ; honoraires conditionnels interdits sauf exceptions (art.~24.1) ; rapport conforme aux normes (art.~41) \\
44+r.~124 Comité d'inspection professionnelle & Surveillance de l'exercice & Inspection portant « plus particulièrement sur les dossiers, livres et registres » ; comité de 12 membres inscrits depuis au moins 5 ans ; avis de 15 jours ; enquête particulière sur la compétence (art.~19) \\
45+r.~126 Délivrance des permis & Accès à la profession & Diplôme, certificat de stagiaire, stage de 48 semaines, cours normes et éthique, examen annuel ; délai de 5 ans (art.~1--2, 8, 23) \\
46+r.~127.1 Formation continue obligatoire & Maintien de la compétence & Au moins 30 heures par période de deux ans débutant le 1\textsuperscript{er} janvier d'une année paire, dont au moins 3 heures en éthique, déontologie et normes ; report maximal de 5 heures ; radiation en cas de défaut (art.~1--3, 17) \\
47+r.~129 Équivalences & Diplômes et formation hors Québec & Diplôme d'au moins \num{1350} heures dont 585 réparties (art.~2) \\
48+r.~133 Tenue des dossiers et cessation d'exercice & Dossier professionnel & Un dossier par contrat ou client ; contenu prescrit ; conservation d'au moins 5 ans après le dernier service ; confidentialité ; cession et garde provisoire (art.~2, 4, 6, 7, 17--29) \\
49+\bottomrule
50+\end{tabularx}
51+\tabsource{synthèse de l'auteur d'après LégisQuébec, textes à jour au 1\textsuperscript{er} juillet 2026, consultés le 31 octobre 2026 \parencite{deontOEAQ,inspectionoeaq,permisoeaq,fcOEAQ,equivalenceoeaq,tenuedossiersoeaq} ; liste des règlements : \textcite{oeaqreglements}.}
52+\end{table}
53+
54+\subsection{Les obligations continues : formation, dossiers, inspection}
55+
56+L'inscription au tableau ne clôt pas les obligations de l'évaluateur ; elle en ouvre trois autres, permanentes. La première est la \motcle{formation continue}. Le règlement exige au moins 30 heures d'activités de formation continue par période de référence de deux ans, qui débute le 1\textsuperscript{er} janvier d'une année paire, dont au moins 3 heures en éthique, en déontologie et en normes de pratique \parencite[art.~1--2]{fcOEAQ}. Un excédent d'au plus 5 heures peut être reporté à la période suivante ; la déclaration doit être transmise au plus tard le 31 janvier qui suit la fin de la période, les pièces justificatives conservées cinq ans, et le défaut non corrigé entraîne la radiation \parencite[art.~3, 7, 8, 17]{fcOEAQ}. On peut écrire le mécanisme du report sous forme récursive : si $H_t$ désigne les heures accomplies durant la période $t$ et $r_t$ le report reçu de la période précédente, le report transmis à la période suivante est
57+\begin{equation}\label{eq:10:report}
58+r_{t+1} = \min\bigl\{5,\ \max\{0,\ H_t + \min\{5, r_t\} - 30\}\bigr\},
59+\end{equation}
60+et la période est conforme si $H_t + \min\{5, r_t\} \geq 30$ et si les heures d'éthique, de déontologie et de normes atteignent 3. Le report ne s'applique qu'au volume total ; le minimum de trois heures en éthique doit être satisfait à l'intérieur de chaque période. L'\cref{ex:10:2} applique cette règle.
61+
62+La deuxième obligation est la \motcle{tenue des dossiers}. L'évaluateur constitue un dossier par contrat de services ou par client, y consigne les éléments prescrits et le conserve pendant au moins cinq ans après le dernier service rendu \parencite[art.~2, 4, 6]{tenuedossiersoeaq}. Cette durée a une portée qui dépasse l'archivage : c'est le dossier, et non le rapport, qui permet de reconstituer après coup le raisonnement, les données consultées, les vérifications faites et les communications avec le client. Un rapport sans dossier est une conclusion sans preuve. La troisième obligation est de se soumettre à l'\motcle{inspection professionnelle}. Le comité d'inspection, formé de douze membres inscrits au tableau depuis au moins cinq ans, surveille l'exercice de la profession « plus particulièrement sur les dossiers, livres et registres », donne un avis d'au moins quinze jours et peut recommander une enquête particulière sur la compétence d'un membre \parencite[art.~1, 2, 11, 19]{inspectionoeaq}. Les trois obligations se tiennent : la formation entretient la compétence, le dossier en garde la trace, l'inspection la vérifie. Le \cref{sec:10:revision} montre comment le contrôle interne de la qualité prolonge cette logique à l'échelle de chaque rapport.
63+
64+\section{Les Normes de pratique professionnelle}\label{sec:10:npp}
65+
66+\subsection{Architecture des normes}
67+
68+Les \motcle{Normes de pratique professionnelle} (NPP) de l'OEAQ sont le texte de référence de la pratique québécoise. L'édition en vigueur est celle « en vigueur depuis le 1\textsuperscript{er} février 2024 », date de la révision de la norme 11 consécutive à la nouvelle Loi concernant l'expropriation ; le fichier diffusé par l'Ordre est daté du 24 mars 2025, et les textes de fond des normes 1 et 2 portent des mentions d'adoption de 1998, 2000 et 2016 \parencite{oeaqNormes,oeaqnpppage}. Les normes sont organisées par paires : à chaque type d'acte professionnel correspond une norme sur l'acte lui-même et une norme sur sa communication. Le \cref{tab:10:structure} en donne la structure. Les normes 1 et 2, qui régissent l'évaluation d'un bien immobilier et la communication de ses résultats, sont celles que le présent ouvrage mobilise ; les normes 19 à 22 concernent l'évaluation municipale et sont évoquées au \cref{ch:9}. Les normes 17 et 18 sont annoncées « à venir ». Il n'existe pas, contrairement à une idée répandue, de « norme d'inspection professionnelle » dans les NPP : l'inspection professionnelle relève d'un règlement distinct \parencite{inspectionoeaq}, et l'inspection de l'immeuble, elle, est une règle de la norme 2.
69+
70+\begin{table}[htbp]
71+\caption{Structure des Normes de pratique professionnelle de l'OEAQ (édition en vigueur depuis le 1\textsuperscript{er} février 2024)}\label{tab:10:structure}
72+\centering
73+\small
74+\begin{tabular}{llp{62mm}}
75+\toprule
76+Normes & Objet & Remarques \\
77+\midrule
78+1 et 2 & Acte d'évaluation d'un bien immobilier ; communication & Cœur de la pratique ; règle 1.2 (12 éléments de substance), règle 2.3 (12 éléments du rapport), 2.4 inspection, 2.5 signature \\
79+3 et 4 & Examen d'un rapport d'évaluation & Révision par un tiers ; voir \cref{sec:10:revision} \\
80+5 et 6 & Consultation & Services autres que l'opinion de valeur \\
81+7 et 8 & Biens meubles & Hors du périmètre de l'ouvrage \\
82+9 et 10 & Entreprises & Hors du périmètre de l'ouvrage \\
83+11 et 12 ; 11.1 et 12.1 & Expropriation & Norme 11 révisée le 1\textsuperscript{er} février 2024 \\
84+13 et 14 & Assurance & Valeur de reconstruction (voir \cref{ch:7}) \\
85+15 et 16 & Fonds de prévoyance & Copropriété divise (\cref{ch:7,ch:42}) \\
86+17 et 18 & --- & « À venir » dans l'édition consultée \\
87+19 ; 20 à 20.3 & Rôle d'évaluation foncière et locative ; documents, évaluation municipale, révision & Voir \cref{ch:9} \\
88+21 et 22 & Recours & Contestation et expertise devant le TAQ \\
89+\bottomrule
90+\end{tabular}
91+\tabsource{table des matières des NPP \parencite{oeaqNormes}, consultée le 31 octobre 2026 ; annexes A à F non reproduites.}
92+\end{table}
93+
94+\subsection{Règles coercitives et directives}
95+
96+La première distinction à maîtriser est celle que l'introduction des NPP établit entre deux catégories de dispositions : « Les normes de pratique sont constituées de règles coercitives et de directives » \parencite[Intro-1]{oeaqNormes}. Les \motcle{règles coercitives} s'imposent sans exception ; l'introduction en dresse la liste, qui comprend notamment la règle 1.1, l'élément 12 de la règle 1.2 et les règles 2.1 à 2.5. Les \motcle{directives} décrivent la pratique attendue, mais l'évaluateur peut s'en écarter avec l'accord préalable du client, à condition d'exposer l'écart dans son rapport, ce qu'exige l'élément 11 de la règle 2.3 \parencite[Intro-1 ; règle 2.3]{oeaqNormes}. La distinction n'est pas académique. Elle signifie qu'un rapport ne peut jamais omettre l'un des douze éléments du contenu minimal ni se dispenser d'inspection sans explication, mais qu'il peut, par exemple, s'écarter d'une manière habituelle de présenter une analyse si le client y consent et si le rapport le dit. Elle signifie aussi que la conformité se vérifie à deux niveaux : la présence des éléments obligatoires, qui est binaire, et la justification des écarts aux directives, qui relève du jugement. Dans tout ce chapitre, la formule « la norme exige » renvoie à une règle coercitive, et « la norme prévoit » ou « il est d'usage » à une directive ou à une pratique.
97+
98+\subsection{Norme 1 : la substance de l'évaluation}
99+
100+La norme 1 porte sur l'acte d'évaluation lui-même, indépendamment de la façon dont il sera communiqué. Sa règle 1.2 énumère douze éléments que tout acte d'évaluation immobilière doit traiter \parencite[règle 1.2]{oeaqNormes} : l'objet de l'évaluation ; sa fin ; son but, c'est-à-dire la valeur recherchée ; sa date ; l'étendue du travail ; les réserves et hypothèses extraordinaires ; les biens meubles compris ; les démembrements du droit de propriété ; les restrictions au droit de propriété ; les forces du marché ; l'usage le meilleur et le plus profitable ; les méthodes et techniques. Trois de ces éléments méritent un commentaire immédiat, parce qu'ils relient la norme aux chapitres qui précèdent. L'élément 3 dispose que « Dans tous les cas, l'évaluateur doit définir le but qu'il vise à atteindre, i.e. la valeur qu'il recherche » \parencite[règle 1.2, élément 3]{oeaqNormes} : c'est l'obligation de nommer la base de valeur, et de retenir la définition légale lorsque la loi en prescrit une, comme la valeur réelle de la Loi sur la fiscalité municipale \parencite[art.~43--45]{lfm} ou l'indemnité d'expropriation. Les éléments 8 et 9 imposent d'identifier les démembrements et les restrictions au droit de propriété, ce qui est l'objet des \cref{ch:2,ch:6,ch:7}. L'élément 6 exige que l'évaluateur tienne compte « de toute réserve et hypothèse extraordinaires pouvant avoir une influence directe sur l'évaluation de l'immeuble » \parencite[règle 1.2, élément 6]{oeaqNormes} ; la \cref{sec:10:hypotheses} y revient.
101+
102+La norme 1 décrit donc un \emph{processus} autant qu'un contenu. La \cref{fig:10:processus} le schématise en sept étapes, de la définition du mandat à la communication, et indique pour chacune les éléments de la règle 1.2 qu'elle traite et les éléments de la règle 2.3 auxquels elle donnera lieu dans le rapport. L'ordre des étapes est celui de la pratique nord-américaine, que l'on retrouve chez \textcite{appraisal2026} et dans l'édition canadienne \parencite{appraisalCdn} ; il n'est pas prescrit tel quel par les NPP, mais il épouse leur logique : on ne peut pas choisir une méthode avant d'avoir identifié les droits évalués et l'usage le meilleur et le plus profitable, ni formuler une conclusion avant d'avoir réconcilié les indications.
103+
104+\begin{figure}[htbp]
105+\centering
106+\begin{tikzpicture}[node distance=3.2mm and 6mm, every node/.style={font=\sffamily\footnotesize},
107+ etape/.style={draw=brandPrimary, fill=brandLight, rounded corners=1pt, minimum width=46mm, minimum height=9mm, align=center},
108+ sub/.style={draw=brandGrayLight, fill=white, rounded corners=1pt, minimum width=36mm, minimum height=9mm, align=center, text width=34mm, font=\sffamily\scriptsize},
109+ rap/.style={draw=brandGrayLight, fill=white, rounded corners=1pt, minimum width=36mm, minimum height=9mm, align=center, text width=34mm, font=\sffamily\scriptsize},
110+ fleche/.style={-{Stealth[length=2mm]}, brandGray, thick}, lien/.style={brandGrayLight, thin, dashed}]
111+\node[etape] (e1) {1. Définir le mandat\\ objet, client, fin, but};
112+\node[etape, below=of e1] (e2) {2. Identifier le bien et les droits\\ désignation, démembrements, restrictions};
113+\node[etape, below=of e2] (e3) {3. Fixer la date et l'étendue du travail\\ hypothèses, réserves, inspection};
114+\node[etape, below=of e3] (e4) {4. Analyser le marché\\ forces du marché, données};
115+\node[etape, below=of e4] (e5) {5. Conclure à l'usage le meilleur\\ et le plus profitable};
116+\node[etape, below=of e5] (e6) {6. Appliquer et réconcilier les méthodes\\ comparaison, coût, revenu};
117+\node[etape, below=of e6, fill=brandSecondary!30] (e7) {7. Communiquer\\ rapport écrit, attestation, signature};
118+\foreach \i/\j in {e1/e2, e2/e3, e3/e4, e4/e5, e5/e6, e6/e7} \draw[fleche] (\i) -- (\j);
119+\node[sub, left=of e1] (s1) {Règle 1.2 : él. 1 objet, 2 fin, 3 but};
120+\node[sub, left=of e2] (s2) {Règle 1.2 : él. 7 biens meubles, 8 démembrements, 9 restrictions};
121+\node[sub, left=of e3] (s3) {Règle 1.2 : él. 4 date, 5 étendue, 6 réserves et hypothèses extraordinaires};
122+\node[sub, left=of e4] (s4) {Règle 1.2 : él. 10 forces du marché};
123+\node[sub, left=of e5] (s5) {Règle 1.2 : él. 11 usage le meilleur et le plus profitable};
124+\node[sub, left=of e6] (s6) {Règle 1.2 : él. 12 méthodes et techniques (coercitif)};
125+\node[rap, right=of e1] (r1) {Règle 2.3 : él. 3 but et fin, 4 définition de la valeur};
126+\node[rap, right=of e2] (r2) {Règle 2.3 : él. 1 identification, 2 droits évalués};
127+\node[rap, right=of e3] (r3) {Règle 2.3 : él. 5 date, 6 étendue, 7 réserves et hypothèses ; règle 2.4 inspection};
128+\node[rap, right=of e4] (r4) {Règle 2.3 : él. 8 traitement de l'information};
129+\node[rap, right=of e5] (r5) {Règle 2.3 : él. 9 usage le meilleur et le plus profitable};
130+\node[rap, right=of e6] (r6) {Règle 2.3 : él. 8 raisonnement, 10 rejet d'une méthode, 11 conformité et écarts};
131+\node[rap, right=of e7] (r7) {Règle 2.3 : él. 12 attestation ; règle 2.5 signature ; règle 2.1 qualités du rapport};
132+\foreach \i/\j in {s1/e1, s2/e2, s3/e3, s4/e4, s5/e5, s6/e6} \draw[lien] (\i) -- (\j);
133+\foreach \i/\j in {e1/r1, e2/r2, e3/r3, e4/r4, e5/r5, e6/r6, e7/r7} \draw[lien] (\i) -- (\j);
134+\node[above=1mm of s1, text=brandGray] {Substance (norme 1)};
135+\node[above=1mm of e1, text=brandGray] {Processus d'évaluation};
136+\node[above=1mm of r1, text=brandGray] {Communication (norme 2)};
137+\end{tikzpicture}
138+\caption{Le processus d'évaluation en sept étapes, mis en correspondance avec les douze éléments de substance de la règle 1.2 (à gauche) et les douze éléments du rapport écrit de la règle 2.3 (à droite). Chaque étape alimente une ou plusieurs rubriques du rapport ; aucune rubrique ne peut être rédigée sans l'étape qui la fonde.}\label{fig:10:processus}
139+\figsource{schéma de l'auteur d'après la structure des NPP \parencite{oeaqNormes} ; l'ordre des étapes suit l'usage nord-américain \parencite{appraisal2026}.}
140+\end{figure}
141+
142+\subsection{Norme 2 : la communication}
143+
144+La norme 2 régit la communication des résultats. Sa règle 2.1 fixe trois qualités que tout rapport doit posséder : un exposé clair et précis ; une information suffisante pour que le destinataire comprenne le rapport et puisse s'y fier ; la divulgation claire de « toute hypothèse ou toute réserve extraordinaire pouvant avoir une influence directe sur l'évaluation », avec l'indication de son incidence sur la valeur \parencite[règle 2.1 a), b), c)]{oeaqNormes}. Les règles 2.2 et 2.3 distinguent ensuite trois formes de rapport écrit, que le \cref{tab:10:formes} résume : le rapport narratif complet, le rapport abrégé et le rapport de mise à jour. La différence entre les formes ne tient pas à la liste des éléments traités, qui est la même, mais à la profondeur du traitement, que les NPP expriment par trois verbes : \emph{décrire}, \emph{résumer}, \emph{mentionner}. Le rapport de mise à jour est réservé au même client et renvoie au rapport original \parencite[règles 2.2--2.3 et annexe B]{oeaqNormes}. On retiendra que la forme abrégée n'autorise jamais l'omission : un élément résumé en une phrase reste un élément présent, et un élément absent rend le rapport non conforme, quelle que soit sa forme.
145+
146+\begin{table}[htbp]
147+\caption{Les trois formes de rapport écrit selon la norme 2 des NPP}\label{tab:10:formes}
148+\centering
149+\small
150+\begin{tabularx}{\textwidth}{lYYl}
151+\toprule
152+Forme & Usage typique & Traitement des douze éléments & Verbe \\
153+\midrule
154+Rapport narratif complet & Litiges, expropriation, immeubles complexes, mandats institutionnels & Chaque élément est développé, les données et le raisonnement sont exposés en détail & Décrire \\
155+Rapport abrégé & Financement hypothécaire résidentiel, mandats courants, formulaires normalisés & Chaque élément est présent mais condensé ; les données détaillées demeurent au dossier & Résumer \\
156+Rapport de mise à jour & Même client, même immeuble, nouvelle date d'évaluation & Renvoi au rapport original ; chaque élément est au moins mentionné ; les changements sont décrits & Mentionner \\
157+\bottomrule
158+\end{tabularx}
159+\tabsource{paraphrase de l'auteur d'après les règles 2.2 et 2.3 et l'annexe B des NPP \parencite{oeaqNormes} ; l'annexe B n'est pas reproduite.}
160+\end{table}
161+
162+Le cœur de la norme 2 est la règle 2.3, règle coercitive, qui énonce que tous les rapports écrits, de quelque forme qu'ils soient, doivent inclure douze éléments \parencite[règle 2.3]{oeaqNormes}. Le \cref{tab:10:elements} les met en correspondance avec les douze éléments de substance de la règle 1.2. Les deux listes ne coïncident pas terme à terme, et la différence est instructive. La règle 1.2 décrit ce que l'évaluateur doit \emph{faire} ; la règle 2.3 décrit ce que le lecteur doit \emph{trouver}. Ainsi, les forces du marché et les méthodes de la règle 1.2 se retrouvent dans l'élément 8 de la règle 2.3, « traitement de l'information et raisonnement », qui exige que le lecteur puisse suivre le chemin des données à la conclusion ; les biens meubles, les démembrements et les restrictions se retrouvent dans l'identification et les droits évalués ; et trois éléments de la règle 2.3 n'ont pas d'équivalent direct dans la règle 1.2, parce qu'ils sont propres à la communication : l'explication du rejet d'une méthode traditionnelle (élément 10), la déclaration de conformité à la substance et l'exposé des écarts aux directives (élément 11), et l'attestation (élément 12).
163+
164+\begin{table}[htbp]
165+\caption{Grille de contenu du rapport écrit : les douze éléments de la règle 2.3 et leur correspondance avec la règle 1.2}\label{tab:10:elements}
166+\centering
167+\small
168+\begin{tabularx}{\textwidth}{clYl}
169+\toprule
170+{N\textsuperscript{o}} & Élément du rapport (règle 2.3) & Ce que le lecteur doit pouvoir vérifier & Règle 1.2 \\
171+\midrule
172+1 & Identification physique et légale & Quel immeuble, quel lot, quelle adresse, quelles composantes ; biens meubles inclus & 1, 7 \\
173+2 & Droits de propriété évalués & Pleine propriété ou droit démembré ; charges, servitudes, baux, restrictions & 8, 9 \\
174+3 & But et fin de l'évaluation & Quelle valeur est recherchée, pour quel usage, pour quel destinataire & 2, 3 \\
175+4 & Définition de la valeur & Texte de la définition retenue (valeur marchande des NPP, valeur réelle de la LFM, etc.) & 3 \\
176+5 & Date d'évaluation & Date à laquelle l'opinion s'applique, distincte de la date du rapport et de l'inspection & 4 \\
177+6 & Étendue du travail & Recherches, vérifications, inspection, limites du travail accompli & 5 \\
178+7 & Réserves et hypothèses de base & Réserves limitant la portée ; hypothèses de base ; hypothèses extraordinaires avec incidence & 6 \\
179+8 & Traitement de l'information et raisonnement & Données, analyse du marché, méthodes, ajustements, réconciliation & 10, 12 \\
180+9 & Usage le meilleur et le plus profitable & Conclusion motivée sur l'usage, terrain vacant et tel qu'aménagé & 11 \\
181+10 & Rejet d'une méthode traditionnelle & Pourquoi une méthode n'a pas été appliquée & 12 \\
182+11 & Conformité à la substance ; écarts aux directives & Déclaration de conformité à la norme 1 ; écarts convenus avec le client & --- \\
183+12 & Attestation & Déclaration signée et datée selon le modèle imposé & --- \\
184+\bottomrule
185+\end{tabularx}
186+\tabsource{paraphrase de l'auteur d'après les règles 1.2 et 2.3 des NPP \parencite{oeaqNormes} ; la correspondance (dernière colonne) est une interprétation de l'auteur, non un tableau de la norme.}
187+\end{table}
188+
189+Cette liste fermée permet de définir une mesure élémentaire de conformité. Pour un rapport donné, soit $c_k$ l'indicatrice de présence de l'élément $k$ :
190+\begin{equation}\label{eq:10:indicatrice}
191+c_k = \mathbf{1}\{\text{l'élément } k \text{ de la règle 2.3 est traité dans le rapport}\}, \qquad k = 1, \dots, 12 .
192+\end{equation}
193+Le \motcle{taux de conformité formelle} est la proportion des éléments présents :
194+\begin{equation}\label{eq:10:taux}
195+\tau = \frac{1}{12} \sum_{k=1}^{12} c_k .
196+\end{equation}
197+Puisque la règle 2.3 est coercitive, le seul taux acceptable est $\tau = 1$ ; tout rapport pour lequel $\tau < 1$ est non conforme, et la liste des $k$ tels que $c_k = 0$ est la liste des corrections à apporter. La mesure est volontairement rudimentaire : elle ne dit rien de la qualité du traitement, et un élément présent peut être mal traité. Son intérêt est opérationnel : elle se prête à une liste de contrôle, à une révision par un tiers et, comme le montre l'exemple de la \cref{sec:10:exemple}, à un contrôle automatique de premier niveau.
198+
199+\subsection{Hypothèses extraordinaires et réserves}\label{sec:10:hypotheses}
200+
201+Parmi les douze éléments, le septième concentre la plus grande part des difficultés rédactionnelles et des litiges. La règle 2.3 dispose que « Dans tous les rapports, l'évaluateur doit mentionner les réserves limitant la portée de son acte et les hypothèses de base », et que les hypothèses extraordinaires doivent être décrites et rappelées chaque fois que la conclusion de valeur est énoncée \parencite[règle 2.3, élément 7]{oeaqNormes}. L'annexe E des NPP fournit un modèle de réserves et d'hypothèses de base, non contraignant quant à la forme mais à suivre sur le fond \parencite{oeaqNormes}. Il faut distinguer trois notions. Une \motcle{réserve} (ou condition limitative) délimite ce que l'évaluateur n'a pas fait ou ne garantit pas : il n'a pas vérifié les titres, il n'est pas ingénieur, il n'a pas inspecté les parties inaccessibles. Une \motcle{hypothèse de base} est une supposition courante, tenue pour vraie sauf indication contraire : le titre est valide, les renseignements fournis sont exacts, aucun vice caché n'affecte le bien. Une \motcle{hypothèse extraordinaire} est une supposition propre au mandat, dont la fausseté changerait matériellement la conclusion : le permis sera délivré, la contamination signalée est absente, l'immeuble sera livré conforme aux plans.
202+
203+Le CUSPAP 2026 de l'Institut canadien des évaluateurs (ICE, \emph{Appraisal Institute of Canada}) offre une terminologie plus fine, utile pour clarifier la pratique québécoise. Il distingue l'\emph{extraordinary assumption}, hypothèse directement liée au mandat qui, si elle se révélait fausse, « could materially alter the opinions or conclusions » ; la \emph{hypothetical condition}, cas particulier où l'on tient pour vraie une information « simulated but untrue » sur les caractéristiques physiques, légales ou économiques du bien ; et l'\emph{extraordinary limiting condition}, modification ou exclusion d'une règle de la norme qui peut réduire la fiabilité du rapport \parencite[s.~3, Definitions]{cuspap2026}. La condition hypothétique est l'hypothèse extraordinaire dont on \emph{sait} qu'elle est fausse à la date d'évaluation : évaluer un immeuble « comme si » les travaux étaient achevés, par exemple pour un financement à la construction. Les NPP ne distinguent pas formellement ces deux cas, mais l'exigence d'en indiquer l'incidence sur la valeur s'applique aux deux, et la pratique prudente consiste à nommer explicitement la nature de l'hypothèse.
204+
205+L'exigence d'indiquer l'incidence sur la valeur se formalise simplement. Soit $V_{\mid H}$ la valeur conclue sous l'hypothèse extraordinaire $H$ et $V_{\mid \neg H}$ la valeur qui prévaudrait si $H$ était fausse. L'\motcle{incidence} de l'hypothèse est l'écart
206+\begin{equation}\label{eq:10:incidence}
207+\Delta V_H = V_{\mid H} - V_{\mid \neg H},
208+\end{equation}
209+que le rapport exprime en dollars et, utilement, en proportion de $V_{\mid \neg H}$. La raison pour laquelle la norme exige cette indication apparaît si l'on considère le lecteur du rapport, qui ne sait pas si $H$ est vraie. S'il attribue à $H$ une probabilité $p$, la valeur qu'il devrait retenir pour sa décision est l'espérance
210+\begin{equation}\label{eq:10:esperance}
211+\E[V] = p\, V_{\mid H} + (1 - p)\, V_{\mid \neg H} = V_{\mid \neg H} + p\, \Delta V_H ,
212+\end{equation}
213+de sorte qu'un rapport qui ne donne que $V_{\mid H}$ surestime la valeur attendue de $(1 - p)\,\Delta V_H$. Donner $\Delta V_H$, ou les deux valeurs, restitue au lecteur l'information nécessaire ; c'est lui, et non l'évaluateur, qui connaît ou apprécie $p$. L'évaluateur ne doit pas remplacer $V_{\mid H}$ par $\E[V]$, parce que la valeur marchande est une estimation conditionnelle à un état du bien et non un pari sur cet état ; mais il doit rendre le calcul possible. Dans l'extrait fictif B de la \cref{sec:10:exemple}, l'hypothèse qu'un permis d'agrandissement a été obtenu conduit à une valeur de \dollars{612000} ; si, sans permis, la valeur était de \dollars{575000}, l'incidence serait de \dollars{37000}, soit 6,43 \%, et un lecteur qui attribuerait 60 \% de chances à l'existence du permis retiendrait \dollars{597200}. Ces chiffres sont fictifs et ne servent qu'à illustrer le mécanisme.
214+
215+\begin{pratique}
216+Une hypothèse extraordinaire bien rédigée comporte quatre éléments : l'énoncé précis de ce qui est supposé ; la raison pour laquelle l'évaluateur ne peut pas le vérifier à la date du rapport ; l'incidence sur la valeur si l'hypothèse se révélait fausse, en dollars ou en pourcentage, ou au minimum son sens et son ordre de grandeur ; et le rappel de l'hypothèse à chaque énoncé de la conclusion, y compris dans le sommaire et la lettre d'envoi. Une formulation du type « nous présumons que les travaux sont conformes » qui n'est suivie d'aucune de ces précisions n'est pas une hypothèse extraordinaire au sens de la norme : c'est une réserve déguisée, et elle n'exonère de rien, comme le rappelle la \cref{sec:10:responsabilite}.
217+\end{pratique}
218+
219+\subsection{Inspection et signature}
220+
221+Deux règles coercitives closent la norme 2. La règle 2.4 pose que « Tout immeuble évalué doit faire l'objet d'une inspection, à moins de circonstances exceptionnelles lesquelles seront expliquées au rapport » ; chaque signataire atteste avoir personnellement inspecté l'immeuble ou, à défaut, le mentionne explicitement ; l'inspection doit au minimum permettre de décrire l'immeuble, de fonder la conclusion sur l'usage le meilleur et le plus profitable et de soutenir les comparaisons \parencite[règle 2.4]{oeaqNormes}. Le \cref{ch:11} traite de l'inspection comme technique ; ici, il suffit de noter que la norme en fait une obligation dont l'omission doit être expliquée, et non une option tarifaire. Les évaluations « sur dossier » que certains prêteurs demandent pour des biens standard ne sont conformes que si le rapport dit clairement que l'immeuble n'a pas été inspecté, pourquoi, et quelles hypothèses en découlent. La règle 2.5 porte sur la signature : elle peut être manuscrite ou électronique selon un procédé reconnu par le secrétaire de l'Ordre, et le signataire « doit accepter l'entière responsabilité du contenu et des conclusions du rapport » \parencite[règle 2.5]{oeaqNormes}. Cette phrase a une portée considérable pour les cabinets où des stagiaires ou des techniciens préparent les rapports : la signature n'est pas une formalité de validation, elle transfère au signataire la responsabilité de chaque chiffre et de chaque phrase, ce que l'article 33 du Code de déontologie confirme en obligeant l'évaluateur à superviser et réviser le travail qu'il délègue \parencite[art.~33]{deontOEAQ}.
modified manuscript/chapters/ch47.tex +67 −56
@@ -1,8 +1,7 @@
1 \chapter{Cas 3 : immeuble de 12 logements à Montréal}\label{ch:47} 1 \chapter{Cas 3 : immeuble de 12 logements à Montréal}\label{ch:47}
2 % Renvois vers d'autres chapitres : \cref si le label est défini (compilation du livre complet), texte simple sinon (compilation isolée). 2 % Renvois vers d'autres chapitres : \cref si le label est défini (compilation du livre complet), texte simple sinon (compilation isolée).
3−\providecommand{\chref}[1]{\ifcsundef{r@ch:#1}{chapitre~#1}{\cref{ch:#1}}}
4 3
5−Ce chapitre réunit, dans un seul dossier d'évaluation, l'ensemble des outils de la partie consacrée au revenu et au multilogement. L'immeuble étudié est un bâtiment de douze logements du quartier Rosemont--La Petite-Patrie, à Montréal, dont les loyers en place sont inférieurs au marché, dont la toiture arrive en fin de vie et dont le financement sera, comme c'est le cas pour la plupart des immeubles de six logements et plus en 2026, limité par la couverture de la dette plutôt que par le ratio prêt-valeur. Le lecteur suit l'évaluateur depuis la définition du mandat jusqu'à la rédaction de la conclusion : analyse de l'état locatif sous les contraintes du Code civil et du Tribunal administratif du logement, normalisation des dépenses, extraction du taux global sur quarante ventes, capitalisation directe, flux actualisés sur dix ans, comparaison par logement, financement, test de résistance par simulation de Monte-Carlo, puis réconciliation et rapport. Les chapitres de méthode (du \chref{26} au \chref{31}, puis le \chref{40}) ont présenté chacune de ces étapes isolément à partir des mêmes données ; le présent chapitre montre comment elles s'articulent et, surtout, comment les hypothèses de l'une contraignent les autres. Toutes les données de ce dossier sont synthétiques : l'immeuble n'existe pas, mais chaque ordre de grandeur est calibré sur des publications publiques citées au fil du texte. 4+Ce chapitre réunit, dans un seul dossier d'évaluation, l'ensemble des outils de la partie consacrée au revenu et au multilogement. L'immeuble étudié est un bâtiment de douze logements du quartier Rosemont--La Petite-Patrie, à Montréal, dont les loyers en place sont inférieurs au marché, dont la toiture arrive en fin de vie et dont le financement sera, comme c'est le cas pour la plupart des immeubles de six logements et plus en 2026, limité par la couverture de la dette plutôt que par le ratio prêt-valeur. Le lecteur suit l'évaluateur depuis la définition du mandat jusqu'à la rédaction de la conclusion : analyse de l'état locatif sous les contraintes du Code civil et du Tribunal administratif du logement, normalisation des dépenses, extraction du taux global sur quarante ventes, capitalisation directe, flux actualisés sur dix ans, comparaison par logement, financement, test de résistance par simulation de Monte-Carlo, puis réconciliation et rapport. Les chapitres de méthode (du \cref{ch:26} au \cref{ch:31}, puis le \cref{ch:40}) ont présenté chacune de ces étapes isolément à partir des mêmes données ; le présent chapitre montre comment elles s'articulent et, surtout, comment les hypothèses de l'une contraignent les autres. Toutes les données de ce dossier sont synthétiques : l'immeuble n'existe pas, mais chaque ordre de grandeur est calibré sur des publications publiques citées au fil du texte.
6 5
7 \begin{objectifs} 6 \begin{objectifs}
8 À la fin de ce chapitre, le lecteur sait : 7 À la fin de ce chapitre, le lecteur sait :
@@ -18,7 +17,7 @@ Ce chapitre réunit, dans un seul dossier d'évaluation, l'ensemble des outils d
18 \end{objectifs} 17 \end{objectifs}
19 18
20 \begin{synthetique}[Données synthétiques — immeuble fictif] 19 \begin{synthetique}[Données synthétiques — immeuble fictif]
21−Le sujet de ce chapitre et toutes les données qui l'entourent sont \emph{synthétiques}. Les fichiers \texttt{data/synthetic/cas3\_montreal\_sujet.json}, \texttt{cas3\_montreal\_etat\_locatif.csv} (douze baux), \texttt{cas3\_montreal\_depenses.csv} (trois exercices, 2023 à 2025), \texttt{cas3\_montreal\_comparables.csv} (quarante ventes d'immeubles de 6 à 30 logements, septembre 2024 à août 2026) et \texttt{cas3\_montreal\_hypotheses\_dcf.json} ont été générés par \texttt{analysis/python/traite/synth.py} avec la graine \texttt{20261031}. Les loyers de marché sont calibrés sur l'Enquête sur les logements locatifs de la SCHL d'octobre 2025 (zone Rosemont), les taux de capitalisation sur la fourchette CBRE du troisième trimestre 2026 pour le multirésidentiel de faible hauteur de catégorie B à Montréal, le taux hypothécaire sur les fonds avancés assurés déclarés à la Banque du Canada en août 2026, les taxes sur le taux général résidentiel 2026 de la Ville de Montréal et le taux de la taxe scolaire 2026-2027. Les lois de génération sont décrites dans \texttt{data/synthetic/README.md} ; les relations entre variables (décote d'ancienneté des baux, effet de l'état sur le taux) sont des hypothèses pédagogiques et non des estimations sur données réelles. Tous les chiffres du chapitre sont recalculés par \texttt{analysis/python/cases/ch47\_dossier.py}, qui s'appuie sur la bibliothèque partagée \texttt{traite/cas3.py} utilisée aussi du \chref{26} au \chref{31}. 20+Le sujet de ce chapitre et toutes les données qui l'entourent sont \emph{synthétiques}. Les fichiers \texttt{data/synthetic/cas3\_montreal\_sujet.json}, \texttt{cas3\_montreal\_etat\_locatif.csv} (douze baux), \texttt{cas3\_montreal\_depenses.csv} (trois exercices, 2023 à 2025), \texttt{cas3\_montreal\_comparables.csv} (quarante ventes d'immeubles de 6 à 30 logements, septembre 2024 à août 2026) et \texttt{cas3\_montreal\_hypotheses\_dcf.json} ont été générés par \texttt{analysis/python/traite/synth.py} avec la graine \texttt{20261031}. Les loyers de marché sont calibrés sur l'Enquête sur les logements locatifs de la SCHL d'octobre 2025 (zone Rosemont), les taux de capitalisation sur la fourchette CBRE du troisième trimestre 2026 pour le multirésidentiel de faible hauteur de catégorie B à Montréal, le taux hypothécaire sur les fonds avancés assurés déclarés à la Banque du Canada en août 2026, les taxes sur le taux général résidentiel 2026 de la Ville de Montréal et le taux de la taxe scolaire 2026-2027. Les lois de génération sont décrites dans \texttt{data/synthetic/README.md} ; les relations entre variables (décote d'ancienneté des baux, effet de l'état sur le taux) sont des hypothèses pédagogiques et non des estimations sur données réelles. Tous les chiffres du chapitre sont recalculés par \texttt{analysis/python/cases/ch47\_dossier.py}, qui s'appuie sur la bibliothèque partagée \texttt{traite/cas3.py} utilisée aussi du \cref{ch:26} au \cref{ch:31}.
22 \end{synthetique} 21 \end{synthetique}
23 22
24 \section{Mandat et étendue de la mission}\label{sec:47:mandat} 23 \section{Mandat et étendue de la mission}\label{sec:47:mandat}
@@ -27,7 +26,7 @@ Le dossier s'ouvre, comme tout acte d'évaluation, par la définition de ce qui
27 26
28 La base de valeur retenue est la valeur marchande telle que la définissent les Normes de pratique professionnelle de l'Ordre des évaluateurs agréés du Québec : « le prix sincère le plus probable, de la vente réelle ou présumée d'un immeuble, à une date donnée, sur un marché libre et ouvert à la concurrence » \parencite{oeaqNormes}, sous les quatre conditions que la norme énumère et que le rapport paraphrase : des parties bien informées de l'état de l'immeuble, des conditions du marché et de l'usage le plus probable ; une exposition suffisante sur le marché ; un paiement en argent comptant ou l'équivalent ; un prix épuré de tout financement avantageux ou de toute mesure incitative. Cette définition converge avec celle de la \emph{market value} des Normes internationales d'évaluation (IVS 102, annexe A10) \parencite{ivs2025} et avec celle des Normes uniformes de pratique professionnelle en matière d'évaluation au Canada \parencite{cuspap2026}. La distinction importe pour un prêteur : la valeur marchande n'est ni la valeur d'investissement propre à l'acheteur (qui dépend de sa fiscalité, de son financement et de ses projets de rattrapage des loyers), ni la valeur au rôle d'évaluation foncière, laquelle est établie à une autre date et, pour un immeuble locatif, sous l'hypothèse que « le vendeur est réputé détenir tous les droits du locataire » \parencite[art.~45.1]{lfm}, c'est-à-dire comme si l'immeuble était libre de baux. Nous reviendrons sur cet écart à la \cref{sec:47:reconciliation}. 27 La base de valeur retenue est la valeur marchande telle que la définissent les Normes de pratique professionnelle de l'Ordre des évaluateurs agréés du Québec : « le prix sincère le plus probable, de la vente réelle ou présumée d'un immeuble, à une date donnée, sur un marché libre et ouvert à la concurrence » \parencite{oeaqNormes}, sous les quatre conditions que la norme énumère et que le rapport paraphrase : des parties bien informées de l'état de l'immeuble, des conditions du marché et de l'usage le plus probable ; une exposition suffisante sur le marché ; un paiement en argent comptant ou l'équivalent ; un prix épuré de tout financement avantageux ou de toute mesure incitative. Cette définition converge avec celle de la \emph{market value} des Normes internationales d'évaluation (IVS 102, annexe A10) \parencite{ivs2025} et avec celle des Normes uniformes de pratique professionnelle en matière d'évaluation au Canada \parencite{cuspap2026}. La distinction importe pour un prêteur : la valeur marchande n'est ni la valeur d'investissement propre à l'acheteur (qui dépend de sa fiscalité, de son financement et de ses projets de rattrapage des loyers), ni la valeur au rôle d'évaluation foncière, laquelle est établie à une autre date et, pour un immeuble locatif, sous l'hypothèse que « le vendeur est réputé détenir tous les droits du locataire » \parencite[art.~45.1]{lfm}, c'est-à-dire comme si l'immeuble était libre de baux. Nous reviendrons sur cet écart à la \cref{sec:47:reconciliation}.
29 28
30−Le droit évalué est la pleine propriété \parencite[art.~947]{ccq} grevée des douze baux résidentiels en vigueur. Au Québec, l'aliénation de l'immeuble « ne permet pas au nouveau locateur de résilier le bail » \parencite[art.~1937]{ccq} : l'acheteur acquiert donc le droit du locateur (\emph{leased fee}), c'est-à-dire le droit de percevoir les loyers contractuels, reconduits de plein droit et encadrés dans leur augmentation, et non le droit de relouer immédiatement au prix du marché. C'est ce droit, et aucun autre, que la valeur marchande doit refléter ; le \chref{2} a montré que l'écart entre la valeur du droit du locateur et la valeur libre de baux est la valeur actualisée du déficit de loyer, pondérée par la probabilité et le calendrier de sa résorption. 29+Le droit évalué est la pleine propriété \parencite[art.~947]{ccq} grevée des douze baux résidentiels en vigueur. Au Québec, l'aliénation de l'immeuble « ne permet pas au nouveau locateur de résilier le bail » \parencite[art.~1937]{ccq} : l'acheteur acquiert donc le droit du locateur (\emph{leased fee}), c'est-à-dire le droit de percevoir les loyers contractuels, reconduits de plein droit et encadrés dans leur augmentation, et non le droit de relouer immédiatement au prix du marché. C'est ce droit, et aucun autre, que la valeur marchande doit refléter ; le \cref{ch:02} a montré que l'écart entre la valeur du droit du locateur et la valeur libre de baux est la valeur actualisée du déficit de loyer, pondérée par la probabilité et le calendrier de sa résorption.
31 30
32 L'étendue du travail couvre l'inspection de l'immeuble (visite fictive du 25~août 2026, intérieur de neuf logements sur douze, aires communes, toiture, enveloppe, sous-sol technique), l'examen de l'état locatif et des baux fournis par le vendeur, l'examen des états de dépenses de 2023 à 2025, la consultation du rôle d'évaluation 2026-2028 et du compte de taxes 2026, la recherche de ventes d'immeubles comparables sur l'île de Montréal, la collecte des indicateurs de marché publiés par la SCHL, l'APCIQ, CBRE et la Banque du Canada, puis l'application de trois méthodes : la méthode du revenu par capitalisation directe et par actualisation des flux, et la méthode de comparaison par unité de revenu. La méthode du coût n'est pas appliquée : pour un immeuble locatif de 1962 dont le marché est actif et dont la valeur repose sur le revenu, la mesure de la dépréciation accumulée serait trop incertaine pour que l'indication ait un poids ; la Norme 2 exige que ce rejet soit justifié au rapport, ce qui est fait ici \parencite{oeaqNormes}. 31 L'étendue du travail couvre l'inspection de l'immeuble (visite fictive du 25~août 2026, intérieur de neuf logements sur douze, aires communes, toiture, enveloppe, sous-sol technique), l'examen de l'état locatif et des baux fournis par le vendeur, l'examen des états de dépenses de 2023 à 2025, la consultation du rôle d'évaluation 2026-2028 et du compte de taxes 2026, la recherche de ventes d'immeubles comparables sur l'île de Montréal, la collecte des indicateurs de marché publiés par la SCHL, l'APCIQ, CBRE et la Banque du Canada, puis l'application de trois méthodes : la méthode du revenu par capitalisation directe et par actualisation des flux, et la méthode de comparaison par unité de revenu. La méthode du coût n'est pas appliquée : pour un immeuble locatif de 1962 dont le marché est actif et dont la valeur repose sur le revenu, la mesure de la dépréciation accumulée serait trop incertaine pour que l'indication ait un poids ; la Norme 2 exige que ce rejet soit justifié au rapport, ce qui est fait ici \parencite{oeaqNormes}.
33 32
@@ -63,7 +62,7 @@ Le terrain mesure \msq{740}. Le bâtiment, construit en 1962, compte trois étag
63 62
64 \subsection{Zonage et droits acquis} 63 \subsection{Zonage et droits acquis}
65 64
66−L'usage résidentiel multifamilial est autorisé dans la zone où se trouve l'immeuble ; la hauteur et le taux d'implantation du bâtiment sont conformes au règlement d'urbanisme de l'arrondissement en vigueur à la date d'évaluation. L'évaluateur n'a relevé aucune dérogation apparente ; si une composante (par exemple le nombre de cases de stationnement, inférieur au minimum exigé pour une construction neuve) s'avérait dérogatoire, elle serait protégée par les dispositions sur les droits acquis que la Loi sur l'aménagement et l'urbanisme autorise les municipalités à régir \parencite[art.~113, al.~2, par.~18\textsuperscript{o}]{lau}, sous réserve des règles de cessation en cas d'abandon ou d'interruption de l'usage. Nous ne développons pas davantage ce point, traité au \chref{8} : il suffit, pour le présent dossier, de constater que l'usage actuel est l'usage le meilleur et le plus profitable et que rien, dans l'état juridique de l'unité, ne restreint la continuation de cet usage. La conversion en copropriété divise est, en pratique, exclue : elle exige l'autorisation du Tribunal administratif du logement et est interdite dans l'agglomération de Montréal sauf dérogation municipale \parencite[art.~51 à 54.13]{loiTAL}. 65+L'usage résidentiel multifamilial est autorisé dans la zone où se trouve l'immeuble ; la hauteur et le taux d'implantation du bâtiment sont conformes au règlement d'urbanisme de l'arrondissement en vigueur à la date d'évaluation. L'évaluateur n'a relevé aucune dérogation apparente ; si une composante (par exemple le nombre de cases de stationnement, inférieur au minimum exigé pour une construction neuve) s'avérait dérogatoire, elle serait protégée par les dispositions sur les droits acquis que la Loi sur l'aménagement et l'urbanisme autorise les municipalités à régir \parencite[art.~113, al.~2, par.~18\textsuperscript{o}]{lau}, sous réserve des règles de cessation en cas d'abandon ou d'interruption de l'usage. Nous ne développons pas davantage ce point, traité au \cref{ch:08} : il suffit, pour le présent dossier, de constater que l'usage actuel est l'usage le meilleur et le plus profitable et que rien, dans l'état juridique de l'unité, ne restreint la continuation de cet usage. La conversion en copropriété divise est, en pratique, exclue : elle exige l'autorisation du Tribunal administratif du logement et est interdite dans l'agglomération de Montréal sauf dérogation municipale \parencite[art.~51 à 54.13]{loiTAL}.
67 66
68 \subsection{Rôle d'évaluation 2026-2028, taxes et droits de mutation} 67 \subsection{Rôle d'évaluation 2026-2028, taxes et droits de mutation}
69 68
@@ -77,7 +76,7 @@ L'inspection fictive du 25~août 2026 conduit à une cote d'état générale de
77 76
78 \begin{table}[htbp]\centering 77 \begin{table}[htbp]\centering
79 \caption{Composantes du bâtiment, état observé et dépenses en capital retenues (dossier synthétique).}\label{tab:47:inspection} 78 \caption{Composantes du bâtiment, état observé et dépenses en capital retenues (dossier synthétique).}\label{tab:47:inspection}
80−\small\begin{tabular}{@{}L{3.6cm}L{1.6cm}L{4.4cm}S[table-format=6.0]c@{}} 79+\small\begin{tabular}{@{}L{3.5cm}L{1.5cm}L{4.3cm}S[table-format=6.0]c@{}}
81 \toprule 80 \toprule
82 Composante & {Année} & État observé & {CAPEX (\$)} & Exercice \\ 81 Composante & {Année} & État observé & {CAPEX (\$)} & Exercice \\
83 \midrule 82 \midrule
@@ -104,7 +103,7 @@ L'analyse de marché d'un immeuble locatif porte sur trois marchés distincts :
104 103
105 \begin{table}[htbp]\centering 104 \begin{table}[htbp]\centering
106 \caption{Indicateurs de marché retenus à la date d'évaluation (sources publiques, consultées le 31~octobre 2026).}\label{tab:47:marche} 105 \caption{Indicateurs de marché retenus à la date d'évaluation (sources publiques, consultées le 31~octobre 2026).}\label{tab:47:marche}
107−\footnotesize\begin{tabular}{@{}L{5.2cm}L{3.1cm}L{4.5cm}@{}} 106+\footnotesize\begin{tabular}{@{}L{5.1cm}L{3.0cm}L{4.5cm}@{}}
108 \toprule 107 \toprule
109 Indicateur & Valeur & Source et période \\ 108 Indicateur & Valeur & Source et période \\
110 \midrule 109 \midrule
@@ -132,7 +131,7 @@ Sur le marché des logements, le taux d'inoccupation de la région métropolitai
132 131
133 Sur le marché des immeubles, les transactions de petits multilogements restent soutenues : le prix médian des plex de deux à cinq logements atteint \dollars{950000} dans le secteur Rosemont au troisième trimestre 2026 (+\pct{2} sur un an) \parencite{apciqbaromontreal2026}, indicateur de la vigueur de la demande des petits investisseurs et des acheteurs-occupants, même si les plex ne sont pas des comparables directs d'un douze logements. Pour les immeubles d'appartements, CBRE situe les taux de capitalisation du multirésidentiel de faible hauteur de catégorie B à Montréal entre \pct{4.25} et \pct{4.75} au troisième trimestre 2026, en légère hausse \parencite{cbrecaprates2026q3} ; l'écart avec l'obligation du Canada à dix ans (\pct{3.84}) n'est que d'une soixantaine de points de base pour le haut de la fourchette, ce qui illustre la compression persistante des taux résidentiels par rapport aux autres catégories d'actifs, phénomène documenté par \textcite{chervachidzeWheaton2013} et \textcite{ducaLing2020} dans le cycle précédent. 132 Sur le marché des immeubles, les transactions de petits multilogements restent soutenues : le prix médian des plex de deux à cinq logements atteint \dollars{950000} dans le secteur Rosemont au troisième trimestre 2026 (+\pct{2} sur un an) \parencite{apciqbaromontreal2026}, indicateur de la vigueur de la demande des petits investisseurs et des acheteurs-occupants, même si les plex ne sont pas des comparables directs d'un douze logements. Pour les immeubles d'appartements, CBRE situe les taux de capitalisation du multirésidentiel de faible hauteur de catégorie B à Montréal entre \pct{4.25} et \pct{4.75} au troisième trimestre 2026, en légère hausse \parencite{cbrecaprates2026q3} ; l'écart avec l'obligation du Canada à dix ans (\pct{3.84}) n'est que d'une soixantaine de points de base pour le haut de la fourchette, ce qui illustre la compression persistante des taux résidentiels par rapport aux autres catégories d'actifs, phénomène documenté par \textcite{chervachidzeWheaton2013} et \textcite{ducaLing2020} dans le cycle précédent.
134 133
135−Sur le marché des capitaux, la Banque du Canada a maintenu son taux directeur à \pct{2.25} pendant toute la période précédant la date d'évaluation, puis l'a relevé à \pct{2.50} le 28~octobre 2026, première hausse depuis juillet 2023 \parencite{bdc20261028}. Le taux hypothécaire conventionnel à cinq ans affiché par les banques est de \pct{6.19}, mais les taux effectivement consentis sur les prêts assurés à taux fixe de cinq ans et plus s'établissaient à \pct{4.12} en août 2026 \parencite{bdctauxhypothecaires2026} ; c'est ce dernier taux, représentatif d'un prêt multilogement assuré par la SCHL, que le dossier retient pour l'analyse de financement. On note dès maintenant une tension centrale du dossier : un taux hypothécaire de \pct{4.12} amorti sur quarante ans correspond à une constante hypothécaire d'environ \pct{5.08}, supérieure au taux de capitalisation de \pct{4.42} que nous extrairons du marché ; le levier financier est donc négatif sur le revenu courant, ce que la \cref{sec:47:financement} mesure et dont le \chref{31} a exposé la théorie. 134+Sur le marché des capitaux, la Banque du Canada a maintenu son taux directeur à \pct{2.25} pendant toute la période précédant la date d'évaluation, puis l'a relevé à \pct{2.50} le 28~octobre 2026, première hausse depuis juillet 2023 \parencite{bdc20261028}. Le taux hypothécaire conventionnel à cinq ans affiché par les banques est de \pct{6.19}, mais les taux effectivement consentis sur les prêts assurés à taux fixe de cinq ans et plus s'établissaient à \pct{4.12} en août 2026 \parencite{bdctauxhypothecaires2026} ; c'est ce dernier taux, représentatif d'un prêt multilogement assuré par la SCHL, que le dossier retient pour l'analyse de financement. On note dès maintenant une tension centrale du dossier : un taux hypothécaire de \pct{4.12} amorti sur quarante ans correspond à une constante hypothécaire d'environ \pct{5.08}, supérieure au taux de capitalisation de \pct{4.42} que nous extrairons du marché ; le levier financier est donc négatif sur le revenu courant, ce que la \cref{sec:47:financement} mesure et dont le \cref{ch:31} a exposé la théorie.
136 135
137 \section{Analyse de l'état locatif}\label{sec:47:etat-locatif} 136 \section{Analyse de l'état locatif}\label{sec:47:etat-locatif}
138 137
@@ -140,9 +139,9 @@ L'état locatif est la pièce maîtresse du dossier : il fixe le revenu que l'ac
140 139
141 \begin{table}[htbp]\centering 140 \begin{table}[htbp]\centering
142 \caption{État locatif au 1\textsuperscript{er} septembre 2026 : loyer contractuel, loyer de marché estimé et écart (données synthétiques).}\label{tab:47:etat-locatif} 141 \caption{État locatif au 1\textsuperscript{er} septembre 2026 : loyer contractuel, loyer de marché estimé et écart (données synthétiques).}\label{tab:47:etat-locatif}
143−\small\begin{tabular}{@{}l c S[table-format=2.0] S[table-format=4.0] S[table-format=4.0] S[table-format=3.0] S[table-format=2.1] c S[table-format=2.0] c@{}} 142+\footnotesize\setlength{\tabcolsep}{3.5pt}\begin{tabular}{@{}l c S[table-format=3.0] S[table-format=5.0] S[table-format=5.0] S[table-format=4.0] S[table-format=2.1] c S[table-format=2.1] c@{}}
144 \toprule 143 \toprule
145−Log. & Type & {m\textsuperscript{2}} & {Loyer contrat (\$/mois)} & {Loyer marché (\$/mois)} & {Écart (\$)} & {Écart (\%)} & Bail depuis & {Ancienneté (ans)} & Stat. \\ 144+Log. & Type & {m\textsuperscript{2}} & {Contrat (\$/mois)} & {Marché (\$/mois)} & {Écart (\$)} & {Écart (\%)} & Bail depuis & {Anc. (ans)} & Stat. \\
146 \midrule 145 \midrule
147 101 & 3½ & 55 & 917 & 1015 & 98 & 9.7 & 2021 & 5 & oui \\ 146 101 & 3½ & 55 & 917 & 1015 & 98 & 9.7 & 2021 & 5 & oui \\
148 102 & 3½ & 52 & 788 & 1004 & 216 & 21.5 & 2014 & 12 & oui \\ 147 102 & 3½ & 52 & 788 & 1004 & 216 & 21.5 & 2014 & 12 & oui \\
@@ -157,15 +156,16 @@ Log. & Type & {m\textsuperscript{2}} & {Loyer contrat (\$/mois)} & {Loyer march
157 303 & 5½ & 96 & 1526 & 1622 & 96 & 5.9 & 2023 & 3 & oui \\ 156 303 & 5½ & 96 & 1526 & 1622 & 96 & 5.9 & 2023 & 3 & oui \\
158 304 & 5½ & 93 & 1616 & 1683 & 67 & 4.0 & 2024 & 2 & non \\ 157 304 & 5½ & 93 & 1616 & 1683 & 67 & 4.0 & 2024 & 2 & non \\
159 \midrule 158 \midrule
160−Total & & 825 & 13795 & 15503 & 1708 & 11.0 & & {6,3 (moy.)} & 6 \\ 159+Total & & 825 & 13795 & 15503 & 1708 & 11.0 & & 6.3 & 6 \\
161 \bottomrule 160 \bottomrule
162 \end{tabular} 161 \end{tabular}
163−\tabsource{\synth{\texttt{cas3\_montreal\_etat\_locatif.csv} ; échéance de tous les baux : 2027-06-30 ; chauffage non inclus ; script \texttt{ch47\_dossier.py}.}} 162+\tabsource{\synth{\texttt{cas3\_montreal\_etat\_locatif.csv} ; échéance de tous les baux : 2027-06-30 ; chauffage non inclus ; ligne « Total » : ancienneté moyenne 6,3 ans ; script \texttt{ch47\_dossier.py}.}}
164 \end{table} 163 \end{table}
165 164
166 \begin{figure}[htbp]\centering 165 \begin{figure}[htbp]\centering
167−\includegraphics{ch47/ch47-fig-etat-locatif.pdf} 166+\includegraphics[width=\linewidth]{ch47/ch47-fig-etat-locatif.pdf}
168 \caption{Loyer contractuel et loyer de marché estimé pour chacun des douze logements ; l'écart relatif, inscrit au-dessus de chaque paire, croît avec l'ancienneté du bail et atteint \pct{30.5} pour le logement 103 occupé depuis 2008.} 167 \caption{Loyer contractuel et loyer de marché estimé pour chacun des douze logements ; l'écart relatif, inscrit au-dessus de chaque paire, croît avec l'ancienneté du bail et atteint \pct{30.5} pour le logement 103 occupé depuis 2008.}
168+\label{fig:47:etat-locatif}
169 \figsource{\synth{script \texttt{analysis/python/figures/ch47\_dossier\_figures.py}, graine 20261031.}} 169 \figsource{\synth{script \texttt{analysis/python/figures/ch47\_dossier\_figures.py}, graine 20261031.}}
170 \end{figure} 170 \end{figure}
171 171
@@ -185,7 +185,7 @@ Quatrièmement, la vente de l'immeuble ne modifie rien aux baux \parencite[art.~
185 185
186 \subsection{Vacance économique} 186 \subsection{Vacance économique}
187 187
188−Le taux d'inoccupation de la SCHL mesure la part des logements vacants à une date donnée ; il ne mesure ni les pertes de loyer entre deux baux, ni les mauvaises créances, ni les concessions. L'évaluateur retient une provision combinée pour inoccupation et mauvaises créances de \pct{3.9} du revenu brut potentiel contractuel. Ce taux résulte du rapprochement de trois éléments : le taux d'inoccupation de \pct{2.9} de la région métropolitaine en octobre 2025 et de \pct{2.2} pour les immeubles de six à dix-neuf logements de l'île \parencite{schlLocatif2025,schlrmltables2025} ; la détente anticipée vers \pct{3.8} en 2026 et \pct{4.5} en 2027 \parencite{schlpmh2026ete} ; et une provision d'environ un point pour les mauvaises créances et les pertes à la rotation, qui, avec deux logements relocalisés par an et un mois perdu à chaque fois, représente à elle seule près de \pct{1.4} du revenu. Aucune statistique officielle ne mesure la vacance économique des petits immeubles québécois ; le \chref{27} a discuté les ordres de grandeur et leur incertitude, et le test de résistance fait varier ce taux de \pct{2.4} à \pct{6.3} au centile 5 et 95. 188+Le taux d'inoccupation de la SCHL mesure la part des logements vacants à une date donnée ; il ne mesure ni les pertes de loyer entre deux baux, ni les mauvaises créances, ni les concessions. L'évaluateur retient une provision combinée pour inoccupation et mauvaises créances de \pct{3.9} du revenu brut potentiel contractuel. Ce taux résulte du rapprochement de trois éléments : le taux d'inoccupation de \pct{2.9} de la région métropolitaine en octobre 2025 et de \pct{2.2} pour les immeubles de six à dix-neuf logements de l'île \parencite{schlLocatif2025,schlrmltables2025} ; la détente anticipée vers \pct{3.8} en 2026 et \pct{4.5} en 2027 \parencite{schlpmh2026ete} ; et une provision d'environ un point pour les mauvaises créances et les pertes à la rotation, qui, avec deux logements relocalisés par an et un mois perdu à chaque fois, représente à elle seule près de \pct{1.4} du revenu. Aucune statistique officielle ne mesure la vacance économique des petits immeubles québécois ; le \cref{ch:27} a discuté les ordres de grandeur et leur incertitude, et le test de résistance fait varier ce taux de \pct{2.4} à \pct{6.3} au centile 5 et 95.
189 189
190 \section{Revenus}\label{sec:47:revenus} 190 \section{Revenus}\label{sec:47:revenus}
191 191
@@ -257,7 +257,7 @@ La gestion est imputée à \pct{4} du revenu brut effectif, soit \dollars{6737.7
257 257
258 \begin{table}[htbp]\centering 258 \begin{table}[htbp]\centering
259 \caption{Normalisation des dépenses : base 2025, règle appliquée et montant de première année (données synthétiques).}\label{tab:47:depenses-normalisees} 259 \caption{Normalisation des dépenses : base 2025, règle appliquée et montant de première année (données synthétiques).}\label{tab:47:depenses-normalisees}
260−\small\begin{tabular}{@{}L{4.1cm}S[table-format=5.0]L{4.3cm}S[table-format=5.2]S[table-format=2.1]@{}} 260+\small\setlength{\tabcolsep}{4pt}\begin{tabular}{@{}L{4.0cm}S[table-format=5.0]L{4.1cm}S[table-format=5.2]S[table-format=2.1]@{}}
261 \toprule 261 \toprule
262 Poste & {2025 (\$)} & Règle de normalisation & {Normalisé (\$)} & {\% du RBE} \\ 262 Poste & {2025 (\$)} & Règle de normalisation & {Normalisé (\$)} & {\% du RBE} \\
263 \midrule 263 \midrule
@@ -284,8 +284,9 @@ Dépenses totales & 70693 & & 78488.19 & 46.6 \\
284 \end{table} 284 \end{table}
285 285
286 \begin{figure}[htbp]\centering 286 \begin{figure}[htbp]\centering
287−\includegraphics{ch47/ch47-fig-depenses.pdf} 287+\includegraphics[width=\linewidth]{ch47/ch47-fig-depenses.pdf}
288 \caption{Structure des dépenses normalisées de première année, par poste et par nature (fixes, variables et gestion imputée, réserve) ; le trait vertical indique le montant historique 2025 de chaque poste, ce qui fait ressortir la baisse des taxes recalculées au taux général et l'ajout de la gestion imputée.} 288 \caption{Structure des dépenses normalisées de première année, par poste et par nature (fixes, variables et gestion imputée, réserve) ; le trait vertical indique le montant historique 2025 de chaque poste, ce qui fait ressortir la baisse des taxes recalculées au taux général et l'ajout de la gestion imputée.}
289+\label{fig:47:depenses}
289 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 290 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
290 \end{figure} 291 \end{figure}
291 292
@@ -311,13 +312,13 @@ La capitalisation directe convertit le revenu net de première année en valeur
311 \begin{equation}\label{eq:47:extraction} 312 \begin{equation}\label{eq:47:extraction}
312 R_j = \frac{\NOI_{1,j}}{P_j}, \qquad j = 1, \dots, n, 313 R_j = \frac{\NOI_{1,j}}{P_j}, \qquad j = 1, \dots, n,
313 \end{equation} 314 \end{equation}
314−où $P_j$ est le prix de vente du comparable $j$ et $\NOI_{1,j}$ son revenu net de première année, normalisé par l'évaluateur selon les mêmes conventions que pour le sujet. Le fichier synthétique de comparables contient quarante ventes d'immeubles de 6 à 30 logements conclues entre septembre 2024 et août 2026 dans cinq quartiers de l'île (Rosemont, Villeray, Hochelaga, Plateau, Ahuntsic), avec pour chacune le revenu brut potentiel, le ratio de dépenses, le revenu net, le prix, le taux global, le prix par logement et le multiplicateur du revenu brut. La fenêtre de vingt-quatre mois et la tranche de 6 à 30 logements retenues par la fonction \texttt{taux\_extraits} conservent les quarante ventes. Le \cref{tab:47:comparables-taux} présente les dix ventes les plus proches du sujet, sélectionnées par la distance d'attributs du \chref{16} (nombre de logements, année de construction, ancienneté de la vente et état, avec un poids double pour la taille) ; le \cref{tab:47:stats-taux} donne les statistiques de l'ensemble et la \cref{fig:47:taux-extraits} la dispersion complète. 315+où $P_j$ est le prix de vente du comparable $j$ et $\NOI_{1,j}$ son revenu net de première année, normalisé par l'évaluateur selon les mêmes conventions que pour le sujet. Le fichier synthétique de comparables contient quarante ventes d'immeubles de 6 à 30 logements conclues entre septembre 2024 et août 2026 dans cinq quartiers de l'île (Rosemont, Villeray, Hochelaga, Plateau, Ahuntsic), avec pour chacune le revenu brut potentiel, le ratio de dépenses, le revenu net, le prix, le taux global, le prix par logement et le multiplicateur du revenu brut. La fenêtre de vingt-quatre mois et la tranche de 6 à 30 logements retenues par la fonction \texttt{taux\_extraits} conservent les quarante ventes. Le \cref{tab:47:comparables-taux} présente les dix ventes les plus proches du sujet, sélectionnées par la distance d'attributs du \cref{ch:16} (nombre de logements, année de construction, ancienneté de la vente et état, avec un poids double pour la taille) ; le \cref{tab:47:stats-taux} donne les statistiques de l'ensemble et la \cref{fig:47:taux-extraits} la dispersion complète.
315 316
316 \begin{table}[htbp]\centering 317 \begin{table}[htbp]\centering
317 \caption{Les dix ventes comparables les plus proches du sujet (sur quarante), classées par distance d'attributs (données synthétiques).}\label{tab:47:comparables-taux} 318 \caption{Les dix ventes comparables les plus proches du sujet (sur quarante), classées par distance d'attributs (données synthétiques).}\label{tab:47:comparables-taux}
318−\footnotesize\begin{tabular}{@{}l l l S[table-format=2.0] S[table-format=4.0] c S[table-format=6.0] S[table-format=7.0] S[table-format=1.2] S[table-format=6.0] S[table-format=2.2]@{}} 319+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}l l l S[table-format=2.0] S[table-format=4.0] c S[table-format=6.0] S[table-format=7.0] S[table-format=1.2] S[table-format=6.0] S[table-format=2.2]@{}}
319 \toprule 320 \toprule
320−Vente & Date & Quartier & {Log.} & {Constr.} & État & {RNE (\$)} & {Prix (\$)} & {Taux (\%)} & {Prix/log. (\$)} & {MRB} \\ 321+Vente & Date & Quartier & {Log.} & {Constr.} & État & {RNE (\$)} & {Prix (\$)} & {Taux (\%)} & {\$/log.} & {MRB} \\
321 \midrule 322 \midrule
322 M040 & 2026-08-09 & Villeray & 12 & 1954 & 3 & 111423 & 2518000 & 4.43 & 209833 & 11.87 \\ 323 M040 & 2026-08-09 & Villeray & 12 & 1954 & 3 & 111423 & 2518000 & 4.43 & 209833 & 11.87 \\
323 M035 & 2026-04-30 & Plateau & 13 & 1947 & 3 & 98988 & 2354000 & 4.21 & 181077 & 13.43 \\ 324 M035 & 2026-04-30 & Plateau & 13 & 1947 & 3 & 98988 & 2354000 & 4.21 & 181077 & 13.43 \\
@@ -336,7 +337,7 @@ M037 & 2026-07-28 & Hochelaga & 16 & 1942 & 3 & 157028 & 3923000 & 4.00 & 245188
336 337
337 \begin{table}[htbp]\centering 338 \begin{table}[htbp]\centering
338 \caption{Statistiques des taux globaux extraits des quarante ventes et indications de valeur correspondantes pour le sujet (données synthétiques).}\label{tab:47:stats-taux} 339 \caption{Statistiques des taux globaux extraits des quarante ventes et indications de valeur correspondantes pour le sujet (données synthétiques).}\label{tab:47:stats-taux}
339−\small\begin{tabular}{@{}L{6.4cm}S[table-format=1.3]S[table-format=7.0]@{}} 340+\small\begin{tabular}{@{}L{6.4cm}S[table-format=-1.3]S[table-format=7.0]@{}}
340 \toprule 341 \toprule
341 Statistique (n = 40) & {Taux (\%)} & {$\NOI_1 / R$ (\$)} \\ 342 Statistique (n = 40) & {Taux (\%)} & {$\NOI_1 / R$ (\$)} \\
342 \midrule 343 \midrule
@@ -361,8 +362,9 @@ Régression : constante (état 3, 12 logements) & 4.567 & 1969694 \\
361 \end{table} 362 \end{table}
362 363
363 \begin{figure}[htbp]\centering 364 \begin{figure}[htbp]\centering
364−\includegraphics{ch47/ch47-fig-taux-extraits.pdf} 365+\includegraphics[width=\linewidth]{ch47/ch47-fig-taux-extraits.pdf}
365 \caption{Taux globaux extraits des quarante ventes selon le nombre de logements et l'état ; la bande grise est l'intervalle interquartile, le trait pointillé la médiane retenue pour le sujet, repéré par le losange à douze logements. Les grands immeubles du Plateau et d'Ahuntsic tirent le bas de la distribution.} 366 \caption{Taux globaux extraits des quarante ventes selon le nombre de logements et l'état ; la bande grise est l'intervalle interquartile, le trait pointillé la médiane retenue pour le sujet, repéré par le losange à douze logements. Les grands immeubles du Plateau et d'Ahuntsic tirent le bas de la distribution.}
367+\label{fig:47:taux-extraits}
366 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 368 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
367 \end{figure} 369 \end{figure}
368 370
@@ -370,13 +372,13 @@ Régression : constante (état 3, 12 logements) & 4.567 & 1969694 \\
370 372
371 La distribution des taux est resserrée : la moitié centrale des ventes se situe entre \pct{4.152} et \pct{4.638}, l'écart-type est de 41~points de base et la fourchette entière, de \pct{3.72} à \pct{5.47}, s'inscrit presque exactement dans l'intervalle publié par CBRE pour le multirésidentiel de faible hauteur à Montréal, \pct{4.25} à \pct{4.75} pour la catégorie B, les quelques ventes sous \pct{4} correspondant à de grands immeubles du Plateau qui relèvent plutôt de la catégorie A \parencite{cbrecaprates2026q3}. Trois sous-échantillons éclairent la position du sujet. Les immeubles de 6 à 14 logements se vendent à une médiane de \pct{4.555}, soit 14~points de base au-dessus de l'ensemble, ce que la pente de la régression confirme (environ 2~points de base de taux par logement supplémentaire) : les petits immeubles sont moins liquides et moins recherchés des investisseurs institutionnels. Les ventes des douze derniers mois donnent une médiane de \pct{4.43}, pratiquement égale à celle de l'ensemble : aucune dérive temporelle n'est perceptible, en accord avec la lecture « stable à légèrement en hausse » du marché. Le quartier Rosemont affiche une médiane de \pct{4.59}, mais sur onze ventes dont plusieurs grands immeubles anciens ; l'effet de quartier est confondu avec l'effet de taille et l'évaluateur ne lui accorde pas de poids distinct. Quant à l'état, la pente estimée est faible et de signe négatif (un meilleur état abaisse le taux), mais elle n'est pas statistiquement distinguable de zéro sur quarante observations. 373 La distribution des taux est resserrée : la moitié centrale des ventes se situe entre \pct{4.152} et \pct{4.638}, l'écart-type est de 41~points de base et la fourchette entière, de \pct{3.72} à \pct{5.47}, s'inscrit presque exactement dans l'intervalle publié par CBRE pour le multirésidentiel de faible hauteur à Montréal, \pct{4.25} à \pct{4.75} pour la catégorie B, les quelques ventes sous \pct{4} correspondant à de grands immeubles du Plateau qui relèvent plutôt de la catégorie A \parencite{cbrecaprates2026q3}. Trois sous-échantillons éclairent la position du sujet. Les immeubles de 6 à 14 logements se vendent à une médiane de \pct{4.555}, soit 14~points de base au-dessus de l'ensemble, ce que la pente de la régression confirme (environ 2~points de base de taux par logement supplémentaire) : les petits immeubles sont moins liquides et moins recherchés des investisseurs institutionnels. Les ventes des douze derniers mois donnent une médiane de \pct{4.43}, pratiquement égale à celle de l'ensemble : aucune dérive temporelle n'est perceptible, en accord avec la lecture « stable à légèrement en hausse » du marché. Le quartier Rosemont affiche une médiane de \pct{4.59}, mais sur onze ventes dont plusieurs grands immeubles anciens ; l'effet de quartier est confondu avec l'effet de taille et l'évaluateur ne lui accorde pas de poids distinct. Quant à l'état, la pente estimée est faible et de signe négatif (un meilleur état abaisse le taux), mais elle n'est pas statistiquement distinguable de zéro sur quarante observations.
372 374
373−L'évaluateur retient la médiane de l'ensemble, \pct{4.415}, arrondie à \pct{4.42} dans la conclusion. Ce choix se justifie par la robustesse de la médiane aux valeurs extrêmes, par la cohérence avec la fourchette CBRE et avec la médiane des ventes récentes, et par la position du sujet : un immeuble de douze logements, d'état moyen, à Rosemont, dont les loyers sont sous le marché. Ce dernier trait mérite réflexion. Un acheteur qui anticipe un rattrapage des loyers accepte un taux de première année plus bas qu'il ne le ferait pour un immeuble déjà au marché, puisque la croissance attendue du revenu est plus forte ; la relation $R \approx r - g$ du modèle de \textcite{gordonShapiro1956}, dérivée au \chref{29}, l'exprime directement. Les comparables synthétiques ont été générés avec des loyers en place eux aussi décotés selon l'ancienneté, de sorte que leur taux global intègre déjà, en moyenne, une attente de rattrapage comparable ; appliquer la médiane au sujet revient à lui prêter un potentiel de croissance moyen, ce qui est prudent compte tenu de l'écart de \pct{11} au marché. L'évaluateur aurait pu, à l'inverse, retenir un taux légèrement supérieur pour la petite taille (vers \pct{4.55}) ; il s'en abstient parce que la méthode des flux actualisés, à la section suivante, rend explicite la trajectoire des loyers et offre un contrepoids à ce jugement. 375+L'évaluateur retient la médiane de l'ensemble, \pct{4.415}, arrondie à \pct{4.42} dans la conclusion. Ce choix se justifie par la robustesse de la médiane aux valeurs extrêmes, par la cohérence avec la fourchette CBRE et avec la médiane des ventes récentes, et par la position du sujet : un immeuble de douze logements, d'état moyen, à Rosemont, dont les loyers sont sous le marché. Ce dernier trait mérite réflexion. Un acheteur qui anticipe un rattrapage des loyers accepte un taux de première année plus bas qu'il ne le ferait pour un immeuble déjà au marché, puisque la croissance attendue du revenu est plus forte ; la relation $R \approx r - g$ du modèle de \textcite{gordonShapiro1956}, dérivée au \cref{ch:29}, l'exprime directement. Les comparables synthétiques ont été générés avec des loyers en place eux aussi décotés selon l'ancienneté, de sorte que leur taux global intègre déjà, en moyenne, une attente de rattrapage comparable ; appliquer la médiane au sujet revient à lui prêter un potentiel de croissance moyen, ce qui est prudent compte tenu de l'écart de \pct{11} au marché. L'évaluateur aurait pu, à l'inverse, retenir un taux légèrement supérieur pour la petite taille (vers \pct{4.55}) ; il s'en abstient parce que la méthode des flux actualisés, à la section suivante, rend explicite la trajectoire des loyers et offre un contrepoids à ce jugement.
374 376
375 L'indication par capitalisation directe est 377 L'indication par capitalisation directe est
376 \begin{equation}\label{eq:47:vcap} 378 \begin{equation}\label{eq:47:vcap}
377 \hat V_{\text{cap}} = \frac{\NOI_1}{R} = \frac{\dollars{89955.75}}{\num{0.04415}} = \dollars{2037502.89}, 379 \hat V_{\text{cap}} = \frac{\NOI_1}{R} = \frac{\dollars{89955.75}}{\num{0.04415}} = \dollars{2037502.89},
378 \end{equation} 380 \end{equation}
379−soit \dollars{169792} par logement et un multiplicateur de \num{12.31} fois le revenu brut potentiel contractuel. La sensibilité au taux est immédiate : à \pct{4.165} l'indication passe à \dollars{2159802}, à \pct{4.665} à \dollars{1928312}, soit une variation d'environ \pct{5.7} pour 25~points de base ; les bornes interquartiles donnent \dollars{2166303} et \dollars{1939747}. Cette plage de plus ou moins \pct{6} autour de la médiane constitue l'incertitude propre à la méthode, indépendamment de celle du revenu net ; le \chref{20} a montré comment combiner les deux. 381+soit \dollars{169792} par logement et un multiplicateur de \num{12.31} fois le revenu brut potentiel contractuel. La sensibilité au taux est immédiate : à \pct{4.165} l'indication passe à \dollars{2159802}, à \pct{4.665} à \dollars{1928312}, soit une variation d'environ \pct{5.7} pour 25~points de base ; les bornes interquartiles donnent \dollars{2166303} et \dollars{1939747}. Cette plage de plus ou moins \pct{6} autour de la médiane constitue l'incertitude propre à la méthode, indépendamment de celle du revenu net ; le \cref{ch:20} a montré comment combiner les deux.
380 382
381 \section{Méthode du revenu : flux actualisés sur dix ans}\label{sec:47:dcf} 383 \section{Méthode du revenu : flux actualisés sur dix ans}\label{sec:47:dcf}
382 384
@@ -418,9 +420,9 @@ Le \cref{tab:47:projection} présente la projection complète et la \cref{fig:47
418 420
419 \begin{table}[htbp]\centering 421 \begin{table}[htbp]\centering
420 \caption{Projection des flux sur dix ans et revenu de l'année 11 servant à la valeur terminale (données synthétiques, \$ courants).}\label{tab:47:projection} 422 \caption{Projection des flux sur dix ans et revenu de l'année 11 servant à la valeur terminale (données synthétiques, \$ courants).}\label{tab:47:projection}
421−\footnotesize\begin{tabular}{@{}c c S[table-format=6.0] S[table-format=6.0] S[table-format=6.0] S[table-format=5.0] S[table-format=4.0] S[table-format=6.0] S[table-format=6.0] S[table-format=6.0]@{}} 423+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}c c S[table-format=6.0] S[table-format=6.0] S[table-format=6.0] S[table-format=5.0] S[table-format=4.0] S[table-format=6.0] S[table-format=6.0] S[table-format=6.0]@{}}
422 \toprule 424 \toprule
423−$t$ & Exercice & {RBP} & {RBP marché} & {RBE} & {Dép. hors rés.} & {Réserve} & {RNE} & {CAPEX} & {Flux avant dette} \\ 425+$t$ & Exerc. & {RBP} & {RBP marché} & {RBE} & {Dép.} & {Rés.} & {RNE} & {CAPEX} & {Flux av. dette} \\
424 \midrule 426 \midrule
425 1 & 2026-27 & 165540 & 186036 & 168444 & 70385 & 8103 & 89956 & 0 & 89956 \\ 427 1 & 2026-27 & 165540 & 186036 & 168444 & 70385 & 8103 & 89956 & 0 & 89956 \\
426 2 & 2027-28 & 177389 & 192547 & 180112 & 72497 & 8103 & 99512 & 85000 & 14512 \\ 428 2 & 2027-28 & 177389 & 192547 & 180112 & 72497 & 8103 & 99512 & 85000 & 14512 \\
@@ -433,15 +435,16 @@ $t$ & Exercice & {RBP} & {RBP marché} & {RBE} & {Dép. hors rés.} & {Réserve}
433 9 & 2034-35 & 242513 & 244974 & 244912 & 89162 & 8103 & 147647 & 0 & 147647 \\ 435 9 & 2034-35 & 242513 & 244974 & 244912 & 89162 & 8103 & 147647 & 0 & 147647 \\
434 10 & 2035-36 & 251101 & 253548 & 253521 & 91837 & 8103 & 153581 & 0 & 153581 \\ 436 10 & 2035-36 & 251101 & 253548 & 253521 & 91837 & 8103 & 153581 & 0 & 153581 \\
435 \midrule 437 \midrule
436−11 & 2036-37 & 259911 & 262422 & 262353 & 94592 & 8103 & 159658 & {—} & {(valeur terminale)} \\ 438+11 & 2036-37 & 259911 & 262422 & 262353 & 94592 & 8103 & 159658 & {—} & {(val. term.)} \\
437 \bottomrule 439 \bottomrule
438 \end{tabular} 440 \end{tabular}
439 \tabsource{\synth{\texttt{traite/cas3.py}, fonction \texttt{projection\_dcf} ; montants arrondis au dollar, calculs non arrondis.}} 441 \tabsource{\synth{\texttt{traite/cas3.py}, fonction \texttt{projection\_dcf} ; montants arrondis au dollar, calculs non arrondis.}}
440 \end{table} 442 \end{table}
441 443
442 \begin{figure}[htbp]\centering 444 \begin{figure}[htbp]\centering
443−\includegraphics{ch47/ch47-fig-flux-dcf.pdf} 445+\includegraphics[width=\linewidth]{ch47/ch47-fig-flux-dcf.pdf}
444 \caption{Revenu net, dépenses en capital et flux avant dette de la projection sur dix ans ; la ligne tiretée est le service annuel de la dette du prêt dimensionné au ratio de couverture de 1,20. Les années de travaux (toiture en 2027, balcons en 2029, cuisines en 2032) font passer le flux avant dette sous le service de la dette.} 446 \caption{Revenu net, dépenses en capital et flux avant dette de la projection sur dix ans ; la ligne tiretée est le service annuel de la dette du prêt dimensionné au ratio de couverture de 1,20. Les années de travaux (toiture en 2027, balcons en 2029, cuisines en 2032) font passer le flux avant dette sous le service de la dette.}
447+\label{fig:47:flux-dcf}
445 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 448 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
446 \end{figure} 449 \end{figure}
447 450
@@ -463,7 +466,7 @@ La méthode de comparaison appliquée à un immeuble de douze logements ne compa
463 466
464 \subsection{Extraction des ajustements} 467 \subsection{Extraction des ajustements}
465 468
466−Faute de paires de ventes appariées, les ajustements sont extraits par une régression auxiliaire du logarithme du prix par logement sur l'état, la taille, le niveau de loyer, l'ancienneté de la vente et le quartier, estimée sur les quarante ventes ($R^2 = \num{0.635}$, écart-type résiduel \pct{10.9}). Trois coefficients sont exploitables : le niveau de loyer moyen, avec une élasticité de \num{1.17} (erreur-type \num{0.25}), ce qui signifie qu'un immeuble dont les loyers sont \pct{10} plus élevés se vend environ \pct{12} plus cher par logement ; la taille, avec un effet de \pct{0.79} par logement supplémentaire (erreur-type \pct{0.26}), qui traduit la prime des grands immeubles ; et le quartier, le Plateau (+\pct{17.5}) et Ahuntsic (+\pct{10.4}) se vendant plus cher que Rosemont, Hochelaga (+\pct{3.5}) et Villeray (+\pct{0.7}) étant statistiquement indiscernables de Rosemont. L'effet temporel estimé est négatif et non significatif ($-\pct{2.8}$ par an, erreur-type \pct{3.1}) : conformément à la lecture d'un marché stable (indice Teranet--Banque Nationale de Montréal à +\pct{1.4} sur un an en septembre 2026 \parencite{teranet202609}), aucun ajustement de conditions de marché n'est appliqué. L'effet de l'état n'est pas identifiable sur l'échantillon ; l'évaluateur retient un ajustement déclaré de \pct{3} du prix par point d'état, hypothèse pédagogique cohérente avec la loi de génération du jeu synthétique et avec les ordres de grandeur du \chref{17}. 469+Faute de paires de ventes appariées, les ajustements sont extraits par une régression auxiliaire du logarithme du prix par logement sur l'état, la taille, le niveau de loyer, l'ancienneté de la vente et le quartier, estimée sur les quarante ventes ($R^2 = \num{0.635}$, écart-type résiduel \pct{10.9}). Trois coefficients sont exploitables : le niveau de loyer moyen, avec une élasticité de \num{1.17} (erreur-type \num{0.25}), ce qui signifie qu'un immeuble dont les loyers sont \pct{10} plus élevés se vend environ \pct{12} plus cher par logement ; la taille, avec un effet de \pct{0.79} par logement supplémentaire (erreur-type \pct{0.26}), qui traduit la prime des grands immeubles ; et le quartier, le Plateau (+\pct{17.5}) et Ahuntsic (+\pct{10.4}) se vendant plus cher que Rosemont, Hochelaga (+\pct{3.5}) et Villeray (+\pct{0.7}) étant statistiquement indiscernables de Rosemont. L'effet temporel estimé est négatif et non significatif ($-\pct{2.8}$ par an, erreur-type \pct{3.1}) : conformément à la lecture d'un marché stable (indice Teranet--Banque Nationale de Montréal à +\pct{1.4} sur un an en septembre 2026 \parencite{teranet202609}), aucun ajustement de conditions de marché n'est appliqué. L'effet de l'état n'est pas identifiable sur l'échantillon ; l'évaluateur retient un ajustement déclaré de \pct{3} du prix par point d'état, hypothèse pédagogique cohérente avec la loi de génération du jeu synthétique et avec les ordres de grandeur du \cref{ch:17}.
467 470
468 Les ajustements sont appliqués dans l'ordre conventionnel de l'ouvrage : d'abord les ajustements de transaction en pourcentage (ici nuls), puis les ajustements de propriété en dollars par logement, additifs, le signe étant celui du point de vue du sujet : 471 Les ajustements sont appliqués dans l'ordre conventionnel de l'ouvrage : d'abord les ajustements de transaction en pourcentage (ici nuls), puis les ajustements de propriété en dollars par logement, additifs, le signe étant celui du point de vue du sujet :
469 \begin{equation}\label{eq:47:ajuste} 472 \begin{equation}\label{eq:47:ajuste}
@@ -473,9 +476,9 @@ avec $A_{j,\text{loc}} = P_j\,[e^{-\hat\mu_{q(j)}} - 1]$ pour ramener le compara
473 476
474 \begin{table}[htbp]\centering 477 \begin{table}[htbp]\centering
475 \caption{Grille d'ajustement des prix par logement (données synthétiques, \$ par logement).}\label{tab:47:grille} 478 \caption{Grille d'ajustement des prix par logement (données synthétiques, \$ par logement).}\label{tab:47:grille}
476−\footnotesize\begin{tabular}{@{}l S[table-format=6.0] S[table-format=-5.0] S[table-format=-4.0] S[table-format=-4.0] S[table-format=-5.0] S[table-format=6.0] S[table-format=2.1] S[table-format=1.3]@{}} 479+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}l S[table-format=6.0] S[table-format=-5.0] S[table-format=-4.0] S[table-format=-4.0] S[table-format=-5.0] S[table-format=6.0] S[table-format=2.1] S[table-format=1.3]@{}}
477 \toprule 480 \toprule
478−Vente & {Prix/log.} & {Localisation} & {Taille} & {État} & {Loyer} & {Prix ajusté} & {Ajust. brut (\%)} & {Poids} \\ 481+Vente & {Prix/log.} & {Local.} & {Taille} & {État} & {Loyer} & {Prix aj.} & {Brut (\%)} & {Poids} \\
479 \midrule 482 \midrule
480 M040 & 209833 & -1528 & 0 & 0 & -52858 & 155448 & 25.9 & 0.072 \\ 483 M040 & 209833 & -1528 & 0 & 0 & -52858 & 155448 & 25.9 & 0.072 \\
481 M035 & 181077 & -29127 & -1429 & 0 & 4834 & 155355 & 19.5 & 0.095 \\ 484 M035 & 181077 & -29127 & -1429 & 0 & 4834 & 155355 & 19.5 & 0.095 \\
@@ -488,8 +491,8 @@ M023 & 292583 & -28833 & 0 & -8777 & -81442 & 173530 & 40.7 & 0.046 \\
488 M031 & 139333 & -4751 & 3352 & -4180 & -6699 & 127056 & 13.6 & 0.137 \\ 491 M031 & 139333 & -4751 & 3352 & -4180 & -6699 & 127056 & 13.6 & 0.137 \\
489 M037 & 245188 & -8360 & -7650 & 0 & -33636 & 195542 & 20.3 & 0.092 \\ 492 M037 & 245188 & -8360 & -7650 & 0 & -33636 & 195542 & 20.3 & 0.092 \\
490 \midrule 493 \midrule
491−\multicolumn{6}{@{}l}{Prix ajusté pondéré (poids inversement proportionnels à l'ajustement brut)} & 155909 & & 1.000 \\ 494+\multicolumn{6}{@{}l}{Prix ajusté pondéré (poids $\propto 1/$ajustement brut)} & 155909 & & 1.000 \\
492−\multicolumn{6}{@{}l}{Moyenne simple / médiane / minimum / maximum} & {159083 / 155401 / 127056 / 195542} & & \\ 495+\multicolumn{6}{@{}l}{Moyenne simple 159~083 ; médiane 155~401 ; min. 127~056 ; max. 195~542} & & & \\
493 \bottomrule 496 \bottomrule
494 \end{tabular} 497 \end{tabular}
495 \tabsource{\synth{\texttt{traite.comparables} (\texttt{ComparableAjuste}, \texttt{poids\_inverse\_brut}, \texttt{reconciliation}) ; script \texttt{ch47\_dossier.py}.}} 498 \tabsource{\synth{\texttt{traite.comparables} (\texttt{ComparableAjuste}, \texttt{poids\_inverse\_brut}, \texttt{reconciliation}) ; script \texttt{ch47\_dossier.py}.}}
@@ -497,7 +500,7 @@ M037 & 245188 & -8360 & -7650 & 0 & -33636 & 195542 & 20.3 & 0.092 \\
497 500
498 \subsection{Indication par le prix par logement et par le multiplicateur} 501 \subsection{Indication par le prix par logement et par le multiplicateur}
499 502
500−Le \cref{tab:47:grille} montre que les prix ajustés se resserrent entre \dollars{150455} et \dollars{173530} pour huit des dix comparables, l'immeuble de neuf logements d'Hochelaga (M031) tirant vers le bas et le seize logements d'Hochelaga (M037) vers le haut. La pondération inversement proportionnelle à l'ajustement brut, règle usuelle du \chref{20}, donne un prix par logement de \dollars{155909}, l'écart-type pondéré étant de \dollars{17152} (\pct{11}). L'indication est 503+Le \cref{tab:47:grille} montre que les prix ajustés se resserrent entre \dollars{150455} et \dollars{173530} pour huit des dix comparables, l'immeuble de neuf logements d'Hochelaga (M031) tirant vers le bas et le seize logements d'Hochelaga (M037) vers le haut. La pondération inversement proportionnelle à l'ajustement brut, règle usuelle du \cref{ch:20}, donne un prix par logement de \dollars{155909}, l'écart-type pondéré étant de \dollars{17152} (\pct{11}). L'indication est
501 \begin{equation}\label{eq:47:vunite} 504 \begin{equation}\label{eq:47:vunite}
502 \hat V_{\text{unité}} = 12 \times \dollars{155909} = \dollars{1870903}. 505 \hat V_{\text{unité}} = 12 \times \dollars{155909} = \dollars{1870903}.
503 \end{equation} 506 \end{equation}
@@ -511,15 +514,20 @@ L'indication par unité est inférieure de \pct{8.2} à la capitalisation direct
511 514
512 Le prêteur fictif offre un prêt de premier rang assuré, au taux de \pct{4.12} fixe pour un terme de cinq ans, amorti sur quarante ans, sous deux contraintes : un ratio prêt-valeur (RPV, \emph{loan-to-value}) maximal de \pct{75} et un ratio de couverture de la dette (RCD, \emph{debt service coverage ratio}) minimal de \num{1.20}. Les hypothèques canadiennes à taux fixe portent intérêt composé semestriellement\footnote{En vertu de la Loi sur l'intérêt (L.R.C. 1985, c.~I-15, art.~6), qui impose d'exprimer le taux en taux annuel calculé semestriellement ou annuellement ; voir l'annexe des conventions de calcul.}, de sorte que le taux mensuel équivalent et le paiement mensuel constant sur $n = 480$ mois sont 515 Le prêteur fictif offre un prêt de premier rang assuré, au taux de \pct{4.12} fixe pour un terme de cinq ans, amorti sur quarante ans, sous deux contraintes : un ratio prêt-valeur (RPV, \emph{loan-to-value}) maximal de \pct{75} et un ratio de couverture de la dette (RCD, \emph{debt service coverage ratio}) minimal de \num{1.20}. Les hypothèques canadiennes à taux fixe portent intérêt composé semestriellement\footnote{En vertu de la Loi sur l'intérêt (L.R.C. 1985, c.~I-15, art.~6), qui impose d'exprimer le taux en taux annuel calculé semestriellement ou annuellement ; voir l'annexe des conventions de calcul.}, de sorte que le taux mensuel équivalent et le paiement mensuel constant sur $n = 480$ mois sont
513 \begin{equation}\label{eq:47:paiement} 516 \begin{equation}\label{eq:47:paiement}
514−i_m = \left(1 + \frac{i}{2}\right)^{1/6} - 1 = \num{0.0034042}, \qquad PMT = M_0\,\frac{i_m}{1 - (1+i_m)^{-n}}, \qquad f = \frac{12\,PMT}{M_0} = \pct{5.0789}, 517+\begin{split}
518+i_m &= \left(1 + \frac{i}{2}\right)^{1/6} - 1 = \num{0.0034042}, \\
519+PMT &= M_0\,\frac{i_m}{1 - (1+i_m)^{-n}}, \qquad f = \frac{12\,PMT}{M_0} = \pct{5.0789},
520+\end{split}
515 \end{equation} 521 \end{equation}
516 où $f$ est la constante hypothécaire, indépendante du montant emprunté. Le prêt maximal est le plus petit des deux montants autorisés : 522 où $f$ est la constante hypothécaire, indépendante du montant emprunté. Le prêt maximal est le plus petit des deux montants autorisés :
517 \begin{equation}\label{eq:47:pretmax} 523 \begin{equation}\label{eq:47:pretmax}
518−M_{\max} = \min\!\left( \LTV_{\max}\, \hat V,\ \frac{\NOI_1}{\DSCR_{\min}\, f} \right) 524+\begin{split}
519−= \min\!\left( \num{0.75} \times \dollars{2208566},\ \frac{\dollars{89955.75}}{\num{1.20} \times \num{0.050789}} \right) 525+M_{\max} &= \min\!\left( \LTV_{\max}\, \hat V,\ \frac{\NOI_1}{\DSCR_{\min}\, f} \right)
520−= \min(\dollars{1656424},\ \dollars{1475959}). 526+= \min\!\left( \num{0.75} \times \dollars{2208566},\ \frac{\dollars{89955.75}}{\num{1.20} \times \num{0.050789}} \right) \\
527+&= \min(\dollars{1656424},\ \dollars{1475959}) = \dollars{1475959}.
528+\end{split}
521 \end{equation} 529 \end{equation}
522−La contrainte liante est la couverture de la dette : le prêt est plafonné à \dollars{1475959}, soit un ratio prêt-valeur effectif de \pct{66.8} sur l'indication DCF, bien en deçà des \pct{75} offerts. Ce résultat est général en 2026 pour les immeubles dont le taux de capitalisation est inférieur à la constante hypothécaire : la condition $\DSCR \geq 1{,}20$ s'écrit $M_0 / V \leq R / (1{,}20\, f)$, soit ici $\num{0.04415} / (1{,}20 \times \num{0.050789}) = \pct{72.4}$ sur la valeur par capitalisation, et moins encore lorsque la valeur retenue excède la capitalisation du revenu courant. Dans la suite de cette section, le financement est présenté, comme au \chref{31}, sur la base de l'indication DCF afin de conserver des chiffres identiques d'un chapitre à l'autre ; la \cref{sec:47:reconciliation} le recalcule à la valeur finale. Le \cref{tab:47:financement} résume les paramètres. 530+La contrainte liante est la couverture de la dette : le prêt est plafonné à \dollars{1475959}, soit un ratio prêt-valeur effectif de \pct{66.8} sur l'indication DCF, bien en deçà des \pct{75} offerts. Ce résultat est général en 2026 pour les immeubles dont le taux de capitalisation est inférieur à la constante hypothécaire : la condition $\DSCR \geq 1{,}20$ s'écrit $M_0 / V \leq R / (1{,}20\, f)$, soit ici $\num{0.04415} / (1{,}20 \times \num{0.050789}) = \pct{72.4}$ sur la valeur par capitalisation, et moins encore lorsque la valeur retenue excède la capitalisation du revenu courant. Dans la suite de cette section, le financement est présenté, comme au \cref{ch:31}, sur la base de l'indication DCF afin de conserver des chiffres identiques d'un chapitre à l'autre ; la \cref{sec:47:reconciliation} le recalcule à la valeur finale. Le \cref{tab:47:financement} résume les paramètres.
523 531
524 \begin{table}[htbp]\centering 532 \begin{table}[htbp]\centering
525 \caption{Dimensionnement du prêt et indicateurs de levier, sur la base de l'indication DCF (données synthétiques).}\label{tab:47:financement} 533 \caption{Dimensionnement du prêt et indicateurs de levier, sur la base de l'indication DCF (données synthétiques).}\label{tab:47:financement}
@@ -556,7 +564,7 @@ TRI sans levier (achat à la valeur DCF) & {\pct{7.25}} \\
556 564
557 \subsection{Levier, flux après dette et point mort} 565 \subsection{Levier, flux après dette et point mort}
558 566
559−Le service annuel de la dette est de \dollars{74963.13}, dont \dollars{60016} d'intérêts et \dollars{14947} de capital la première année. Le flux après dette de la première année n'est que de \dollars{14992.63}, soit un rendement courant des fonds propres de \pct{2.05} sur une mise de fonds de \dollars{732606} : à ce prix, l'acheteur accepte un rendement courant inférieur à celui d'une obligation d'État et mise entièrement sur la croissance du revenu et sur la plus-value. La raison est le levier négatif sur le revenu courant : la constante hypothécaire de \pct{5.08} dépasse le taux de capitalisation de \pct{4.42}, de sorte que chaque dollar emprunté coûte, en service de la dette, plus qu'il ne rapporte en revenu net. Le \chref{31} a démontré que le rendement courant des fonds propres s'écrit 567+Le service annuel de la dette est de \dollars{74963.13}, dont \dollars{60016} d'intérêts et \dollars{14947} de capital la première année. Le flux après dette de la première année n'est que de \dollars{14992.63}, soit un rendement courant des fonds propres de \pct{2.05} sur une mise de fonds de \dollars{732606} : à ce prix, l'acheteur accepte un rendement courant inférieur à celui d'une obligation d'État et mise entièrement sur la croissance du revenu et sur la plus-value. La raison est le levier négatif sur le revenu courant : la constante hypothécaire de \pct{5.08} dépasse le taux de capitalisation de \pct{4.42}, de sorte que chaque dollar emprunté coûte, en service de la dette, plus qu'il ne rapporte en revenu net. Le \cref{ch:31} a démontré que le rendement courant des fonds propres s'écrit
560 \begin{equation}\label{eq:47:levier} 568 \begin{equation}\label{eq:47:levier}
561 R_E = \frac{\NOI_1 - f M_0}{V - M_0} = R + (R - f)\frac{M_0}{V - M_0}, 569 R_E = \frac{\NOI_1 - f M_0}{V - M_0} = R + (R - f)\frac{M_0}{V - M_0},
562 \end{equation} 570 \end{equation}
@@ -580,9 +588,9 @@ Le terme de cinq ans expose l'emprunteur à un renouvellement en 2031 à un taux
580 588
581 \begin{table}[htbp]\centering 589 \begin{table}[htbp]\centering
582 \caption{Renouvellement du prêt à l'échéance du terme de cinq ans : service de la dette et ratios de couverture selon le taux renouvelé (données synthétiques).}\label{tab:47:scenarios} 590 \caption{Renouvellement du prêt à l'échéance du terme de cinq ans : service de la dette et ratios de couverture selon le taux renouvelé (données synthétiques).}\label{tab:47:scenarios}
583−\small\begin{tabular}{@{}l S[table-format=1.2] S[table-format=6.0] S[table-format=5.0] S[table-format=1.3] S[table-format=1.3] S[table-format=1.3]@{}} 591+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}l S[table-format=1.2] S[table-format=6.0] S[table-format=5.0] S[table-format=1.3] S[table-format=1.3] S[table-format=1.3]@{}}
584 \toprule 592 \toprule
585−Scénario & {Taux (\%)} & {Service de la dette (\$/an)} & {Flux après dette, an 6 (\$)} & {RCD an 6} & {RCD an 8} & {RCD an 10} \\ 593+Scénario & {Taux (\%)} & {Service dette (\$/an)} & {Flux ap. dette an 6 (\$)} & {RCD an 6} & {RCD an 8} & {RCD an 10} \\
586 \midrule 594 \midrule
587 Taux inchangé & 4.12 & 74963 & 55927 & 1.746 & 1.894 & 2.049 \\ 595 Taux inchangé & 4.12 & 74963 & 55927 & 1.746 & 1.894 & 2.049 \\
588 +100 points de base & 5.12 & 85177 & 45713 & 1.537 & 1.667 & 1.803 \\ 596 +100 points de base & 5.12 & 85177 & 45713 & 1.537 & 1.667 & 1.803 \\
@@ -594,8 +602,9 @@ Taux inchangé & 4.12 & 74963 & 55927 & 1.746 & 1.894 & 2.049 \\
594 \end{table} 602 \end{table}
595 603
596 \begin{figure}[htbp]\centering 604 \begin{figure}[htbp]\centering
597−\includegraphics{ch47/ch47-fig-rcd-scenarios.pdf} 605+\includegraphics[width=\linewidth]{ch47/ch47-fig-rcd-scenarios.pdf}
598 \caption{Ratio de couverture de la dette par année de projection : pendant le premier terme, le ratio part du seuil de 1,20 et monte avec le revenu net ; au renouvellement de l'année 6, une hausse de 300~points de base le ramène tout juste au-dessus de 1,20.} 606 \caption{Ratio de couverture de la dette par année de projection : pendant le premier terme, le ratio part du seuil de 1,20 et monte avec le revenu net ; au renouvellement de l'année 6, une hausse de 300~points de base le ramène tout juste au-dessus de 1,20.}
607+\label{fig:47:rcd-scenarios}
599 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 608 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
600 \end{figure} 609 \end{figure}
601 610
@@ -605,14 +614,14 @@ Le programme APH Select de la SCHL, en vigueur depuis mars 2022, module les cond
605 614
606 \begin{table}[htbp]\centering 615 \begin{table}[htbp]\centering
607 \caption{Variante APH Select (50 points : RPV \pct{85}, RCD 1,10) sur la valeur finale de \dollars{2060000}, taux \pct{4.12}, trois amortissements (données synthétiques).}\label{tab:47:aph} 616 \caption{Variante APH Select (50 points : RPV \pct{85}, RCD 1,10) sur la valeur finale de \dollars{2060000}, taux \pct{4.12}, trois amortissements (données synthétiques).}\label{tab:47:aph}
608−\small\begin{tabular}{@{}l S[table-format=7.0] S[table-format=7.0] S[table-format=2.1] S[table-format=5.0] S[table-format=6.0] S[table-format=1.2] S[table-format=6.0]@{}} 617+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}L{3.1cm} S[table-format=7.0] S[table-format=2.1] S[table-format=5.0] S[table-format=6.0] S[table-format=1.2] S[table-format=6.0]@{}}
609 \toprule 618 \toprule
610−Amortissement & {Prêt max. RPV (\$)} & {Prêt max. RCD (\$)} & {RPV effectif (\%)} & {Service dette (\$)} & {Mise de fonds (\$)} & {Prime (\%)} & {Prime (\$)} \\ 619+Formule & {Prêt (\$)} & {RPV (\%)} & {Serv. dette (\$)} & {Mise de fonds (\$)} & {Prime (\%)} & {Prime (\$)} \\
611 \midrule 620 \midrule
612−Conventionnel 40 ans (RPV 75 \%, RCD 1,20) & 1545000 & 1475959 & 71.6 & 74963 & 584041 & {—} & {—} \\ 621+Conventionnel 40 ans (RPV 75 \%, RCD 1,20 ; prêt RPV 1~545~000) & 1475959 & 71.6 & 74963 & 584041 & {—} & {—} \\
613−APH Select 40 ans & 1751000 & 1610138 & 78.2 & 81778 & 449862 & 5.10 & 82117 \\ 622+APH Select 40 ans (prêt RPV 1~751~000) & 1610138 & 78.2 & 81778 & 449862 & 5.10 & 82117 \\
614−APH Select 45 ans & 1751000 & 1682397 & 81.7 & 81778 & 377603 & 6.35 & 106832 \\ 623+APH Select 45 ans & 1682397 & 81.7 & 81778 & 377603 & 6.35 & 106832 \\
615−APH Select 50 ans & 1751000 & 1741328 & 84.5 & 81778 & 318672 & 6.60 & 114928 \\ 624+APH Select 50 ans & 1741328 & 84.5 & 81778 & 318672 & 6.60 & 114928 \\
616 \bottomrule 625 \bottomrule
617 \end{tabular} 626 \end{tabular}
618 \tabsource{\synth{paramètres \textcite{schlaphselect,schlaphprimes} ; calculs \texttt{ch47\_dossier.py}, fonction \texttt{aph\_select} ; admissibilité au programme non vérifiée.}} 627 \tabsource{\synth{paramètres \textcite{schlaphselect,schlaphprimes} ; calculs \texttt{ch47\_dossier.py}, fonction \texttt{aph\_select} ; admissibilité au programme non vérifiée.}}
@@ -622,11 +631,11 @@ APH Select 50 ans & 1751000 & 1741328 & 84.5 & 81778 & 318672 & 6.60 & 114928 \\
622 631
623 \subsection{Lois des intrants} 632 \subsection{Lois des intrants}
624 633
625−L'analyse de sensibilité un facteur à la fois ne dit pas quelle est la probabilité que plusieurs facteurs se dégradent ensemble. La simulation de Monte-Carlo, introduite en évaluation immobilière par \textcite{pyhrr1973} et \textcite{wofford1978} et développée par \textcite{frenchGabrielli2005} et \textcite{hoesliJaniBender2006}, tire conjointement les intrants incertains du modèle de flux dans des lois déclarées, recalcule la valeur pour chaque tirage et décrit la distribution obtenue. Le \chref{40} en a exposé la méthode ; nous l'appliquons au dossier avec \num{10000} tirages et la graine \texttt{20261031}, ce qui rend chaque chiffre reproductible. Le \cref{tab:47:lois} déclare les sept lois. Les choix suivent trois principes : centrer chaque loi sur l'hypothèse de base, pour que la simulation mesure l'incertitude autour du scénario déterministe et non un autre scénario ; donner à chaque loi une dispersion justifiée par les données disponibles ; lier le taux terminal au taux d'actualisation par un écart aléatoire plutôt que de les tirer indépendamment, parce que les deux réagissent aux mêmes mouvements des marchés de capitaux. L'inoccupation suit une loi log-normale de médiane \pct{3.9} et d'écart-type logarithmique \num{0.30}, ce qui place \pct{90} des tirages entre \pct{2.4} et \pct{6.3}, plage qui couvre les observations SCHL de 2023 à 2025 et les prévisions à 2028. La croissance des loyers de marché est normale de moyenne \pct{3.5} et d'écart-type un point, soit un intervalle à \pct{90} de \pct{1.8} à \pct{5.1}. La rotation est triangulaire entre \pct{8} et \pct{25} avec un mode à \pct{15} : la borne basse correspond au taux régional de la SCHL, la borne haute à un immeuble où plusieurs départs se concentrent. La croissance des dépenses est normale (\pct{3.0}, écart-type \num{0.75} point), le taux d'actualisation normal (\pct{7.25}, écart-type 50~points de base) et l'écart terminal $r - R_T$ normal (\num{2.25} points, écart-type 40~points de base), avec un plancher de \pct{3.5} pour le taux terminal. Chaque dépense en capital est multipliée par un facteur log-normal de médiane 1 et d'écart-type logarithmique \num{0.20}, indépendant d'un chantier à l'autre. La croissance des loyers contractuels (\pct{3.1}) est maintenue fixe : c'est un paramètre réglementaire dont la variabilité d'une année à l'autre se compense sur dix ans. 634+L'analyse de sensibilité un facteur à la fois ne dit pas quelle est la probabilité que plusieurs facteurs se dégradent ensemble. La simulation de Monte-Carlo, introduite en évaluation immobilière par \textcite{pyhrr1973} et \textcite{wofford1978} et développée par \textcite{frenchGabrielli2005} et \textcite{hoesliJaniBender2006}, tire conjointement les intrants incertains du modèle de flux dans des lois déclarées, recalcule la valeur pour chaque tirage et décrit la distribution obtenue. Le \cref{ch:40} en a exposé la méthode ; nous l'appliquons au dossier avec \num{10000} tirages et la graine \texttt{20261031}, ce qui rend chaque chiffre reproductible. Le \cref{tab:47:lois} déclare les sept lois. Les choix suivent trois principes : centrer chaque loi sur l'hypothèse de base, pour que la simulation mesure l'incertitude autour du scénario déterministe et non un autre scénario ; donner à chaque loi une dispersion justifiée par les données disponibles ; lier le taux terminal au taux d'actualisation par un écart aléatoire plutôt que de les tirer indépendamment, parce que les deux réagissent aux mêmes mouvements des marchés de capitaux. L'inoccupation suit une loi log-normale de médiane \pct{3.9} et d'écart-type logarithmique \num{0.30}, ce qui place \pct{90} des tirages entre \pct{2.4} et \pct{6.3}, plage qui couvre les observations SCHL de 2023 à 2025 et les prévisions à 2028. La croissance des loyers de marché est normale de moyenne \pct{3.5} et d'écart-type un point, soit un intervalle à \pct{90} de \pct{1.8} à \pct{5.1}. La rotation est triangulaire entre \pct{8} et \pct{25} avec un mode à \pct{15} : la borne basse correspond au taux régional de la SCHL, la borne haute à un immeuble où plusieurs départs se concentrent. La croissance des dépenses est normale (\pct{3.0}, écart-type \num{0.75} point), le taux d'actualisation normal (\pct{7.25}, écart-type 50~points de base) et l'écart terminal $r - R_T$ normal (\num{2.25} points, écart-type 40~points de base), avec un plancher de \pct{3.5} pour le taux terminal. Chaque dépense en capital est multipliée par un facteur log-normal de médiane 1 et d'écart-type logarithmique \num{0.20}, indépendant d'un chantier à l'autre. La croissance des loyers contractuels (\pct{3.1}) est maintenue fixe : c'est un paramètre réglementaire dont la variabilité d'une année à l'autre se compense sur dix ans.
626 635
627 \begin{table}[htbp]\centering 636 \begin{table}[htbp]\centering
628 \caption{Lois déclarées des intrants de la simulation (10~000 tirages, graine 20261031) et quantiles effectivement tirés.}\label{tab:47:lois} 637 \caption{Lois déclarées des intrants de la simulation (10~000 tirages, graine 20261031) et quantiles effectivement tirés.}\label{tab:47:lois}
629−\footnotesize\begin{tabular}{@{}L{3.6cm}L{4.5cm}S[table-format=1.4]S[table-format=1.4]S[table-format=1.4]@{}} 638+\footnotesize\setlength{\tabcolsep}{3pt}\begin{tabular}{@{}L{3.2cm}L{4.1cm}S[table-format=1.4]S[table-format=1.4]S[table-format=1.4]@{}}
630 \toprule 639 \toprule
631 Intrant & Loi (paramètres) & {Moyenne tirée} & {Quantile 5 \%} & {Quantile 95 \%} \\ 640 Intrant & Loi (paramètres) & {Moyenne tirée} & {Quantile 5 \%} & {Quantile 95 \%} \\
632 \midrule 641 \midrule
@@ -654,14 +663,15 @@ le code vectorisé reproduisant exactement, pour les intrants de base, la projec
654 La \cref{fig:47:montecarlo} et le \cref{tab:47:mc-resultats} présentent les résultats. La valeur simulée a une médiane de \dollars{2191608}, très proche de la valeur déterministe de \dollars{2208566}, et une moyenne de \dollars{2233506}, supérieure parce que la valeur est une fonction convexe du taux terminal et du taux d'actualisation ; \textcite{hoesliJaniBender2006} signalent ce biais de convexité, qui justifie de rapporter la médiane plutôt que la moyenne comme résumé central. L'écart-type est de \dollars{350443}, soit un coefficient de variation de \pct{15.7} ; l'intervalle à \pct{90} s'étend de \dollars{1735194} à \dollars{2876902}, c'est-à-dire de $-\pct{21}$ à $+\pct{30}$ autour de la médiane. La probabilité que la valeur simulée soit inférieure à l'indication par capitalisation directe est de \pct{30.9}, et celle qu'elle soit inférieure à deux millions de dollars de \pct{26.5}. Ces résultats placent l'incertitude de la méthode des flux actualisés dans son vrai ordre de grandeur : une plage de plus ou moins \pct{25} n'est pas un défaut du modèle mais la mesure honnête de ce qu'un horizon de dix ans et une valeur terminale à \pct{70} impliquent. 663 La \cref{fig:47:montecarlo} et le \cref{tab:47:mc-resultats} présentent les résultats. La valeur simulée a une médiane de \dollars{2191608}, très proche de la valeur déterministe de \dollars{2208566}, et une moyenne de \dollars{2233506}, supérieure parce que la valeur est une fonction convexe du taux terminal et du taux d'actualisation ; \textcite{hoesliJaniBender2006} signalent ce biais de convexité, qui justifie de rapporter la médiane plutôt que la moyenne comme résumé central. L'écart-type est de \dollars{350443}, soit un coefficient de variation de \pct{15.7} ; l'intervalle à \pct{90} s'étend de \dollars{1735194} à \dollars{2876902}, c'est-à-dire de $-\pct{21}$ à $+\pct{30}$ autour de la médiane. La probabilité que la valeur simulée soit inférieure à l'indication par capitalisation directe est de \pct{30.9}, et celle qu'elle soit inférieure à deux millions de dollars de \pct{26.5}. Ces résultats placent l'incertitude de la méthode des flux actualisés dans son vrai ordre de grandeur : une plage de plus ou moins \pct{25} n'est pas un défaut du modèle mais la mesure honnête de ce qu'un horizon de dix ans et une valeur terminale à \pct{70} impliquent.
655 664
656 \begin{figure}[htbp]\centering 665 \begin{figure}[htbp]\centering
657−\includegraphics{ch47/ch47-fig-montecarlo.pdf} 666+\includegraphics[width=\linewidth]{ch47/ch47-fig-montecarlo.pdf}
658 \caption{Distribution de la valeur sur \num{10000} tirages : quantiles 5~\%, 50~\% et 95~\% (traits tiretés), valeur DCF déterministe et indication par capitalisation directe. La distribution est asymétrique à droite, effet de la convexité de la valeur dans les taux.} 667 \caption{Distribution de la valeur sur \num{10000} tirages : quantiles 5~\%, 50~\% et 95~\% (traits tiretés), valeur DCF déterministe et indication par capitalisation directe. La distribution est asymétrique à droite, effet de la convexité de la valeur dans les taux.}
668+\label{fig:47:montecarlo}
659 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 669 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
660 \end{figure} 670 \end{figure}
661 671
662 \begin{table}[htbp]\centering 672 \begin{table}[htbp]\centering
663 \caption{Résultats de la simulation : valeur, ratios de couverture et corrélations de rang (données synthétiques).}\label{tab:47:mc-resultats} 673 \caption{Résultats de la simulation : valeur, ratios de couverture et corrélations de rang (données synthétiques).}\label{tab:47:mc-resultats}
664−\small\begin{tabular}{@{}L{8.2cm}S[table-format=7.0]@{}} 674+\small\begin{tabular}{@{}L{7.3cm}S[table-format=7.0]@{}}
665 \toprule 675 \toprule
666 Statistique & {Valeur} \\ 676 Statistique & {Valeur} \\
667 \midrule 677 \midrule
@@ -698,8 +708,9 @@ Corrélation de rang avec la valeur : taux terminal & {$-0{,}78$} \\
698 La \cref{fig:47:tornade} fait varier chaque intrant, isolément, entre ses quantiles 10 et 90, les autres restant à leur valeur de base. Le taux d'actualisation domine (valeur de \dollars{1931796} à \dollars{2568330}, amplitude \dollars{636535}), suivi de la croissance des loyers de marché (amplitude \dollars{459632}) et de l'écart terminal (\dollars{320404}) ; la croissance des dépenses (\dollars{185540}), l'inoccupation (\dollars{117049}), le facteur CAPEX (\dollars{92066}) et la rotation (\dollars{90738}) viennent loin derrière. Les corrélations de rang de la simulation conjointe confirment cette hiérarchie : $-0{,}78$ pour le taux terminal, $-0{,}71$ pour le taux d'actualisation, $+0{,}49$ pour la croissance des loyers de marché. La leçon est double. D'une part, l'incertitude de la valeur est d'abord une incertitude sur les taux, c'est-à-dire sur le marché des capitaux et sur la prime de risque exigée, et non sur l'exploitation de l'immeuble : c'est ce que \textcite{sivitanidouSivitanides1999} et \textcite{chervachidzeWheaton2013} ont établi empiriquement pour les taux de capitalisation. D'autre part, la rotation, dont dépend pourtant tout le rattrapage des loyers, pèse peu sur la valeur parce que ses effets s'exercent sur les premières années, fortement actualisées, et que la croissance des loyers de marché gouverne de toute façon le revenu de l'année 11 qui fonde la valeur terminale ; l'écart entre capitalisation et DCF constaté plus haut tient moins à la rotation qu'au couple de taux retenu. 708 La \cref{fig:47:tornade} fait varier chaque intrant, isolément, entre ses quantiles 10 et 90, les autres restant à leur valeur de base. Le taux d'actualisation domine (valeur de \dollars{1931796} à \dollars{2568330}, amplitude \dollars{636535}), suivi de la croissance des loyers de marché (amplitude \dollars{459632}) et de l'écart terminal (\dollars{320404}) ; la croissance des dépenses (\dollars{185540}), l'inoccupation (\dollars{117049}), le facteur CAPEX (\dollars{92066}) et la rotation (\dollars{90738}) viennent loin derrière. Les corrélations de rang de la simulation conjointe confirment cette hiérarchie : $-0{,}78$ pour le taux terminal, $-0{,}71$ pour le taux d'actualisation, $+0{,}49$ pour la croissance des loyers de marché. La leçon est double. D'une part, l'incertitude de la valeur est d'abord une incertitude sur les taux, c'est-à-dire sur le marché des capitaux et sur la prime de risque exigée, et non sur l'exploitation de l'immeuble : c'est ce que \textcite{sivitanidouSivitanides1999} et \textcite{chervachidzeWheaton2013} ont établi empiriquement pour les taux de capitalisation. D'autre part, la rotation, dont dépend pourtant tout le rattrapage des loyers, pèse peu sur la valeur parce que ses effets s'exercent sur les premières années, fortement actualisées, et que la croissance des loyers de marché gouverne de toute façon le revenu de l'année 11 qui fonde la valeur terminale ; l'écart entre capitalisation et DCF constaté plus haut tient moins à la rotation qu'au couple de taux retenu.
699 709
700 \begin{figure}[htbp]\centering 710 \begin{figure}[htbp]\centering
701−\includegraphics{ch47/ch47-fig-tornade.pdf} 711+\includegraphics[width=\linewidth]{ch47/ch47-fig-tornade.pdf}
702 \caption{Diagramme en tornade : valeur DCF lorsque chaque intrant est porté, seul, à son quantile 10~\% puis 90~\%, les autres restant à la base (trait vertical, \dollars{2208566}). Les taux dominent ; la rotation et les dépenses en capital sont des risques de second ordre pour la valeur.} 712 \caption{Diagramme en tornade : valeur DCF lorsque chaque intrant est porté, seul, à son quantile 10~\% puis 90~\%, les autres restant à la base (trait vertical, \dollars{2208566}). Les taux dominent ; la rotation et les dépenses en capital sont des risques de second ordre pour la valeur.}
713+\label{fig:47:tornade}
703 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}} 714 \figsource{\synth{script \texttt{ch47\_dossier\_figures.py}, graine 20261031.}}
704 \end{figure} 715 \end{figure}
705 716
@@ -711,13 +722,13 @@ La lecture du ratio de couverture renverse partiellement la hiérarchie. Le prê
711 722
712 \subsection{Pondération des indications} 723 \subsection{Pondération des indications}
713 724
714−Le dossier a produit trois indications de valeur et deux repères (\cref{tab:47:reconciliation}). La réconciliation, au sens des Normes et du \chref{20}, n'est pas une moyenne : c'est un jugement motivé sur la fiabilité relative de chaque indication compte tenu de la qualité des données, de la pertinence de la méthode pour le type de bien et pour le marché, et du droit évalué. 725+Le dossier a produit trois indications de valeur et deux repères (\cref{tab:47:reconciliation}). La réconciliation, au sens des Normes et du \cref{ch:20}, n'est pas une moyenne : c'est un jugement motivé sur la fiabilité relative de chaque indication compte tenu de la qualité des données, de la pertinence de la méthode pour le type de bien et pour le marché, et du droit évalué.
715 726
716 La capitalisation directe repose sur quarante ventes, sur un revenu net normalisé poste par poste et sur une médiane de taux stable dans le temps et cohérente avec les fourchettes publiées ; elle est la méthode que les acheteurs et les prêteurs de ce segment utilisent effectivement. Sa faiblesse est de résumer en un taux l'anticipation de rattrapage des loyers, sans que l'on sache si le comparable moyen partage le profil du sujet. Elle reçoit le poids le plus élevé, \pct{45}. La méthode des flux actualisés explicite la trajectoire des loyers, les dépenses en capital et la revente ; elle est la seule à intégrer les trois chantiers et le profil des baux. Sa faiblesse est la sensibilité de \pct{70} de sa valeur à un couple de taux terminal et d'actualisation que le marché ne révèle pas directement, et la dépendance à une hypothèse de rotation plus favorable que les données régionales ; la simulation a montré un intervalle à \pct{90} de plus ou moins un quart. Elle reçoit \pct{35}. La comparaison par unité s'appuie sur dix ventes ajustées par des coefficients estimés avec une précision modeste et sur un ajustement de loyer qui, à lui seul, atteint souvent \pct{20} du prix ; elle est la méthode la moins fiable ici, mais elle ancre la valeur dans les prix payés par logement et elle rappelle que le marché ne paie pas la totalité du rattrapage futur. Elle reçoit \pct{20}. Le multiplicateur du revenu brut n'est pas pondéré ; il situe la valeur entre \num{11.5} et \num{14.6} fois le revenu brut, soit entre \dollars{1.90} et \dollars{2.42} millions, plage qui contient les trois indications. 727 La capitalisation directe repose sur quarante ventes, sur un revenu net normalisé poste par poste et sur une médiane de taux stable dans le temps et cohérente avec les fourchettes publiées ; elle est la méthode que les acheteurs et les prêteurs de ce segment utilisent effectivement. Sa faiblesse est de résumer en un taux l'anticipation de rattrapage des loyers, sans que l'on sache si le comparable moyen partage le profil du sujet. Elle reçoit le poids le plus élevé, \pct{45}. La méthode des flux actualisés explicite la trajectoire des loyers, les dépenses en capital et la revente ; elle est la seule à intégrer les trois chantiers et le profil des baux. Sa faiblesse est la sensibilité de \pct{70} de sa valeur à un couple de taux terminal et d'actualisation que le marché ne révèle pas directement, et la dépendance à une hypothèse de rotation plus favorable que les données régionales ; la simulation a montré un intervalle à \pct{90} de plus ou moins un quart. Elle reçoit \pct{35}. La comparaison par unité s'appuie sur dix ventes ajustées par des coefficients estimés avec une précision modeste et sur un ajustement de loyer qui, à lui seul, atteint souvent \pct{20} du prix ; elle est la méthode la moins fiable ici, mais elle ancre la valeur dans les prix payés par logement et elle rappelle que le marché ne paie pas la totalité du rattrapage futur. Elle reçoit \pct{20}. Le multiplicateur du revenu brut n'est pas pondéré ; il situe la valeur entre \num{11.5} et \num{14.6} fois le revenu brut, soit entre \dollars{1.90} et \dollars{2.42} millions, plage qui contient les trois indications.
717 728
718 \begin{table}[htbp]\centering 729 \begin{table}[htbp]\centering
719 \caption{Réconciliation des indications de valeur (données synthétiques).}\label{tab:47:reconciliation} 730 \caption{Réconciliation des indications de valeur (données synthétiques).}\label{tab:47:reconciliation}
720−\small\begin{tabular}{@{}L{6.0cm}S[table-format=7.0]S[table-format=2.0]S[table-format=7.0]@{}} 731+\small\begin{tabular}{@{}L{5.8cm}S[table-format=7.0]S[table-format=3.0]S[table-format=7.0]@{}}
721 \toprule 732 \toprule
722 Indication & {Valeur (\$)} & {Poids (\%)} & {Contribution (\$)} \\ 733 Indication & {Valeur (\$)} & {Poids (\%)} & {Contribution (\$)} \\
723 \midrule 734 \midrule
@@ -749,7 +760,7 @@ est arrondie à \dollars{2060000}, l'arrondi au dix mille près reflétant la pr
749 760
750 \subsection{Valeur, rôle et droits de mutation} 761 \subsection{Valeur, rôle et droits de mutation}
751 762
752−La valeur finale est inférieure de \pct{6.4} à la valeur inscrite au rôle 2026-2028 (\dollars{2200000}), ce qui pourrait surprendre dans un marché où les valeurs au rôle sont réputées en retard sur le marché. Deux raisons l'expliquent et illustrent la différence de base de valeur. D'abord, le rôle évalue l'immeuble comme si le vendeur détenait tous les droits du locataire \parencite[art.~45.1]{lfm}, c'est-à-dire aux loyers de marché : en reprenant le calcul du dossier avec les loyers de marché, le revenu brut effectif serait de \dollars{188141}, le revenu net de \dollars{108865} (\dollars{18909} de plus) et la valeur par capitalisation de \dollars{2465788} ; la valeur au rôle, à \pct{89} de ce chiffre, est alors cohérente avec un marché qui a progressé depuis le 1\textsuperscript{er} juillet 2024. Ensuite, la date de référence du rôle précède de vingt-six mois la date d'évaluation ; l'écart entre les deux dates se mesure par les indices et non par comparaison directe. L'évaluateur ne doit donc ni « corriger » sa valeur pour la rapprocher du rôle, ni conclure que le rôle est erroné : les deux chiffres répondent à deux questions différentes, et l'écart de \pct{6} s'explique entièrement par le droit évalué. Un propriétaire qui jugerait sa valeur au rôle excessive disposerait du recours en révision du \chref{9}, mais l'argument des baux à loyer bas lui serait fermé par l'article~45.1. 763+La valeur finale est inférieure de \pct{6.4} à la valeur inscrite au rôle 2026-2028 (\dollars{2200000}), ce qui pourrait surprendre dans un marché où les valeurs au rôle sont réputées en retard sur le marché. Deux raisons l'expliquent et illustrent la différence de base de valeur. D'abord, le rôle évalue l'immeuble comme si le vendeur détenait tous les droits du locataire \parencite[art.~45.1]{lfm}, c'est-à-dire aux loyers de marché : en reprenant le calcul du dossier avec les loyers de marché, le revenu brut effectif serait de \dollars{188141}, le revenu net de \dollars{108865} (\dollars{18909} de plus) et la valeur par capitalisation de \dollars{2465788} ; la valeur au rôle, à \pct{89} de ce chiffre, est alors cohérente avec un marché qui a progressé depuis le 1\textsuperscript{er} juillet 2024. Ensuite, la date de référence du rôle précède de vingt-six mois la date d'évaluation ; l'écart entre les deux dates se mesure par les indices et non par comparaison directe. L'évaluateur ne doit donc ni « corriger » sa valeur pour la rapprocher du rôle, ni conclure que le rôle est erroné : les deux chiffres répondent à deux questions différentes, et l'écart de \pct{6} s'explique entièrement par le droit évalué. Un propriétaire qui jugerait sa valeur au rôle excessive disposerait du recours en révision du \cref{ch:09}, mais l'argument des baux à loyer bas lui serait fermé par l'article~45.1.
753 764
754 Enfin, pour l'acheteur, l'acquisition au prix de \dollars{2060000} entraîne des droits sur les mutations immobilières de \dollars{41325.50} selon le barème montréalais de 2026 (\pct{0.5} sur \dollars{62900}, \pct{1} jusqu'à \dollars{315000}, \pct{1.5} jusqu'à \dollars{552300}, \pct{2} jusqu'à \dollars{1104700}, \pct{2.5} au-delà) \parencite{mtlmutations2026}. Au financement conventionnel recalculé à la valeur finale, le prêt reste limité par la couverture à \dollars{1475959} (RPV \pct{71.6}), la mise de fonds est de \dollars{584041} et le besoin de liquidités à la signature, droits compris et avant frais de notaire et d'inspection, de \dollars{625366}. Le rendement courant des fonds propres passe à \pct{2.57} et le TRI des fonds propres sur dix ans à \pct{13.83} (contre \pct{8.11} sans levier à ce prix), le levier demeurant négatif sur le revenu courant et positif sur le rendement total. 765 Enfin, pour l'acheteur, l'acquisition au prix de \dollars{2060000} entraîne des droits sur les mutations immobilières de \dollars{41325.50} selon le barème montréalais de 2026 (\pct{0.5} sur \dollars{62900}, \pct{1} jusqu'à \dollars{315000}, \pct{1.5} jusqu'à \dollars{552300}, \pct{2} jusqu'à \dollars{1104700}, \pct{2.5} au-delà) \parencite{mtlmutations2026}. Au financement conventionnel recalculé à la valeur finale, le prêt reste limité par la couverture à \dollars{1475959} (RPV \pct{71.6}), la mise de fonds est de \dollars{584041} et le besoin de liquidités à la signature, droits compris et avant frais de notaire et d'inspection, de \dollars{625366}. Le rendement courant des fonds propres passe à \pct{2.57} et le TRI des fonds propres sur dix ans à \pct{13.83} (contre \pct{8.11} sans levier à ce prix), le levier demeurant négatif sur le revenu courant et positif sur le rendement total.
755 766
added references/lib-ch47.bib +8 −0
@@ -0,0 +1,8 @@
1+% lib-ch47.bib — chapitre 47 : AUCUNE nouvelle entrée. Toutes les citations du chapitre utilisent des clés existantes de references/library.bib :
2+% oeaqNormes, ivs2025, cuspap2026, deontOEAQ, ccq, loi31_2024, loi65_2024, reglCriteresFixation, decret1455_2025, talIndices2026, talCommunique2026,
3+% talResumesDecisions, loiTAL, lau, lfm, ldmi, mtlmutations2026, montrealrole2026, mtlrole2026, ledevoirmtl2026, mtltaux2026, taxescolaire2026,
4+% schlLocatif2025, schlrmltables2025, schlpmh2026ete, apciqt32026, apciqbaromontreal2026, cbrecaprates2026q3, altusmontreal2026q2, bdc20261028, bdcvalet,
5+% bdctauxhypothecaires2026, statcanipc, teranet202609, osfiMqr, schlaphselect, schlaphprimes, schlfraisexploitation2015, oaciqFormulaireDV, oaciqContexteDV,
6+% geltnerMillerClaytonEichholtz2014, brueggemanFisher2022, fisherMartin2007, appraisal2026, gordonShapiro1956, hoesliJaniBender2006, frenchGabrielli2005,
7+% pyhrr1973, wofford1978, sivitanidouSivitanides1999, chervachidzeWheaton2013, ducaLing2020, bokhariGeltner2018, arnott1995, kholodilin2024,
8+% diamondMcquadeQian2019, autorPalmerPathak2014, dubeTheriaultDesrosiers2013.
modified solutions/ch47.tex +30 −1
@@ -1 +1,30 @@
1−% Solutions du chapitre 47 — Cas 3 : immeuble de 12 logements à Montréal 1+% Solutions du chapitre 47 — Cas 3 : immeuble de 12 logements à Montréal (données synthétiques)
2+% Tous les chiffres proviennent de analysis/python/cases/ch47_dossier.py (fonction exercices) ; tests : analysis/python/tests/test_ch47.py
3+
4+\begin{solution}{ex:47:1}{Vacance économique de 5~\%}
5+Le revenu brut effectif devient $RBE = \dollars{165540} \times (1 - \num{0.05}) + \dollars{9360} = \dollars{166623.00}$, en baisse de \dollars{1820.94}. La gestion imputée, à \pct{4} du revenu brut effectif, diminue de \dollars{72.84}, de sorte que les dépenses hors réserve passent de \dollars{70385.19} à \dollars{70312.35} ; la réserve reste à \dollars{8103}. Le revenu net normalisé est $\NOI_1 = \dollars{166623.00} - \dollars{70312.35} - \dollars{8103.00} = \dollars{88207.65}$, soit \dollars{1748.10} de moins que dans le dossier (la baisse du revenu est partiellement compensée par la baisse de la gestion). Capitalisé à \pct{4.415}, il donne $\dollars{88207.65} / \num{0.04415} = \dollars{1997908.27}$ : la valeur diminue de \dollars{39594.62}, soit \pct{1.9}. On retient qu'un point d'inoccupation vaut, pour cet immeuble, environ \dollars{36000} de valeur, et que la sensibilité à la vacance est modérée parce que la provision ne s'applique qu'aux loyers et non aux autres revenus.
6+\end{solution}
7+
8+\begin{solution}{ex:47:2}{Taxes municipales indexées plutôt que recalculées}
9+Le compte 2025 indexé de \pct{5.3} donne $\dollars{10631} \times \num{1.053} = \dollars{11194.44}$, contre \dollars{10188.20} par recalcul au taux général sur la valeur au rôle : l'écart est de \dollars{1006.24} de dépenses supplémentaires. Le revenu net passe de \dollars{89955.75} à \dollars{88949.51} et l'indication par capitalisation directe à $\dollars{88949.51} / \num{0.04415} = \dollars{2014711.42}$, soit \dollars{22791.46} de moins (\pct{1.1}). L'évaluateur doit signaler ce choix parce que les deux méthodes de normalisation reposent sur des informations incomplètes : le recalcul applique le seul taux général et ignore la taxe d'arrondissement et un éventuel taux propre aux immeubles de six logements et plus, tandis que l'indexation suppose que la hausse moyenne annoncée pour l'arrondissement s'applique à l'immeuble. La règle 2.3 des Normes exige que l'étendue du travail et les réserves soient décrites ; l'écart de \dollars{1006} sur les dépenses, qui se traduit par \dollars{22791} de valeur, est précisément le type d'incertitude qu'un lecteur du rapport doit pouvoir mesurer. La bonne pratique est d'obtenir le compte de taxes 2026 réel et le document officiel des taux avant la remise du rapport.
10+\end{solution}
11+
12+\begin{solution}{ex:47:3}{Choix du taux de capitalisation}
13+Avec $\NOI_1 = \dollars{89955.75}$ : au premier quartile (\pct{4.1525}), $V = \dollars{2166303.49}$ (+\pct{6.3} par rapport à la médiane) ; au troisième quartile (\pct{4.6375}), $V = \dollars{1939746.68}$ ($-\pct{4.8}$) ; au taux prédit par la régression pour un immeuble d'état 3 de douze logements, soit la constante \pct{4.567}, $V = \dollars{1969693.82}$ ($-\pct{3.3}$) ; au taux moyen pondéré par le revenu net (\pct{4.332}), $V = \dollars{2076580.69}$ (+\pct{1.9}). La fourchette interquartile mesure la dispersion des comparables et constitue une borne d'incertitude raisonnable de la méthode. Le taux de régression est pertinent parce qu'il corrige la taille, mais il repose sur une pente estimée imprécisément et sur une constante qui, par construction, s'approche de la moyenne de l'échantillon pour un immeuble moyen ; il suggère néanmoins qu'un douze logements se vend à un taux légèrement supérieur à la médiane de l'ensemble, ce qui plaide pour la prudence. La moyenne pondérée par le revenu net donne un poids excessif aux grands immeubles du Plateau et d'Ahuntsic, dont le taux est plus bas et le profil différent du sujet : elle est la statistique la moins pertinente ici. La médiane reste le meilleur compromis, la réconciliation finale (taux implicite \pct{4.37}) se situant d'ailleurs entre la médiane et la moyenne pondérée.
14+\end{solution}
15+
16+\begin{solution}{ex:47:4}{Rotation des locataires}
17+Avec une rotation de \pct{10} (un logement relocalisé par an), la valeur DCF est de \dollars{2140916.43}, soit \dollars{67649.48} de moins que la base ($-\pct{3.1}$) ; le revenu net de l'année 11 est de \dollars{156352.52} et le revenu brut potentiel de l'année 10 de \dollars{247211.89}. Avec une rotation de \pct{25} (trois logements par an), la valeur est de \dollars{2231654.72}, soit \dollars{23088.81} de plus (+\pct{1.0}) ; le revenu net de l'année 11 atteint \dollars{160590.16} et le revenu brut de l'année 10 \dollars{251977.55}. L'asymétrie vient de ce que le rattrapage est borné par le marché : à \pct{15}, l'écart de \pct{11} est déjà résorbé vers la cinquième année et le revenu brut de l'année 10 atteint \pct{99} du marché ; accélérer la rotation ne peut gagner que quelques années d'écart sur les premiers exercices, alors que la ralentir repousse le rattrapage au-delà de l'horizon et ampute durablement le revenu, y compris celui de l'année 11 qui fonde la valeur terminale. Ces montants sont exactement les bornes de la barre « rotation annuelle » de la tornade (quantiles 10 et 90 de la loi triangulaire, soit \pct{11.45} et \pct{20.88}, qui donnent \dollars{2140916} et \dollars{2231655} parce que l'arrondi à l'entier du nombre de logements relocalisés ramène ces taux à un et trois logements respectivement) ; l'amplitude de \dollars{90738} place la rotation au dernier rang des sources d'incertitude.
18+\end{solution}
19+
20+\begin{solution}{ex:47:5}{Hausse du taux hypothécaire de 100~points de base}
21+À \pct{5.12} composé semestriellement, le taux mensuel équivalent est $(1 + \num{0.0256})^{1/6} - 1 = \num{0.0042207}$ et la constante hypothécaire sur 480 mois $f = \pct{5.8391}$. Le prêt maximal selon le ratio prêt-valeur de \pct{75} sur \dollars{2060000} est \dollars{1545000} ; selon la couverture de \num{1.20}, $\dollars{89955.75} / (\num{1.20} \times \num{0.058391}) = \dollars{1283814.77}$. La contrainte liante demeure la couverture ; le prêt est de \dollars{1283814.77}, le ratio prêt-valeur effectif de \pct{62.3}, le service de la dette inchangé à \dollars{74963.13} (il est fixé par le revenu net et le ratio de couverture, non par le taux) et la mise de fonds de \dollars{776185.23}. Par rapport au cas de base à la valeur finale (prêt de \dollars{1475959.45}), le prêt diminue de \dollars{192144.68}, soit \pct{13.0} : une hausse de 100~points de base du taux réduit la capacité d'emprunt d'un immeuble contraint par la couverture dans la même proportion que la constante hypothécaire augmente ($\num{0.050789} / \num{0.058391} - 1 = -\pct{13.0}$). La mise de fonds requise augmente de \dollars{192145}, ce qui, à rendement exigé constant, se répercute sur le prix que les acheteurs endettés peuvent offrir : c'est le canal par lequel les taux d'intérêt agissent sur les taux de capitalisation.
22+\end{solution}
23+
24+\begin{solution}{ex:47:6}{Variante APH Select}
25+À la valeur finale de \dollars{2060000}, le prêt maximal selon le ratio prêt-valeur de \pct{85} est \dollars{1751000} ; selon la couverture de \num{1.10} à \pct{4.12} sur 480 mois, $\dollars{89955.75} / (\num{1.10} \times \num{0.050789}) = \dollars{1610137.58}$. La couverture est liante ; le prêt est de \dollars{1610137.58}, soit \pct{78.2} de la valeur, la mise de fonds de \dollars{449862.42} (contre \dollars{584040.55} au financement conventionnel, un gain de \dollars{134178}). La prime d'assurance est de \pct{4.35} pour un ratio prêt-valeur entre 75 et \pct{80}, majorée de \pct{0.25} par tranche de cinq ans d'amortissement au-delà de vingt-cinq ans, soit $3 \times \pct{0.25} = \pct{0.75}$ pour quarante ans : \pct{5.10} du prêt, soit \dollars{82117.02}, normalement capitalisée au prêt. Le service de la dette est de \dollars{81777.96} et le flux après dette de la première année de $\dollars{89955.75} - \dollars{81777.96} = \dollars{8177.79}$, soit un rendement courant de \pct{1.8} sur la mise de fonds. Quant à la compatibilité : l'obtention de 50~points par le volet abordabilité exige, pour un immeuble existant, qu'une part des logements (par exemple \pct{40}) demeure à un loyer inférieur ou égal à \pct{30} du revenu médian des locataires pendant dix ans. Or la valeur DCF du dossier repose sur le rattrapage des loyers au marché par la rotation, et la valeur finale en retient une partie. Un engagement d'abordabilité fige une fraction du revenu sous le marché : la valeur de l'immeuble \emph{sous cet engagement} est une valeur d'investissement, propre au propriétaire qui l'accepte en contrepartie d'un financement avantageux, et non la valeur marchande du dossier. Le prêteur qui finance sur la base d'APH Select devrait soit demander une évaluation sous l'hypothèse (extraordinaire, à déclarer) de l'engagement d'abordabilité, soit s'assurer que la couverture de \num{1.10} est calculée sur le revenu contraint ; dans le dossier, le revenu de première année est déjà celui des baux en place, de sorte que le prêt de \dollars{1610138} est soutenable à court terme, mais la croissance du revenu qui justifie le prix le serait moins. La valeur marchande n'est donc pas la bonne base pour mesurer le risque de ce prêt à long terme ; il faut une projection conforme à l'engagement.
26+\end{solution}
27+
28+\begin{solution}{ex:47:7}{Simulation dans un marché détendu}
29+Avec une inoccupation log-normale de médiane \pct{6} (écart-type logarithmique \num{0.30}), les autres lois inchangées, \num{10000} tirages et la graine \texttt{20261031}, la valeur simulée a une médiane de \dollars{2108707} (contre \dollars{2191608} dans le dossier, $-\pct{3.8}$) et un intervalle 5--95~\% de \dollars{1654991} à \dollars{2785424}. Le ratio de couverture de la première année du prêt de \dollars{1475959} a une médiane de \num{1.156} ; la probabilité qu'il soit inférieur à 1,20 est de \pct{92.6} et celle qu'il soit inférieur à 1,00 de \pct{0.38} (38 tirages). Le prêt dimensionné sur une inoccupation de \pct{3.9} est donc en bris de covenant dans l'immense majorité des scénarios d'un marché détendu, bien que le défaut de paiement reste rare. Pour ramener la probabilité de bris sous \pct{10}, il faut que le service de la dette $SD'$ satisfasse $P(\NOI_1 / SD' < 1{,}20) = 0{,}10$, c'est-à-dire $SD' = q_{0{,}10}(\NOI_1) / 1{,}20$ où $q_{0{,}10}(\NOI_1) = \dollars{82228}$ est le quantile 10~\% du revenu net simulé ; $SD' = \dollars{68523}$, soit un prêt de $\dollars{68523} / \num{0.050789} = \dollars{1349000}$ environ et un ratio de couverture \emph{sur le revenu de base} de $\dollars{89955.75} / \dollars{68523} = \num{1.31}$. Le prêteur devrait donc exiger un ratio de couverture d'environ \num{1.31} (la vérification par simulation donne \pct{14.2} de bris à \num{1.30} et \pct{3.8} à \num{1.35}), contre \num{1.24} dans la loi de base du dossier : le covenant qui protège le prêteur dépend entièrement de la loi de l'inoccupation, ce qui justifie que celle-ci soit déclarée et discutée dans le rapport de consultation.
30+\end{solution}
modified sources/registry.csv +1 −0
@@ -291,3 +291,4 @@ SRC-E-066,rapport,Rapport mensuel sur le marché immobilier au Québec — août
291 SRC-E-067,article,Banque du Canada — décision du 28 octobre 2026 (analyse),Desjardins Études économiques,2026-10-28,https://www.desjardins.com/fr/epargne-placements/etudes-economiques/canada-bdc-28-octobre-2026.html,2026-10-31,verifie,ch. 3,"Secondaire (institution financière) ; confirme +25 pb à 2,50 %, première hausse depuis juillet 2023." 291 SRC-E-067,article,Banque du Canada — décision du 28 octobre 2026 (analyse),Desjardins Études économiques,2026-10-28,https://www.desjardins.com/fr/epargne-placements/etudes-economiques/canada-bdc-28-octobre-2026.html,2026-10-31,verifie,ch. 3,"Secondaire (institution financière) ; confirme +25 pb à 2,50 %, première hausse depuis juillet 2023."
292 SRC-O-001,loi,Loi sur l'intérêt (L.R.C. 1985 ch. I-15) art. 6,Canada (ministère de la Justice),texte consolidé consulté le 2026-10-31,https://laws-lois.justice.gc.ca/fra/lois/i-15/page-1.html,2026-10-31,verifie,29;30;31;annexe conventions,"art. 6 lu : taux « calculé annuellement ou semestriellement, mais non d'avance » ; fonde la composition semestrielle de traite.finance.Hypotheque" 292 SRC-O-001,loi,Loi sur l'intérêt (L.R.C. 1985 ch. I-15) art. 6,Canada (ministère de la Justice),texte consolidé consulté le 2026-10-31,https://laws-lois.justice.gc.ca/fra/lois/i-15/page-1.html,2026-10-31,verifie,29;30;31;annexe conventions,"art. 6 lu : taux « calculé annuellement ou semestriellement, mais non d'avance » ; fonde la composition semestrielle de traite.finance.Hypotheque"
293 SRC-21-001,guide,RC4028 — Remboursement de la TPS/TVH pour habitations neuves,Agence du revenu du Canada,Rév. 10/2026,https://www.canada.ca/fr/agence-revenu/services/formulaires-publications/publications/rc4028/remboursement-tps-tvh-habitations-neuves.html,2026-10-31,partiellement_verifie,21,"Lu par agent web-fetch le 2026-10-31 : seuil 450 000 $ (maison construite par le propriétaire) verbatim ; remboursement premiers acheteurs (travaux dès le 20 mars 2025, 1 M$ / 1,5 M$) verbatim ; 36 % / 6 300 $ / 350 000 $ non vérifiés mot à mot. Pages Revenu Québec (TVQ) HTTP 403." 293 SRC-21-001,guide,RC4028 — Remboursement de la TPS/TVH pour habitations neuves,Agence du revenu du Canada,Rév. 10/2026,https://www.canada.ca/fr/agence-revenu/services/formulaires-publications/publications/rc4028/remboursement-tps-tvh-habitations-neuves.html,2026-10-31,partiellement_verifie,21,"Lu par agent web-fetch le 2026-10-31 : seuil 450 000 $ (maison construite par le propriétaire) verbatim ; remboursement premiers acheteurs (travaux dès le 20 mars 2025, 1 M$ / 1,5 M$) verbatim ; 36 % / 6 300 $ / 350 000 $ non vérifiés mot à mot. Pages Revenu Québec (TVQ) HTTP 403."
294+# Aucune nouvelle source : le chapitre 47 ne cite que des clés déjà présentes dans references/library.bib (voir ch47.md).,,,,,,,,,
modified verification/calculations.csv +26 −0
@@ -52,3 +52,29 @@ CALC-30-10,30,"Proposition 30.1 : DCF = NOI_1/(r−g) pour T = 1, 5, 10, 25, 60
52 CALC-30-11,30,"Sensibilité R_T 4,25 → 5,75 % : V 2 481 417 → 2 006 893 (+12,4 % → −9,1 %) ; surfaces V(r,R_T) et V(r,g) monotones",analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_sensibilite_monotone,2481417 ; 2006893,2481416.97 ; 2006893.39,verifie,2026-10-31 52 CALC-30-11,30,"Sensibilité R_T 4,25 → 5,75 % : V 2 481 417 → 2 006 893 (+12,4 % → −9,1 %) ; surfaces V(r,R_T) et V(r,g) monotones",analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_sensibilite_monotone,2481417 ; 2006893,2481416.97 ; 2006893.39,verifie,2026-10-31
53 CALC-30-12,30,"Tornade : amplitude max = croissance des loyers de marché 368 235 ; scénarios : encadrement serré 1 912 046 (−13,4 %), hausse des taux 1 947 889 (−11,8 %) ; TRI FP si payé base 7,48 % / 8,31 % ; RCD min 1,04",analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_tornade_et_scenarios,368235 ; 1912046 ; 1947889,368235.05 ; 1912046.35 ; 1947889.41,verifie,2026-10-31 53 CALC-30-12,30,"Tornade : amplitude max = croissance des loyers de marché 368 235 ; scénarios : encadrement serré 1 912 046 (−13,4 %), hausse des taux 1 947 889 (−11,8 %) ; TRI FP si payé base 7,48 % / 8,31 % ; RCD min 1,04",analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_tornade_et_scenarios,368235 ; 1912046 ; 1947889,368235.05 ; 1912046.35 ; 1947889.41,verifie,2026-10-31
54 CALC-30-13,30,Solutions des exercices 30.1 à 30.8 (fonction exercices()),analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_exercices_coherence,voir solutions/ch30.tex,OK,verifie,2026-10-31 54 CALC-30-13,30,Solutions des exercices 30.1 à 30.8 (fonction exercices()),analysis/python/cases/ch30_dcf.py,analysis/python/tests/test_ch30.py::test_exercices_coherence,voir solutions/ch30.tex,OK,verifie,2026-10-31
55+CALC-47-001,47,RBP contractuel = 12 × somme des loyers,analysis/python/cases/ch47_dossier.py,analysis/python/tests/test_ch47.py::test_identites_revenus_depenses,165540,165540.00,verifie,2026-10-31
56+CALC-47-002,47,RBE = RBP(1−0,039) + 9 360,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_identites_revenus_depenses,168443.94,168443.94,verifie
57+CALC-47-003,47,Dépenses normalisées hors réserve (taxes recalculées ; gestion 4 % RBE ; +3 %),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,70385.19,70385.19,verifie,2026-10-31
58+CALC-47-004,47,RNE normalisé = RBE − DE − réserve 8 103,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,89955.75,89955.75,verifie,2026-10-31
59+CALC-47-005,47,Taux global : médiane de 40 ventes (moyenne 4,404 ; pondérée 4,332 ; Q1 4,1525 ; Q3 4,6375),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees
60+CALC-47-006,47,Valeur par capitalisation directe RNE/0,04415,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf,2037502.89,2037502.89,verifie
61+CALC-47-007,47,Projection DCF 10 ans (rotation 15 % ; g_c 3,1 ; g_m 3,5 ; g_d 3,0 ; CAPEX 85k/40k/120k) : RNE_11,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,159658.38
62+CALC-47-008,47,Valeur terminale brute RNE_11/0,05 et nette ×0,975,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf,3193167.52 / 3113338.33,3193167.52 / 3113338.33
63+CALC-47-009,47,Valeur DCF (r 7,25 %) = VA flux 662 409,91 + VA terminale 1 546 156,00 ; part terminale 70,0 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf
64+CALC-47-010,47,OLS ln(prix/logement) : élasticité loyer 1,1707 ; taille 0,0079/log. ; R² 0,635,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1.1707 / 0.0079
65+CALC-47-011,47,Comparaison par unité : prix/logement pondéré 155 908,56 × 12,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1870902.67,1870902.67,verifie
66+CALC-47-012,47,MRB médian des 10 comparables 11,94 × RBP contractuel,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1976547.60,1976547.60,verifie
67+CALC-47-013,47,Prêt max RCD 1,20 à 4,12 % / 480 mois (constante 5,0789 %) ; prêt RPV 75 % de V_dcf 1 656 424,43,analysis/python/traite/cas3.py,test_ch47.py::test_valeurs_citees
68+CALC-47-014,47,Service de la dette 74 963,13 ; paiement mensuel 6 246,93 ; solde après 60 mois 1 394 739,19 ; après 120 mois 1 295 148,22,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees
69+CALC-47-015,47,TRI fonds propres (achat à V_dcf) 11,22 % ; VAN nulle au TRI ; point mort 92,7 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_financement_identites,0.1122 / 0.927,0.1122 / 0.927
70+CALC-47-016,47,Scénarios de renouvellement an 6 : SD +200 pb 95 920,20 ; RCD an 6 à +300 pb 1,222,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,95920.20 / 1.222,95920.20 / 1.222
71+CALC-47-017,47,APH Select 40 ans (RPV 85 %,RCD 1,10) à la valeur finale : prêt,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1610137.58,1610137.58
72+CALC-47-018,47,Monte-Carlo 10 000 tirages graine 20261031 : médiane / q05 / q95 / moyenne / écart-type,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_monte_carlo_citations_et_reproductibilite,2191607.87 / 1735194.21 / 2876902.35 / 2233505.68 / 350442.57,idem,verifie,2026-10-31
73+CALC-47-019,47,Monte-Carlo : P(RCD1<1,20) 0,4936 ; P(RCD1<1,00) 0 ; P(RCD6<1,20 | +200 pb) 0,0140 ; P(V<cap) 0,3094 ; P(V<rôle) 0
74+CALC-47-020,47,Projection vectorisée (MC) reproduit cas3.projection_dcf aux paramètres de base (rtol 1e-12),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_projection_vectorisee_reproduit_cas3,identique,identique,verifie,2026-10-31
75+CALC-47-021,47,Tornade : amplitude taux d'actualisation 636 535 ; croissance loyers marché 459 632 ; écart terminal 320 404,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_projection_vectorisee_reproduit_cas3 (V_base),636534.56,636534.56,verifie,2026-10-31
76+CALC-47-022,47,Réconciliation 45/35/20 → 2 064 054,90 arrondie à 2 060 000 ; ratio/rôle 0,9364 ; MRB 12,44 ; taux implicite 4,367 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees
77+CALC-47-023,47,Droits de mutation Montréal 2026 sur 2 060 000 (7 tranches),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_droits_mutation_montreal,41325.50,41325.50,verifie,2026-10-31
78+CALC-47-024,47,Valeur libre de baux (loyers de marché) : RNE 108 864,54 ; valeur 2 465 788,04,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,2465788.04,2465788.04
79+CALC-47-025,47,Financement à la valeur finale : mise de fonds 584 040,55 ; RPV 71,65 % ; TRI FP 13,83 % ; TRI sans levier 8,11 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees
80+CALC-47-026,47,Exercices 1 à 7 (RNE 88 207,65 ; 88 949,51 ; V Q1/Q3 ; rotation 10/25 % ; prêt à 5,12 % 1 283 814,77 ; APH ; MC inoccupation 6 % P=0,9262),analysis/python/cases/ch47_dossier.py
added verification/chapters/ch47-calculations.csv +27 −0
@@ -0,0 +1,27 @@
1+id,chapitre,description,script,test,resultat_attendu,resultat_obtenu,statut,date
2+CALC-47-001,47,RBP contractuel = 12 × somme des loyers,analysis/python/cases/ch47_dossier.py,analysis/python/tests/test_ch47.py::test_identites_revenus_depenses,165540,165540.00,verifie,2026-10-31
3+CALC-47-002,47,RBE = RBP(1−0,039) + 9 360,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_identites_revenus_depenses,168443.94,168443.94,verifie,2026-10-31
4+CALC-47-003,47,Dépenses normalisées hors réserve (taxes recalculées ; gestion 4 % RBE ; +3 %),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,70385.19,70385.19,verifie,2026-10-31
5+CALC-47-004,47,RNE normalisé = RBE − DE − réserve 8 103,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,89955.75,89955.75,verifie,2026-10-31
6+CALC-47-005,47,Taux global : médiane de 40 ventes (moyenne 4,404 ; pondérée 4,332 ; Q1 4,1525 ; Q3 4,6375),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,0.04415,0.04415,verifie,2026-10-31
7+CALC-47-006,47,Valeur par capitalisation directe RNE/0,04415,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf,2037502.89,2037502.89,verifie,2026-10-31
8+CALC-47-007,47,Projection DCF 10 ans (rotation 15 % ; g_c 3,1 ; g_m 3,5 ; g_d 3,0 ; CAPEX 85k/40k/120k) : RNE_11,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,159658.38,159658.38,verifie,2026-10-31
9+CALC-47-008,47,Valeur terminale brute RNE_11/0,05 et nette ×0,975,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf,3193167.52 / 3113338.33,3193167.52 / 3113338.33,verifie,2026-10-31
10+CALC-47-009,47,Valeur DCF (r 7,25 %) = VA flux 662 409,91 + VA terminale 1 546 156,00 ; part terminale 70,0 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_capitalisation_et_dcf,2208565.91,2208565.91,verifie,2026-10-31
11+CALC-47-010,47,OLS ln(prix/logement) : élasticité loyer 1,1707 ; taille 0,0079/log. ; R² 0,635,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1.1707 / 0.0079,1.1707 / 0.0079,verifie,2026-10-31
12+CALC-47-011,47,Comparaison par unité : prix/logement pondéré 155 908,56 × 12,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1870902.67,1870902.67,verifie,2026-10-31
13+CALC-47-012,47,MRB médian des 10 comparables 11,94 × RBP contractuel,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1976547.60,1976547.60,verifie,2026-10-31
14+CALC-47-013,47,Prêt max RCD 1,20 à 4,12 % / 480 mois (constante 5,0789 %) ; prêt RPV 75 % de V_dcf 1 656 424,43,analysis/python/traite/cas3.py,test_ch47.py::test_valeurs_citees,1475959.45,1475959.45,verifie,2026-10-31
15+CALC-47-014,47,Service de la dette 74 963,13 ; paiement mensuel 6 246,93 ; solde après 60 mois 1 394 739,19 ; après 120 mois 1 295 148,22,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,74963.13,74963.13,verifie,2026-10-31
16+CALC-47-015,47,TRI fonds propres (achat à V_dcf) 11,22 % ; VAN nulle au TRI ; point mort 92,7 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_financement_identites,0.1122 / 0.927,0.1122 / 0.927,verifie,2026-10-31
17+CALC-47-016,47,Scénarios de renouvellement an 6 : SD +200 pb 95 920,20 ; RCD an 6 à +300 pb 1,222,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,95920.20 / 1.222,95920.20 / 1.222,verifie,2026-10-31
18+CALC-47-017,47,APH Select 40 ans (RPV 85 %, RCD 1,10) à la valeur finale : prêt,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,1610137.58,1610137.58,verifie,2026-10-31
19+CALC-47-018,47,Monte-Carlo 10 000 tirages graine 20261031 : médiane / q05 / q95 / moyenne / écart-type,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_monte_carlo_citations_et_reproductibilite,2191607.87 / 1735194.21 / 2876902.35 / 2233505.68 / 350442.57,idem,verifie,2026-10-31
20+CALC-47-019,47,Monte-Carlo : P(RCD1<1,20) 0,4936 ; P(RCD1<1,00) 0 ; P(RCD6<1,20 | +200 pb) 0,0140 ; P(V<cap) 0,3094 ; P(V<rôle) 0,5118,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_monte_carlo_citations_et_reproductibilite,0.4936 / 0 / 0.0140,idem,verifie,2026-10-31
21+CALC-47-020,47,Projection vectorisée (MC) reproduit cas3.projection_dcf aux paramètres de base (rtol 1e-12),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_projection_vectorisee_reproduit_cas3,identique,identique,verifie,2026-10-31
22+CALC-47-021,47,Tornade : amplitude taux d'actualisation 636 535 ; croissance loyers marché 459 632 ; écart terminal 320 404,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_projection_vectorisee_reproduit_cas3 (V_base),636534.56,636534.56,verifie,2026-10-31
23+CALC-47-022,47,Réconciliation 45/35/20 → 2 064 054,90 arrondie à 2 060 000 ; ratio/rôle 0,9364 ; MRB 12,44 ; taux implicite 4,367 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,2060000,2060000,verifie,2026-10-31
24+CALC-47-023,47,Droits de mutation Montréal 2026 sur 2 060 000 (7 tranches),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_droits_mutation_montreal,41325.50,41325.50,verifie,2026-10-31
25+CALC-47-024,47,Valeur libre de baux (loyers de marché) : RNE 108 864,54 ; valeur 2 465 788,04,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,2465788.04,2465788.04,verifie,2026-10-31
26+CALC-47-025,47,Financement à la valeur finale : mise de fonds 584 040,55 ; RPV 71,65 % ; TRI FP 13,83 % ; TRI sans levier 8,11 %,analysis/python/cases/ch47_dossier.py,test_ch47.py::test_valeurs_citees,584040.55,584040.55,verifie,2026-10-31
27+CALC-47-026,47,Exercices 1 à 7 (RNE 88 207,65 ; 88 949,51 ; V Q1/Q3 ; rotation 10/25 % ; prêt à 5,12 % 1 283 814,77 ; APH ; MC inoccupation 6 % P=0,9262),analysis/python/cases/ch47_dossier.py,test_ch47.py::test_exercices,voir solutions/ch47.tex,conformes,verifie,2026-10-31
added verification/chapters/ch47-claims.csv +38 −0
@@ -0,0 +1,38 @@
1+id,chapitre,affirmation,source_id,verification,date,statut,notes
2+CLM-47-001,47,Définition OEAQ de la valeur marchande (« prix sincère le plus probable… marché libre et ouvert à la concurrence ») et quatre conditions,SRC-C-001,NPP p. Intro-11 lues (notes-C §1.1),2026-10-31,verifie,citation ≤ 25 mots ; conditions paraphrasées
3+CLM-47-002,47,Contenu minimal du rapport écrit : 12 éléments (règle 2.3) ; inspection obligatoire (règle 2.4) ; hypothèses extraordinaires rappelées à chaque conclusion (2.1 c et 2.3 él. 7) ; rejet d'une méthode justifié,SRC-C-001,NPP lues (notes-C §2.1),2026-10-31,verifie,
4+CLM-47-003,47,Code de déontologie : n'agir que pour une partie (art. 20) ; divulgation double rôle (18-19) ; honoraires conditionnels interdits (24.1) ; signature et qualité (34),SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,
5+CLM-47-004,47,IVS 102 annexe A10 : définition de la market value (édition en vigueur 31 janv. 2025),SRC-C-014,PDF officiel (notes-C §1.2),2026-10-31,verifie,
6+CLM-47-005,47,Art. 1937 C.c.Q. : l'aliénation ne permet pas au nouveau locateur de résilier le bail ; 1936 maintien dans les lieux,SRC-A-001,LégisQuébec à jour au 2026-10-15 (notes-A §4),2026-10-31,verifie,
7+CLM-47-006,47,Art. 1941-1945 et 1947 C.c.Q. : avis de modification 3 à 6 mois avant le terme ; refus dans le mois ; demande au TAL dans le mois,SRC-A-001,notes-A §4,2026-10-31,verifie,
8+CLM-47-007,47,Règlement sur les critères de fixation (D. 1455-2025 en vigueur 2026-01-01) : six critères art. 3 ; pourcentage de base IPC moyen art. 3.1 ; 5 % sur immobilisations ; base 2026 = 3,1 % ; 2027 non publié,SRC-A-003;SRC-A-004;SRC-E-048,LégisQuébec + Gazette + page TAL lues (notes-A §6.2),2026-10-31,verifie,2027 à vérifier en janvier 2027
9+CLM-47-008,47,La valeur locative de marché n'est pas un critère de fixation (résumé TAL Société de financement alouette c. A. 2022),SRC-A-001,résumés de décisions sur le site du TAL (notes-A §6.3) ; pas de référence neutre,2026-10-31,partiellement_verifie,source secondaire officielle (résumé) ; décision non lue
10+CLM-47-009,47,Art. 1896 et 1950 C.c.Q. : avis du loyer le plus bas des 12 mois (section G) ; nouveau locataire 10 jours pour demander la fixation,SRC-A-001,notes-A §4,2026-10-31,verifie,
11+CLM-47-010,47,Loi 31 (2024 c. 2) : art. 1978.2 refus de cession pour motif autre que sérieux → bail résilié ; 1978.3 aucune contrepartie,SRC-A-001;SRC-A-013,notes-A §4,2026-10-31,verifie,
12+CLM-47-011,47,Loi 65 (2024 c. 23) art. 1 : aucune éviction (art. 1959) avant le 6 juin 2027 ; art. 1959.1 protection 65 ans/10 ans/125 % ; art. 1965 indemnité 1 mois par année entre 3 et 24 mois,SRC-A-001;SRC-A-014,notes-A §5,2026-10-31,verifie,aucun avis de fin anticipée repéré au 2026-10-31
13+CLM-47-012,47,Art. 1904 C.c.Q. : aucun dépôt ; art. 947 pleine propriété,SRC-A-001,notes-A,2026-10-31,verifie,
14+CLM-47-013,47,Formulaire DV de l'OACIQ obligatoire seulement pour les immeubles résidentiels de moins de 5 logements,SRC-A-026;SRC-A-028,notes-A §2.4,2026-10-31,verifie,
15+CLM-47-014,47,Loi sur le TAL art. 51-54.13 : conversion en copropriété divise soumise à autorisation ; interdite dans l'agglomération de Montréal sauf dérogation,SRC-A-002,notes-A §6.1,2026-10-31,verifie,
16+CLM-47-015,47,LAU art. 113 al. 2 par. 18° : pouvoir de régir les usages dérogatoires protégés par droits acquis,SRC-B-0xx (LAU),notes-B ch. 8,2026-10-31,verifie,id registre LAU à compléter par l'orchestrateur
17+CLM-47-016,47,LFM art. 43 (valeur réelle) ; art. 45.1 vendeur réputé détenir les droits du locataire ; art. 46 date de référence = 1er juillet du 2e exercice précédent (rôle 2026-2028 → 2024-07-01),SRC-B-001 (LFM),notes-B §9.2,2026-10-31,verifie,
18+CLM-47-017,47,Rôle 2026-2028 de Montréal : déposé 2025-09-10 ; agglomération +12,2 % ; résidentiel +9,8 % ; 6 logements et plus +10,9 % ; facteur comparatif 1,00,SRC-E-051;SRC-E-052;SRC-B-031;SRC-B-030,communiqués Ville + presse (notes-B §9.9 ; notes-E ch. 47),2026-10-31,verifie,variation Rosemont non trouvée
19+CLM-47-018,47,Taxe foncière générale résidentielle Montréal 2026 : 0,4631 $/100 $ ; hausse moyenne 3,8 % ; Rosemont 5,3 %,SRC-E-031;SRC-B-024;SRC-B-023,Le Devoir 2026-01-12 (document officiel GED non lisible),2026-10-31,partiellement_verifie,presse ; taux « 6 logements et plus » et taxe d'arrondissement NON VÉRIFIÉS
20+CLM-47-019,47,Taux « 6 logements et plus » de Montréal et taxe d'arrondissement Rosemont 2026,—,document « Taux de taxes 2026 » non accessible par script,2026-10-31,non_verifie,normalisation au taux général ; sensibilité ±1 006 $ (ex. 47.2)
21+CLM-47-020,47,Taxe scolaire 2026-2027 : 0,07899 $/100 $ (exemption des premiers 25 000 $),SRC-E-026,communiqué CSS (avis officiel G.O. non ouvert),2026-10-31,verifie,avis G.O. 158 G.O. 1 520 non consulté
22+CLM-47-021,47,Droits de mutation Montréal 2026 : 7 tranches 0,5 % ≤ 62 900 ; 1 % ≤ 315 000 ; 1,5 % ≤ 552 300 ; 2 % ≤ 1 104 700 ; 2,5 % ≤ 2 136 500 ; 3,5 % ≤ 3 113 000 ; 4 % au-delà ; base = max(prix, contrepartie, valeur marchande) (LDMI art. 2),SRC-B-030;SRC-B-034,page Ville 2026-01-12 + LégisQuébec,2026-10-31,verifie,
23+CLM-47-022,47,SCHL oct. 2025 : zone Rosemont loyers 1 ch. 1 055 / 2 ch. 1 348 / 3 ch.+ 1 750 / tous 1 237 ; univers 44 554 ; RMR inoccupation 2,9 % (2,1 %) ; 6-19 log. île 2,2 % et 1 177 $ ; variation 7,4 % / 7,2 % ; rotation 9,4 % (11,1 %),SRC-E-007,tableaux Excel SCHL (notes-E ch. 4 15 27),2026-10-31,verifie,
24+CLM-47-023,47,SCHL Perspectives été 2026 : inoccupation RMR Montréal 3,8 % (2026) 4,5 % (2027) 4,8 % (2028),SRC-C-0xx (schlpmh2026ete),notes-C §4.6,2026-10-31,verifie,
25+CLM-47-024,47,APCIQ T3 2026 : plex Rosemont 950 000 $ (+2 %) ; île 900 000 $ ; RMR 860 500 $,SRC-E-001;SRC-E-004,communiqué et baromètre (notes-E),2026-10-31,verifie,attribution APCIQ/Centris requise
26+CLM-47-025,47,CBRE T3 2026 Montréal low-rise B 4,25-4,75 % (en hausse) ; écart avec obligation 10 ans,SRC-E-045,rapport CBRE publié 2026-10-21,2026-10-31,verifie,industrie ; valeurs ponctuelles seulement
27+CLM-47-026,47,Altus : 2,9 G$ investis en multirésidentiel à Montréal au S1 2026 (+30 %),SRC-E-0xx (altusmontreal2026q2),notes-E ch. 29,2026-10-31,verifie,
28+CLM-47-027,47,Banque du Canada : taux directeur 2,25 % du 2025-10-30 au 2026-10-28 puis 2,50 % ; obligations 5 ans 3,58 % et 10 ans 3,84 % (2026-10-29) ; hypothécaire 5 ans affiché 6,19 %,SRC-C-027;SRC-C-028;SRC-C-029;SRC-C-030,communiqué + Valet (notes-C §4.1),2026-10-31,verifie,
29+CLM-47-028,47,Fonds avancés prêts assurés fixe ≥ 5 ans : 4,12 % (août 2026),SRC-E-012,Valet A4_RATES_MORTGAGES,2026-10-31,verifie,
30+CLM-47-029,47,Loi sur l'intérêt L.R.C. 1985 c. I-15 art. 6 : taux exprimé en composition semestrielle ou annuelle,—,docstring finance.py et annexe conventions ; texte fédéral non consulté dans les lots A-E,2026-10-31,partiellement_verifie,à vérifier sur laws-lois.justice.gc.ca
31+CLM-47-030,47,BSIF : taux admissible minimal non assuré = max(taux + 2 %, 5,25 %),SRC-C-035 (osfiMqr),page BSIF modifiée 2026-01-29 (notes-C §4.2),2026-10-31,verifie,
32+CLM-47-031,47,APH Select : 50 points → RPV 85 % existant / amortissement 40 ans / RCD 1,10 ; 70 et 100 points → 95 % et 45/50 ans ; critère abordabilité 40 % des logements ≤ 30 % du revenu médian des locataires ; engagement 10 ans,SRC-C-040,page SCHL 2026-09-21 (notes-C §4.4),2026-10-31,partiellement_verifie,cellules 70/100 points déduites de la mise en page ; admissibilité du sujet non examinée
33+CLM-47-032,47,Primes SCHL immeubles collectifs : 2,60 % ≤ 65 % ; 2,85 ; 3,35 ; 4,35 % ≤ 80 % ; 5,35 % ≤ 85 % ; surprime 0,25 % par 5 ans au-delà de 25 ans,SRC-C-041,PDF SCHL (notes-C §4.4),2026-10-31,verifie,PDF non daté
34+CLM-47-033,47,SCHL Frais d'exploitation Québec (avril 2015) : gestion 3 à 5 % du revenu brut selon la taille ; entretien 500 $/log. ; TAL forfait gestion 5 %,SRC-E-047;SRC-E-048,document hébergé par un tiers ; formulaire RN,2026-10-31,partiellement_verifie,document daté (2015) à titre indicatif
35+CLM-47-034,47,IPC Québec +3,1 % (sept. 2026) ; tarif électricité domestique +3,0 % (2026),SRC-E-0xx (statcanipc ; Régie D-2026-033),notes-E ch. 27-28,2026-10-31,verifie,
36+CLM-47-035,47,Teranet–BN Montréal +1,4 % a/a (sept. 2026),SRC-C-0xx (teranet202609),notes-C §4.9,2026-10-31,verifie,
37+CLM-47-036,47,Réserve de remplacement 650-675 $/logement et gestion imputée 4 % : conventions de l'ouvrage (aucune enquête officielle récente),—,data/synthetic/README.md ; notes-E ch. 28 (NON VÉRIFIÉ),2026-10-31,non_verifie,incohérence : docstring cas3.py « 650 $/logement » vs donnée 8 103 $ = 675,25 $/logement (à harmoniser)
38+CLM-47-037,47,Vacance économique 3,9 % ; rotation 15 % ; croissance loyers marché 3,5 % ; r 7,25 % ; R_T 5,0 % ; coûts de vente 2,5 % ; ajustement état 3 %/point : hypothèses de l'auteur,—,déclarées comme hypothèses dans le texte et testées par Monte-Carlo,2026-10-31,non_verifie,hypothèses pédagogiques (données synthétiques)
added verification/chapters/ch47-figures.csv +8 −0
@@ -0,0 +1,8 @@
1+fichier,chapitre,script,donnees,type_donnees,legende_courte,statut
2+ch47/ch47-fig-etat-locatif.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_etat_locatif.csv,synthetique,Loyers contractuels vs loyers de marché des 12 logements [données synthétiques],verifie
3+ch47/ch47-fig-depenses.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_depenses.csv;cas3_montreal_hypotheses_dcf.json,synthetique,Structure des dépenses normalisées de première année par poste et nature [données synthétiques],verifie
4+ch47/ch47-fig-taux-extraits.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_comparables.csv,synthetique,Taux globaux extraits de 40 ventes selon la taille et l'état avec médiane et sujet [données synthétiques],verifie
5+ch47/ch47-fig-flux-dcf.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_*.csv;hypotheses_dcf.json,synthetique,RNE CAPEX et flux avant dette sur 10 ans avec service de la dette [données synthétiques],verifie
6+ch47/ch47-fig-montecarlo.pdf,47,analysis/python/figures/ch47_dossier_figures.py,simulation 10 000 tirages graine 20261031 (ch47_dossier.monte_carlo),synthetique,Histogramme de la valeur simulée avec quantiles 5/50/95 DCF et capitalisation [données synthétiques],verifie
7+ch47/ch47-fig-tornade.pdf,47,analysis/python/figures/ch47_dossier_figures.py,ch47_dossier.tornade (quantiles 10/90 des lois déclarées),synthetique,Tornade de la valeur DCF selon sept intrants [données synthétiques],verifie
8+ch47/ch47-fig-rcd-scenarios.pdf,47,analysis/python/figures/ch47_dossier_figures.py,ch47_dossier.scenarios_renouvellement,synthetique,RCD par année sous quatre scénarios de taux au renouvellement [données synthétiques],verifie
added verification/chapters/ch47-registry.csv +2 −0
@@ -0,0 +1,2 @@
1+id,type,titre,organisme,version_ou_date,url,date_consultation,statut,utilise_dans,notes
2+# Aucune nouvelle source : le chapitre 47 ne cite que des clés déjà présentes dans references/library.bib (voir ch47.md).
added verification/chapters/ch47.md +33 −0
@@ -0,0 +1,33 @@
1+# Chapitre 47 — Cas 3 : immeuble de 12 logements à Montréal — bilan de rédaction (2026-10-31)
2+
3+## Livrables
4+- `manuscript/chapters/ch47.tex` : ≈ 12 800 mots de prose (18 300 mots au compteur brut de `check_manuscript.py`, tableaux et équations compris), 14 sections (47.1 mandat → 47.14 rapport), **19 tableaux**, **7 figures**, **18 équations numérotées**, encadrés `synthetique`, `quebec`, `erreurs`, `limites`, `synthese`, **7 exercices** (niveaux 1-1-2-2-2-3-3), « Pour aller plus loin ». 57 clés bibliographiques citées, toutes déjà présentes dans `references/library.bib` (aucune nouvelle entrée : `references/lib-ch47.bib` ne contient qu'un commentaire ; `ch47-registry.csv` vide).
5+- `analysis/python/cases/ch47_dossier.py` : tous les chiffres cités (revenus, dépenses, RNE, taux extraits, capitalisation, DCF, comparaison par unité avec OLS auxiliaire + `traite.comparables`, financement, scénarios de renouvellement, APH Select, Monte-Carlo vectorisé 10 000 tirages graine 20261031, tornade, réconciliation, droits de mutation Montréal 2026, valeur libre de baux, exercices). Appelle `traite.cas3` sans le modifier. Exécution ≈ 0,7 s.
6+- `analysis/python/figures/ch47_dossier_figures.py` → `assets/figures/ch47/ch47-fig-{etat-locatif,depenses,taux-extraits,flux-dcf,montecarlo,tornade,rcd-scenarios}.pdf` (style `traite.style`, 135 mm).
7+- `analysis/python/tests/test_ch47.py` : 8 tests (identités, valeurs citées ±1 $, reproduction exacte de `cas3.projection_dcf` par la version vectorisée, reproductibilité MC même graine / robustesse autre graine, exercices) — **vert** (`8 passed`), `test_cas3.py` toujours vert.
8+- `solutions/ch47.tex` : 7 solutions détaillées (chiffres de `exercices()`).
9+- `verification/chapters/ch47-figures.csv` (7 lignes), `ch47-calculations.csv` (26 lignes), `ch47-claims.csv` (37 lignes), `ch47-registry.csv` (aucune ligne), `references/lib-ch47.bib` (aucune entrée) — à fusionner par l'orchestrateur.
10+
11+## Contrôles
12+- `tools/compile_chapter.sh 47` → `OK ch47 : 50 pages, 0 references/citations non definies, 0 overfull hbox` (dernière passe ; voir sortie console). Inspection visuelle : `pdftoppm -r 50` sur les 50 pages, pages 6, 9, 10, 16, 22, 28, 34, 40 vérifiées (tableaux dans la justification, figures à la largeur du texte, encadrés, exercices).
13+- `python3 -m pytest -q analysis/python/tests/test_ch47.py` → `8 passed`.
14+- `tools/check_manuscript.py --no-log` : 7 « problèmes » = les 7 figures du ch. 47 absentes de `verification/figures.csv` global (lignes fournies dans `ch47-figures.csv`, fusion non faite par le rédacteur conformément à la consigne).
15+
16+## Chiffres pivots (coïncident avec `traite.cas3.tout()` donc avec ch. 29-31)
17+RBP 165 540 ; RBE 168 443,94 ; dépenses 78 488,19 (dont réserve 8 103) ; RNE 89 955,75 ; taux médian 4,415 % (n = 40) ; V_cap 2 037 502,89 ; V_DCF 2 208 565,91 (VT nette 3 113 338,33 ; part terminale 70,0 %) ; comparaison par unité 1 870 902,67 (MRB 11,94 → 1 976 548) ; prêt RCD 1,20 = 1 475 959,45 (liant ; RPV 75 % = 1 656 424 sur V_DCF) ; SD 74 963,13 ; TRI FP 11,22 % ; point mort 92,7 % ; MC : médiane 2 191 608, IC 90 % [1 735 194 ; 2 876 902], P(RCD₁<1,20) 49,4 %, P(RCD<1,0) = 0 ; **valeur finale 2 060 000 $** (45/35/20), 171 667 $/log., taux implicite 4,37 %, 93,6 % du rôle ; droits de mutation 41 325,50 $.
18+
19+## Points non vérifiés / à harmoniser
20+1. Taux de taxe foncière Montréal 2026 « 6 logements et plus » et taxe d'arrondissement Rosemont : non vérifiés (document officiel non lisible) ; normalisation au taux général 0,4631 $/100 $ (source presse) ; sensibilité −1 006 $ de RNE (ex. 47.2). Claims CLM-47-018/019.
21+2. **Réserve de remplacement** : la docstring de `traite/cas3.py` annonce « 650 $/logement (convention du livre) » mais le code reprend l'historique 2025 = 8 103 $ = 675,25 $/logement. Le chapitre écrit « 8 103 $ (675 $/logement) » et signale la convention ≈ 650 $. À harmoniser (donnée ou docstring) avec les ch. 28-30.
22+3. Pourcentage TAL 2027 non publié ; croissance contractuelle = pourcentage de base 3,1 % seulement.
23+4. Rotation 15 % > taux SCHL régional 9,4 % (hypothèse déclarée, testée 8-25 %).
24+5. Loi sur l'intérêt (fédérale, art. 6) citée en note de bas de page sans clé bib (aucune entrée dans library.bib) — CLM-47-029 `partiellement_verifie`.
25+6. APH Select : cellules 70/100 points et admissibilité du sujet non vérifiées (CLM-47-031).
26+7. Résumé TAL « valeur locative non critère » : source secondaire officielle, décision non lue (CLM-47-008).
27+8. `ch47-claims.csv` : quelques `source_id` indiqués « SRC-B-0xx » / « SRC-C-0xx » (LAU, LFM, schlpmh2026ete, altus, statcanipc, teranet) à compléter par l'orchestrateur avec les identifiants exacts du registre fusionné.
28+9. Renvois inter-chapitres : macro locale `\chref{NN}` (= `\cref{ch:NN}` si le label existe, « chapitre NN » sinon) pour permettre la compilation isolée à 0 référence non définie ; la convention « \cref sans article » du 2026-10-31 est respectée (article écrit dans le texte). L'orchestrateur peut remplacer `\chref` par `\cref` lors de l'harmonisation.
29+
30+## Suggestions
31+- Vérifier que les ch. 29/30/31 présentent le financement sur la même base (V_DCF) ; le ch. 47 le recalcule aussi à la valeur finale (mise de fonds 584 041 $, RPV 71,6 %).
32+- Le fichier `data/synthetic/cas3_montreal_sujet.json` ne contient ni lot ni description des composantes : la fiche d'inspection (tab. 47.2) est narrative ; si les ch. 11/28 veulent la réutiliser, l'ajouter au JSON.
33+- Reproduire la figure 47.5 (Monte-Carlo) et le tab. 47.16 au ch. 40 si celui-ci utilise le cas 3 : même graine, mêmes lois (`MC_LOIS`).
modified verification/claims.csv +37 −0
@@ -66,3 +66,40 @@ CLM-30-14,30,"Loi sur l'intérêt (L.R.C. 1985, c. I-15, art. 6) : taux hypothé
66 CLM-30-15,30,Droits sur les mutations immobilières payés par l'acquéreur (Québec),SRC-A-021,"clé bib quebecMutations ; Loi concernant les droits sur les mutations immobilières (D-15.1), art. sur le débiteur du droit non relu dans ce lot",2026-10-31,partiellement_verifie,vérifier l'article qui désigne l'acquéreur comme débiteur 66 CLM-30-15,30,Droits sur les mutations immobilières payés par l'acquéreur (Québec),SRC-A-021,"clé bib quebecMutations ; Loi concernant les droits sur les mutations immobilières (D-15.1), art. sur le débiteur du droit non relu dans ce lot",2026-10-31,partiellement_verifie,vérifier l'article qui désigne l'acquéreur comme débiteur
67 CLM-30-16,30,"SCHL APH Select : RCD minimal 1,10 pour le locatif ordinaire",SRC-C-040,Page APH Select publiée 2026-09-21 lue le 2026-10-31 (notes-C.md §4.4),2026-10-31,partiellement_verifie,cellules du tableau SCHL partiellement déduites (notes-C.md point 7) 67 CLM-30-16,30,"SCHL APH Select : RCD minimal 1,10 pour le locatif ordinaire",SRC-C-040,Page APH Select publiée 2026-09-21 lue le 2026-10-31 (notes-C.md §4.4),2026-10-31,partiellement_verifie,cellules du tableau SCHL partiellement déduites (notes-C.md point 7)
68 CLM-30-17,30,"Aucune enquête publique vérifiée ne publie de taux d'actualisation pour le multirésidentiel québécois ; le taux de 7,25 % du cas 3 est une hypothèse",,constat d'absence (notes-C,notes-E),2026-10-31,non_verifie 68 CLM-30-17,30,"Aucune enquête publique vérifiée ne publie de taux d'actualisation pour le multirésidentiel québécois ; le taux de 7,25 % du cas 3 est une hypothèse",,constat d'absence (notes-C,notes-E),2026-10-31,non_verifie
69+CLM-47-001,47,Définition OEAQ de la valeur marchande (« prix sincère le plus probable… marché libre et ouvert à la concurrence ») et quatre conditions,SRC-C-001,NPP p. Intro-11 lues (notes-C §1.1),2026-10-31,verifie,citation ≤ 25 mots ; conditions paraphrasées
70+CLM-47-002,47,Contenu minimal du rapport écrit : 12 éléments (règle 2.3) ; inspection obligatoire (règle 2.4) ; hypothèses extraordinaires rappelées à chaque conclusion (2.1 c et 2.3 él. 7) ; rejet d'une méthode justifié,SRC-C-001,NPP lues (notes-C §2.1),2026-10-31,verifie,
71+CLM-47-003,47,Code de déontologie : n'agir que pour une partie (art. 20) ; divulgation double rôle (18-19) ; honoraires conditionnels interdits (24.1) ; signature et qualité (34),SRC-C-003,LégisQuébec C-26 r. 123 à jour au 2026-07-01 (notes-C §2.2),2026-10-31,verifie,
72+CLM-47-004,47,IVS 102 annexe A10 : définition de la market value (édition en vigueur 31 janv. 2025),SRC-C-014,PDF officiel (notes-C §1.2),2026-10-31,verifie,
73+CLM-47-005,47,Art. 1937 C.c.Q. : l'aliénation ne permet pas au nouveau locateur de résilier le bail ; 1936 maintien dans les lieux,SRC-A-001,LégisQuébec à jour au 2026-10-15 (notes-A §4),2026-10-31,verifie,
74+CLM-47-006,47,Art. 1941-1945 et 1947 C.c.Q. : avis de modification 3 à 6 mois avant le terme ; refus dans le mois ; demande au TAL dans le mois,SRC-A-001,notes-A §4,2026-10-31,verifie,
75+CLM-47-007,47,Règlement sur les critères de fixation (D. 1455-2025 en vigueur 2026-01-01) : six critères art. 3 ; pourcentage de base IPC moyen art. 3.1 ; 5 % sur immobilisations ; base 2026 = 3,1 % ; 2027 non publié,SRC-A-003;SRC-A-004;SRC-E-048,LégisQuébec + Gazette + page TAL lues (notes-A §6.2),2026-10-31,verifie
76+CLM-47-008,47,La valeur locative de marché n'est pas un critère de fixation (résumé TAL Société de financement alouette c. A. 2022),SRC-A-001,résumés de décisions sur le site du TAL (notes-A §6.3) ; pas de référence neutre,2026-10-31,partiellement_verifie,source secondaire officielle (résumé) ; décision non lue
77+CLM-47-009,47,Art. 1896 et 1950 C.c.Q. : avis du loyer le plus bas des 12 mois (section G) ; nouveau locataire 10 jours pour demander la fixation,SRC-A-001,notes-A §4,2026-10-31,verifie,
78+CLM-47-010,47,Loi 31 (2024 c. 2) : art. 1978.2 refus de cession pour motif autre que sérieux → bail résilié ; 1978.3 aucune contrepartie,SRC-A-001;SRC-A-013,notes-A §4,2026-10-31,verifie,
79+CLM-47-011,47,Loi 65 (2024 c. 23) art. 1 : aucune éviction (art. 1959) avant le 6 juin 2027 ; art. 1959.1 protection 65 ans/10 ans/125 % ; art. 1965 indemnité 1 mois par année entre 3 et 24 mois,SRC-A-001;SRC-A-014,notes-A §5,2026-10-31,verifie,aucun avis de fin anticipée repéré au 2026-10-31
80+CLM-47-012,47,Art. 1904 C.c.Q. : aucun dépôt ; art. 947 pleine propriété,SRC-A-001,notes-A,2026-10-31,verifie,
81+CLM-47-013,47,Formulaire DV de l'OACIQ obligatoire seulement pour les immeubles résidentiels de moins de 5 logements,SRC-A-026;SRC-A-028,notes-A §2.4,2026-10-31,verifie,
82+CLM-47-014,47,Loi sur le TAL art. 51-54.13 : conversion en copropriété divise soumise à autorisation ; interdite dans l'agglomération de Montréal sauf dérogation,SRC-A-002,notes-A §6.1,2026-10-31,verifie,
83+CLM-47-015,47,LAU art. 113 al. 2 par. 18° : pouvoir de régir les usages dérogatoires protégés par droits acquis,SRC-B-0xx (LAU),notes-B ch. 8,2026-10-31,verifie,id registre LAU à compléter par l'orchestrateur
84+CLM-47-016,47,LFM art. 43 (valeur réelle) ; art. 45.1 vendeur réputé détenir les droits du locataire ; art. 46 date de référence = 1er juillet du 2e exercice précédent (rôle 2026-2028 → 2024-07-01),SRC-B-001 (LFM),notes-B §9.2,2026-10-31,verifie,
85+CLM-47-017,47,Rôle 2026-2028 de Montréal : déposé 2025-09-10 ; agglomération +12,2 % ; résidentiel +9,8 % ; 6 logements et plus +10,9 % ; facteur comparatif 1,00,SRC-E-051;SRC-E-052;SRC-B-031;SRC-B-030
86+CLM-47-018,47,Taxe foncière générale résidentielle Montréal 2026 : 0,4631 $/100 $ ; hausse moyenne 3,8 % ; Rosemont 5,3 %,SRC-E-031;SRC-B-024;SRC-B-023,Le Devoir 2026-01-12 (document officiel GED non lisible)
87+CLM-47-019,47,Taux « 6 logements et plus » de Montréal et taxe d'arrondissement Rosemont 2026,—,document « Taux de taxes 2026 » non accessible par script,2026-10-31,non_verifie,normalisation au taux général ; sensibilité ±1 006 $ (ex. 47.2)
88+CLM-47-020,47,Taxe scolaire 2026-2027 : 0,07899 $/100 $ (exemption des premiers 25 000 $),SRC-E-026,communiqué CSS (avis officiel G.O. non ouvert),2026-10-31,verifie
89+CLM-47-021,47,Droits de mutation Montréal 2026 : 7 tranches 0,5 % ≤ 62 900 ; 1 % ≤ 315 000 ; 1,5 % ≤ 552 300 ; 2 % ≤ 1 104 700 ; 2,5 % ≤ 2 136 500 ; 3,5 % ≤ 3 113 000 ; 4 % au-delà ; base = max(prix,contrepartie
90+CLM-47-022,47,SCHL oct. 2025 : zone Rosemont loyers 1 ch. 1 055 / 2 ch. 1 348 / 3 ch.+ 1 750 / tous 1 237 ; univers 44 554 ; RMR inoccupation 2,9 % (2,1 %) ; 6-19 log. île 2,2 % et 1 177 $ ; variation 7,4 % / 7,2 % ; rotation 9
91+CLM-47-023,47,SCHL Perspectives été 2026 : inoccupation RMR Montréal 3,8 % (2026) 4,5 % (2027) 4,8 % (2028),SRC-C-0xx (schlpmh2026ete),notes-C §4.6
92+CLM-47-024,47,APCIQ T3 2026 : plex Rosemont 950 000 $ (+2 %) ; île 900 000 $ ; RMR 860 500 $,SRC-E-001;SRC-E-004,communiqué et baromètre (notes-E),2026-10-31,verifie,attribution APCIQ/Centris requise
93+CLM-47-025,47,CBRE T3 2026 Montréal low-rise B 4,25-4,75 % (en hausse) ; écart avec obligation 10 ans,SRC-E-045,rapport CBRE publié 2026-10-21,2026-10-31
94+CLM-47-026,47,Altus : 2,9 G$ investis en multirésidentiel à Montréal au S1 2026 (+30 %),SRC-E-0xx (altusmontreal2026q2),notes-E ch. 29,2026-10-31,verifie
95+CLM-47-027,47,Banque du Canada : taux directeur 2,25 % du 2025-10-30 au 2026-10-28 puis 2,50 % ; obligations 5 ans 3,58 % et 10 ans 3,84 % (2026-10-29) ; hypothécaire 5 ans affiché 6,19 %
96+CLM-47-028,47,Fonds avancés prêts assurés fixe ≥ 5 ans : 4,12 % (août 2026),SRC-E-012,Valet A4_RATES_MORTGAGES,2026-10-31,verifie
97+CLM-47-029,47,Loi sur l'intérêt L.R.C. 1985 c. I-15 art. 6 : taux exprimé en composition semestrielle ou annuelle,—,docstring finance.py et annexe conventions ; texte fédéral non consulté dans les lots A-E,2026-10-31,partiellement_verifie,à vérifier sur laws-lois.justice.gc.ca
98+CLM-47-030,47,BSIF : taux admissible minimal non assuré = max(taux + 2 %,5,25 %),SRC-C-035 (osfiMqr),page BSIF modifiée 2026-01-29 (notes-C §4.2),2026-10-31
99+CLM-47-031,47,APH Select : 50 points → RPV 85 % existant / amortissement 40 ans / RCD 1,10 ; 70 et 100 points → 95 % et 45/50 ans ; critère abordabilité 40 % des logements ≤ 30 % du revenu médian des locataires ; engagement 10 ans,SRC-C-040,page SCHL 2026-09-21 (notes-C §4.4),2026-10-31,partiellement_verifie
100+CLM-47-032,47,Primes SCHL immeubles collectifs : 2,60 % ≤ 65 % ; 2,85 ; 3,35 ; 4,35 % ≤ 80 % ; 5,35 % ≤ 85 % ; surprime 0
101+CLM-47-033,47,SCHL Frais d'exploitation Québec (avril 2015) : gestion 3 à 5 % du revenu brut selon la taille ; entretien 500 $/log. ; TAL forfait gestion 5 %,SRC-E-047;SRC-E-048,document hébergé par un tiers ; formulaire RN,2026-10-31,partiellement_verifie,document daté (2015) à titre indicatif
102+CLM-47-034,47,IPC Québec +3,1 % (sept. 2026) ; tarif électricité domestique +3,0 % (2026),SRC-E-0xx (statcanipc ; Régie D-2026-033),notes-E ch. 27-28,2026-10-31
103+CLM-47-035,47,Teranet–BN Montréal +1,4 % a/a (sept. 2026),SRC-C-0xx (teranet202609),notes-C §4.9,2026-10-31,verifie
104+CLM-47-036,47,Réserve de remplacement 650-675 $/logement et gestion imputée 4 % : conventions de l'ouvrage (aucune enquête officielle récente),—,data/synthetic/README.md ; notes-E ch. 28 (NON VÉRIFIÉ),2026-10-31,non_verifie,incohérence : docstring cas3.py « 650 $/logement » vs donnée 8 103 $ = 675
105+CLM-47-037,47,Vacance économique 3,9 % ; rotation 15 % ; croissance loyers marché 3,5 % ; r 7,25 % ; R_T 5,0 % ; coûts de vente 2,5 % ; ajustement état 3 %/point : hypothèses de l'auteur
modified verification/figures.csv +7 −0
@@ -18,3 +18,10 @@ ch30/ch30-fig-loyers.pdf,30,analysis/python/figures/ch30_loyers.py,data/syntheti
18 ch30/ch30-fig-flux.pdf,30,analysis/python/figures/ch30_flux.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"RNE, CAPEX, flux avant dette (barres) et VA cumulée + VA de la valeur terminale, cas 3 [données synthétiques]",verifie 18 ch30/ch30-fig-flux.pdf,30,analysis/python/figures/ch30_flux.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"RNE, CAPEX, flux avant dette (barres) et VA cumulée + VA de la valeur terminale, cas 3 [données synthétiques]",verifie
19 ch30/ch30-fig-surface.pdf,30,analysis/python/figures/ch30_surface.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"Heatmap V(r, R_T) du DCF du cas 3, 9 x 7 cellules, base encadrée [données synthétiques]",verifie 19 ch30/ch30-fig-surface.pdf,30,analysis/python/figures/ch30_surface.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"Heatmap V(r, R_T) du DCF du cas 3, 9 x 7 cellules, base encadrée [données synthétiques]",verifie
20 ch30/ch30-fig-tornade.pdf,30,analysis/python/figures/ch30_tornade.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"Diagramme en tornade de la valeur DCF du cas 3, 9 intrants [données synthétiques]",verifie 20 ch30/ch30-fig-tornade.pdf,30,analysis/python/figures/ch30_tornade.py,data/synthetic/cas3_montreal_*.csv;cas3_montreal_hypotheses_dcf.json,synthetique,"Diagramme en tornade de la valeur DCF du cas 3, 9 intrants [données synthétiques]",verifie
21+ch47/ch47-fig-etat-locatif.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_etat_locatif.csv,synthetique,Loyers contractuels vs loyers de marché des 12 logements [données synthétiques],verifie
22+ch47/ch47-fig-depenses.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_depenses.csv;cas3_montreal_hypotheses_dcf.json,synthetique,Structure des dépenses normalisées de première année par poste et nature [données synthétiques],verifie
23+ch47/ch47-fig-taux-extraits.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_comparables.csv,synthetique,Taux globaux extraits de 40 ventes selon la taille et l'état avec médiane et sujet [données synthétiques],verifie
24+ch47/ch47-fig-flux-dcf.pdf,47,analysis/python/figures/ch47_dossier_figures.py,data/synthetic/cas3_montreal_*.csv;hypotheses_dcf.json,synthetique,RNE CAPEX et flux avant dette sur 10 ans avec service de la dette [données synthétiques],verifie
25+ch47/ch47-fig-montecarlo.pdf,47,analysis/python/figures/ch47_dossier_figures.py,simulation 10 000 tirages graine 20261031 (ch47_dossier.monte_carlo),synthetique,Histogramme de la valeur simulée avec quantiles 5/50/95 DCF et capitalisation [données synthétiques],verifie
26+ch47/ch47-fig-tornade.pdf,47,analysis/python/figures/ch47_dossier_figures.py,ch47_dossier.tornade (quantiles 10/90 des lois déclarées),synthetique,Tornade de la valeur DCF selon sept intrants [données synthétiques],verifie
27+ch47/ch47-fig-rcd-scenarios.pdf,47,analysis/python/figures/ch47_dossier_figures.py,ch47_dossier.scenarios_renouvellement,synthetique,RCD par année sous quatre scénarios de taux au renouvellement [données synthétiques],verifie
21 28
Menu

Theme