#!/usr/bin/env python3 """Figure ch03 — Modèle des quatre quadrants (DiPasquale–Wheaton) tracé à partir des paramètres pédagogiques du marché type (analysis/python/cases/ch03_cycles.py, SYNTHÉTIQUE). Équilibre de référence (trait plein) et nouvel équilibre après une baisse du taux de capitalisation de 5 % à 4 % (tirets), avec la réaction de court terme (stock fixe) en pointillé. Sortie : assets/figures/ch03/ch03-fig-quatre-quadrants.pdf""" import sys, pathlib ROOT = pathlib.Path(__file__).resolve().parents[3] sys.path.insert(0, str(ROOT / "analysis/python")); sys.path.insert(0, str(ROOT / "analysis/python/cases")) import numpy as np, matplotlib.pyplot as plt from traite.style import setup, palette, savefig, LARGEUR import ch03_cycles as cas def main(): setup() p = cas.Parametres4Q() base = cas.equilibre_4q(p) i2 = 0.04 nouv = cas.equilibre_4q(p, i=i2) ct = cas.court_terme_4q(p, i=i2) k = 1000.0 # affichage en milliers c0, c1 = palette.cat[0], palette.cat[1] fig, ax = plt.subplots(figsize=(LARGEUR, LARGEUR * 0.92)) # bornes Smax, Pmax, Rmax, Cmax = 150, 420, 25, 3.2 ax.set_xlim(-Pmax, Smax); ax.set_ylim(-Cmax, Rmax) ax.axhline(0, color=palette.ink, lw=0.9); ax.axvline(0, color=palette.ink, lw=0.9) ax.grid(False) for s in ("left", "bottom"): ax.spines[s].set_visible(False) ax.set_xticks([]); ax.set_yticks([]) # --- NE : marché de l'espace, D(R) = S → R = (a − S)/b S = np.linspace(60, 150, 50) * k ax.plot(S / k, (p.a - S) / p.b / k, color=c0, lw=1.8) ax.plot(S / k, (1.0 * p.a - S) / p.b / k, color=c0, lw=0, label="_") # --- NW : marché des actifs, P = R / i (P à gauche, R en haut) R = np.linspace(0, Rmax, 50) * k ax.plot(-R / p.i / k, R / k, color=c0, lw=1.8) ax.plot(-R / i2 / k, R / k, color=c1, lw=1.8, ls="--") # --- SW : construction, C = (P − c0)/c1 (P à gauche, C en bas) P = np.linspace(p.c0, Pmax * k, 50) ax.plot(-P / k, -(P - p.c0) / p.c1 / k, color=c0, lw=1.8) # --- SE : ajustement du stock, S = C/δ (C en bas, S à droite) C = np.linspace(0, Cmax, 50) * k ax.plot(C / p.delta / k, -C / k, color=c0, lw=1.8) def rectangle(eq, color, ls, lw=1.0): Sx, Rx, Px, Cx = eq["S"] / k, eq["R"] / k, eq["P"] / k, eq["C"] / k ax.plot([Sx, Sx], [0, Rx], color=color, lw=lw, ls=ls) ax.plot([-Px, Sx], [Rx, Rx], color=color, lw=lw, ls=ls) ax.plot([-Px, -Px], [Rx, -Cx], color=color, lw=lw, ls=ls) ax.plot([-Px, Sx], [-Cx, -Cx], color=color, lw=lw, ls=ls) ax.plot([Sx, Sx], [-Cx, 0], color=color, lw=lw, ls=ls) rectangle(base, c0, "-") rectangle(nouv, c1, "--") # court terme : stock fixe, prix bondit à R0/i2 ax.plot([-ct["P"] / k, base["S"] / k], [base["R"] / k, base["R"] / k], color=c1, lw=1.0, ls=":") ax.plot([-ct["P"] / k, -ct["P"] / k], [base["R"] / k, -ct["C"] / k], color=c1, lw=1.0, ls=":") ax.plot([-ct["P"] / k, base["S"] / k], [-ct["C"] / k, -ct["C"] / k], color=c1, lw=1.0, ls=":") # points ax.plot(base["S"] / k, base["R"] / k, "o", color=c0, ms=4); ax.plot(nouv["S"] / k, nouv["R"] / k, "o", color=c1, ms=4) ax.plot(-base["P"] / k, base["R"] / k, "o", color=c0, ms=4); ax.plot(-nouv["P"] / k, nouv["R"] / k, "o", color=c1, ms=4) ax.plot(-ct["P"] / k, base["R"] / k, "o", color=c1, ms=4, mfc="white") # étiquettes d'axes et de quadrants ax.text(Smax, 0.6, "Stock $S$ (milliers de log.)", ha="right", va="bottom", fontsize=8) ax.text(-Pmax, 0.6, "Prix $P$ (k\\$/log.)", ha="left", va="bottom", fontsize=8) ax.text(1.5, Rmax, "Loyer $R$ (k\\$/log./an)", ha="left", va="top", fontsize=8) ax.text(1.5, -Cmax, "Construction $C$ (milliers de log./an)", ha="left", va="bottom", fontsize=8) ax.text(Smax * 0.98, Rmax * 0.93, "Marché de l'espace\n$D(R) = S$", ha="right", va="top", fontsize=8, color=palette.ink2) ax.text(-Pmax * 0.98, Rmax * 0.93, "Marché des actifs\n$P = R/i$", ha="left", va="top", fontsize=8, color=palette.ink2) ax.text(-Pmax * 0.98, -Cmax * 0.93, "Construction\n$C = (P - c_0)/c_1$", ha="left", va="bottom", fontsize=8, color=palette.ink2) ax.text(Smax * 0.98, -Cmax * 0.93, "Ajustement du stock\n$\\Delta S = C - \\delta S = 0$", ha="right", va="bottom", fontsize=8, color=palette.ink2) # annotations numériques ax.annotate(f"$S^*$ = {base['S']/k:.0f}", xy=(base["S"] / k, 0), xytext=(base["S"] / k + 2, 1.2), fontsize=7.5, color=c0) ax.annotate(f"{nouv['S']/k:.1f}", xy=(nouv["S"] / k, 0), xytext=(nouv["S"] / k + 2, 3.4), fontsize=7.5, color=c1) ax.annotate(f"$R^*$ = {base['R']/k:.1f}", xy=(0, base["R"] / k), xytext=(3, base["R"] / k + 0.6), fontsize=7.5, color=c0) ax.annotate(f"{nouv['R']/k:.1f}", xy=(0, nouv["R"] / k), xytext=(3, nouv["R"] / k - 1.9), fontsize=7.5, color=c1) ax.annotate(f"$P^*$ = {base['P']/k:.0f}", xy=(-base["P"] / k, 0), xytext=(-base["P"] / k - 6, 1.2), fontsize=7.5, color=c0, ha="right") ax.annotate(f"court terme : {ct['P']/k:.0f}", xy=(-ct["P"] / k, 0), xytext=(-ct["P"] / k - 4, -0.9), fontsize=7.5, color=c1, ha="right", va="top") ax.annotate(f"$C^*$ = {base['C']/k:.1f}", xy=(0, -base["C"] / k), xytext=(3, -base["C"] / k - 0.1), fontsize=7.5, color=c0, va="top") from matplotlib.lines import Line2D h = [Line2D([], [], color=c0, lw=1.8, label="équilibre de référence ($i$ = 5 %)"), Line2D([], [], color=c1, lw=1.8, ls="--", label="nouvel équilibre de long terme ($i$ = 4 %)"), Line2D([], [], color=c1, lw=1.0, ls=":", label="court terme (stock fixe)")] ax.legend(handles=h, loc="lower center", bbox_to_anchor=(0.5, -0.06), ncol=3, fontsize=7.5, frameon=False) out = savefig(fig, "ch03/ch03-fig-quatre-quadrants.pdf"); print("OK", out) if __name__ == "__main__": main()