SPB Git
9.0 KB · 144 lines python
1"""Tests du chapitre 30 — flux actualisés et valeur terminale (cas 3, données synthétiques).2Identités : VAN(TRI) = 0 ; VT = RNE_{T+1}/R_T × (1 − c_v) ; DCF = Σ VA + VA(VT) ; cohérence Gordon (DCF = RNE_1/(r − g)3sous croissance constante, quel que soit T) ; flux après dette = flux − SD ; valeurs citées dans manuscript/chapters/ch30.tex."""4import math, pytest5from traite import finance as fin6from cases import ch30_dcf as c78@pytest.fixture(scope="module")9def R():10    return c.base()1112# ------------------------------------------------------------------ valeurs citées (tolérances déclarées)1314def test_valeurs_citees_dcf(R):15    d = R["dcf"]; h = R["hyp"]16    assert d["valeur"] == pytest.approx(2_208_566, abs=1)            # valeur DCF17    assert d["va_flux"] == pytest.approx(662_410, abs=1)18    assert d["va_terminale"] == pytest.approx(1_546_156, abs=1)19    assert d["vt_brute"] == pytest.approx(3_193_168, abs=1)20    assert d["vt_nette"] == pytest.approx(3_113_338, abs=1)21    assert d["rne_T1"] == pytest.approx(159_658, abs=1)22    assert d["part_terminale"] == pytest.approx(0.700, abs=0.001)23    assert R["valeur_capitalisation"] == pytest.approx(2_037_503, abs=1)24    assert R["taux"]["mediane"] == pytest.approx(0.04415, abs=1e-6)25    assert R["base"]["rne"] == pytest.approx(89_956, abs=1)26    assert h["taux_actualisation"] == 0.0725 and abs(h["taux_terminal"] - 0.05) < 1e-12 and h["couts_vente"] == 0.025 and h["horizon_ans"] == 102728def test_tableau_dcf_identites(R):29    t = c.tableau_dcf(R); h = R["hyp"]; T = h["horizon_ans"]30    tt = t[t["annee"] <= T]31    assert len(t) == T + 132    # RNE = RBE − dépenses − réserve ; flux = RNE − CAPEX ; VA = flux × facteur ; Σ VA = va_flux33    assert (tt["rne"] - (tt["rbe"] - tt["depenses"] - tt["reserve"])).abs().max() < 1e-634    assert (tt["flux_avant_dette"] - (tt["rne"] - tt["capex"])).abs().max() < 1e-635    assert (tt["va"] - tt["flux_avant_dette"] * (1 + h["taux_actualisation"]) ** (-tt["annee"])).abs().max() < 1e-636    assert tt["va"].sum() == pytest.approx(R["dcf"]["va_flux"], rel=1e-12)37    assert tt["va_cumulee"].iloc[-1] == pytest.approx(R["dcf"]["va_flux"], rel=1e-12)38    assert tt["capex"].sum() == pytest.approx(85_000 + 40_000 + 120_000)39    assert (t["rbp"] <= t["rbp_marche"] + 1e-6).all()4041def test_valeur_terminale(R):42    d = R["dcf"]; h = R["hyp"]43    vt_b, vt_n = fin.valeur_terminale(d["rne_T1"], h["taux_terminal"], h["couts_vente"])44    assert vt_b == pytest.approx(d["rne_T1"] / h["taux_terminal"]) and vt_n == pytest.approx(vt_b * (1 - h["couts_vente"]))45    assert d["va_terminale"] == pytest.approx(vt_n * fin.facteur_va(h["taux_actualisation"], h["horizon_ans"]), rel=1e-12)46    # actualiser à T+1 au lieu de T serait une erreur de (1+r)^-1 : on vérifie l'ampleur citée (≈ 104 519 $)47    erreur = vt_n * fin.facteur_va(h["taux_actualisation"], h["horizon_ans"]) - vt_n * fin.facteur_va(h["taux_actualisation"], h["horizon_ans"] + 1)48    assert erreur == pytest.approx(104_519, abs=1)49    assert d["valeur"] == pytest.approx(d["va_flux"] + d["va_terminale"], rel=1e-12)5051# ------------------------------------------------------------------ TRI / VAN5253def test_van_au_tri_nulle_sans_levier(R):54    sl = c.dcf_sans_levier(R)55    assert sl["tri"] == pytest.approx(R["hyp"]["taux_actualisation"], abs=1e-9)   # acheter à V_0 rapporte exactement r56    assert abs(sl["van_au_taux"]) < 1e-457    assert abs(fin.van(sl["flux"], sl["tri"])) < 1e-458    assert sl["mirr"] == pytest.approx(R["hyp"]["taux_actualisation"], abs=1e-9)  # MIRR(r, r) = r quand VAN(r) = 059    assert 9.0 < sl["delai_recuperation"] < 10.06061def test_levier(R):62    lv = c.flux_levier(R); f = R["financement"]; h = R["hyp"]; T = h["horizon_ans"]63    hyp = f["hypotheque"]64    assert lv["service_dette"] == pytest.approx(hyp.paiement * 12)65    assert lv["mise_de_fonds"] == pytest.approx(R["dcf"]["valeur"] - f["pret"])66    assert lv["solde_final"] == pytest.approx(hyp.solde(T * 12))67    tab = lv["tableau"]68    assert (tab["flux_apres_dette"] - (tab["flux_avant_dette"] - tab["service_dette"])).abs().max() < 1e-669    assert tab["flux_fonds_propres"].iloc[-1] == pytest.approx(tab["flux_apres_dette"].iloc[-1] + R["dcf"]["vt_nette"] - lv["solde_final"])70    assert abs(fin.van(lv["flux"], lv["tri"])) < 1e-4                                 # VAN(TRI) = 071    assert lv["changements_signe"] == 5                                                 # flux non conventionnels (CAPEX 2027, 2032)72    assert lv["tri"] == pytest.approx(0.1122, abs=0.0005)73    assert lv["mirr"] == pytest.approx(0.1061, abs=0.0005)74    assert h["financement"]["taux"] < lv["mirr"] < lv["tri"]                            # MIRR entre taux de financement et TRI ici75    assert lv["tri"] > h["taux_actualisation"]                                          # levier positif76    assert lv["mise_de_fonds"] == pytest.approx(732_606, abs=1) and lv["solde_final"] == pytest.approx(1_295_148, abs=1)77    assert lv["rcd_1"] == pytest.approx(1.20, abs=1e-6)78    # composition semestrielle : i_m = (1 + i/2)^(1/6) − 1 < i/1279    assert lv["taux_mensuel"] == pytest.approx((1 + h["financement"]["taux"] / 2) ** (1 / 6) - 1) and lv["taux_mensuel"] < h["financement"]["taux"] / 1280    assert lv["taux_effectif_annuel_hyp"] == pytest.approx((1 + h["financement"]["taux"] / 2) ** 2 - 1)8182# ------------------------------------------------------------------ cohérence DCF ↔ capitalisation directe (Gordon)8384@pytest.mark.parametrize("T", [1, 5, 10, 25, 60])85def test_proposition_gordon(T):86    v = c.verification_gordon(noi1=100_000, r=0.0725, g=0.035, T=T)87    assert v["V_dcf"] == pytest.approx(v["V_gordon"], rel=1e-10)88    # la part de la valeur terminale décroît avec T et vaut ((1+g)/(1+r))^T89    assert v["part_terminale"] == pytest.approx(((1 + 0.035) / (1 + 0.0725)) ** T, rel=1e-9)9091def test_gordon_avec_couts_de_vente_brise_l_egalite():92    v = c.verification_gordon(noi1=100_000, r=0.0725, g=0.035, T=10, couts_vente=0.025)93    assert v["V_dcf"] < v["V_gordon"]94    assert v["V_gordon"] - v["V_dcf"] == pytest.approx(0.025 * v["V_gordon"] * ((1 + 0.035) / (1 + 0.0725)) ** 10, rel=1e-9)9596def test_taux_implicites(R):97    ti = c.taux_implicites(R); h = R["hyp"]98    assert ti["R_implicite_dcf"] == pytest.approx(R["base"]["rne"] / R["dcf"]["valeur"])99    assert ti["R_implicite_dcf"] == pytest.approx(0.0407, abs=0.0001)100    assert ti["g_implicite_terminal"] == pytest.approx(h["taux_actualisation"] - h["taux_terminal"])101    assert ti["croissance_rne_annualisee"] == pytest.approx(0.0590, abs=0.0005)102    assert ti["tri_si_paye_V_cap"] == pytest.approx(0.0825, abs=0.0005)103    assert ti["tri_si_paye_V_cap"] > h["taux_actualisation"]                 # payer moins que V_0 rapporte plus que r104    assert ti["ecart_dcf_cap"] == pytest.approx(171_063, abs=1)105    assert fin.taux_gordon(h["taux_actualisation"], h["croissance_loyers_marche"]) == pytest.approx(0.0375)106107# ------------------------------------------------------------------ sensibilité et scénarios108109def test_sensibilite_monotone(R):110    s = c.sensibilite_taux_terminal(R)111    assert (s["valeur"].diff().dropna() < 0).all()                       # V décroît en R_T112    assert s.loc[(s["taux_terminal"] - 0.05).abs() < 1e-9, "valeur"].iloc[0] == pytest.approx(R["dcf"]["valeur"])113    assert s["valeur"].iloc[0] == pytest.approx(2_481_417, abs=1) and s["valeur"].iloc[-1] == pytest.approx(2_006_893, abs=1)114    S = c.surface_r_Rt(R)115    assert (S.diff().dropna() < 0).values.all() and (S.T.diff().dropna() < 0).values.all()   # V décroît en r et en R_T116    G = c.surface_r_g(R)117    assert (G.T.diff().dropna() > 0).values.all()                                            # V croît avec g_marché118119def test_tornade_et_scenarios(R):120    t = c.tornade(R)121    assert t["intrant"].iloc[0].startswith("Croissance des loyers de marché")122    assert (t["amplitude"].diff().dropna() <= 1e-9).all()123    assert t["amplitude"].iloc[0] == pytest.approx(368_235, abs=1)124    s = c.scenarios(R).set_index("scenario")125    assert s.loc["Base", "valeur"] == pytest.approx(R["dcf"]["valeur"])126    assert s.loc["Base", "tri_sans_levier_prix_base"] == pytest.approx(0.0725, abs=1e-9)127    assert s.loc["Encadrement serré", "valeur"] == pytest.approx(1_912_046, abs=1)128    assert s.loc["Hausse des taux", "valeur"] == pytest.approx(1_947_889, abs=1)129    assert s.loc["Encadrement serré", "tri_sans_levier_prix_base"] < 0.0725 and s.loc["Hausse des taux", "tri_sans_levier_prix_base"] < 0.0725130    assert s.loc["Hausse des taux", "rcd_min"] < 1.2131132def test_exercices_coherence():133    e = c.exercices()134    assert e["ex1"]["R"] == pytest.approx(0.0375)135    assert e["ex2"]["va_10"] - e["ex2"]["va_11_erronee"] == pytest.approx(e["ex2"]["ecart"])136    f5 = e["ex3"]137    assert f5["valeur"] == pytest.approx(f5["va_flux"] + f5["va_vt"])138    assert f5["van_prix"] < 0 and f5["tri_prix"] < 0.07139    assert e["ex4"]["i_m_can"] < e["ex4"]["i_m_us"] and e["ex4"]["paiement_can"] < e["ex4"]["paiement_us"]140    for T, v in e["ex5"].items():141        assert v["V_dcf"] == pytest.approx(v["V_gordon"], rel=1e-10)142    assert e["ex6"]["valeur"] == pytest.approx(1_986_966, abs=1)143    assert abs(fin.van(e["ex7"]["flux"], e["ex7"]["tri"])) < 1e-4144
Files · b39d09a4fc988c2cb0fb8ba9c86d745a81de3752
Menu

Theme