import math, pytest from traite import finance as f def test_taux_mensuel_canadien_connu(): # 6 % composé semestriellement → 0,49386 % par mois (valeur standard des tables canadiennes) assert f.taux_mensuel_canadien(0.06) == pytest.approx(0.0049386, abs=1e-7) assert f.taux_effectif_annuel(0.06, 2) == pytest.approx(0.0609, abs=1e-6) def test_facteurs_identites(): r, n = 0.05, 25 assert f.facteur_va_annuite(r, n) * f.facteur_recouvrement(r, n) == pytest.approx(1.0) # recouvrement = SFF + r assert f.facteur_recouvrement(r, n) == pytest.approx(f.facteur_fonds_amortissement(r, n) + r) assert f.facteur_va(r, n) * f.facteur_vf(r, n) == pytest.approx(1.0) assert f.facteur_va_annuite(0.0, 10) == 10 def test_van_au_tri_est_nulle(): flux = [-1_000_000, 65_000, 67_000, 69_000, 71_000, 1_250_000] r = f.tri(flux) assert f.van(flux, r) == pytest.approx(0.0, abs=1e-4) assert 0.09 < r < 0.12 def test_tri_sans_changement_de_signe(): with pytest.raises(ValueError): f.tri([100, 10, 10]) def test_mirr_bornes(): flux = [-100, 30, 40, 50, 60] m = f.tri_modifie(flux, 0.05, 0.05) assert 0.0 < m < f.tri(flux) + 1e-9 # avec réinvestissement à 5 % < TRI, le MIRR est plus bas def test_hypotheque_paiement_canadien_connu(): # 300 000 $ à 5 % composé semestriellement, 25 ans : paiement mensuel ≈ 1 744,81 $ (calculateurs canadiens) h = f.Hypotheque(300_000, 0.05, 300) assert h.paiement == pytest.approx(1744.81, abs=0.05) assert h.solde(300) == pytest.approx(0.0, abs=1e-6) tab = h.tableau() assert len(tab) == 300 and tab[-1]["solde"] == pytest.approx(0.0, abs=1e-6) assert sum(r["capital"] for r in tab) == pytest.approx(300_000, abs=1e-4) assert h.solde(60) == pytest.approx(tab[59]["solde"], abs=1e-4) assert h.interets_annee(1) + h.capital_annee(1) == pytest.approx(12 * h.paiement, abs=1e-6) def test_hypotheque_us_composition_mensuelle(): h = f.Hypotheque(200_000, 0.06, 360, composition=12) assert h.paiement == pytest.approx(1199.10, abs=0.01) def test_pret_max_rcd_coherent(): noi = 180_000 pret = f.pret_max_par_rcd(noi, 1.25, 0.05, 300) h = f.Hypotheque(pret, 0.05, 300) assert f.rcd(noi, h.service_dette_annuel) == pytest.approx(1.25) def test_rbe_rne_identites(): rbp = 240_000 rbe = f.revenu_brut_effectif(rbp, 0.04, 6_000) assert rbe == pytest.approx(240_000 * 0.96 + 6_000) assert f.rne(rbe, 90_000, 6_000) == pytest.approx(rbe - 96_000) def test_capitalisation_directe_identite(): v = f.capitalisation_directe(120_000, 0.05) assert v == 2_400_000 assert f.taux_global_extrait(120_000, v) == pytest.approx(0.05) with pytest.raises(ValueError): f.capitalisation_directe(1, 0) def test_bande_investissement_et_rcd(): h = f.Hypotheque(1.0, 0.05, 300) R = f.taux_bande_investissement(0.70, h.constante_hypothecaire, 0.09) assert R == pytest.approx(0.70 * h.constante_hypothecaire + 0.30 * 0.09) assert f.taux_par_rcd(1.25, 0.70, h.constante_hypothecaire) == pytest.approx(1.25 * 0.70 * h.constante_hypothecaire) def test_ellwood_egale_akerson_et_gordon(): h = f.Hypotheque(1.0, 0.055, 300) fconst = h.constante_hypothecaire P = 1 - h.solde(120) # part remboursée après 10 ans a = f.taux_ellwood(0.10, 0.70, fconst, P, 10, 0.20) b = f.taux_akerson(0.10, 0.70, fconst, P, 10, 0.20) assert a == pytest.approx(b, abs=1e-12) # sans dette (M = 0) et sans variation de valeur : R = Y_E assert f.taux_ellwood(0.10, 0.0, fconst, P, 10, 0.0) == pytest.approx(0.10) assert f.taux_gordon(0.08, 0.02) == pytest.approx(0.06) with pytest.raises(ValueError): f.taux_gordon(0.02, 0.03) def test_dcf_croissance_constante_egale_gordon(): # RNE croissant à g, actualisé à r, VT = RNE_{T+1}/(r−g) ⇒ V_0 = RNE_1/(r−g) exactement r, g, noi1, T = 0.08, 0.02, 100_000, 10 noi = f.projeter_croissance(noi1, g, T + 1) res = f.dcf(noi[:T], r, noi_suivant=noi[T], taux_terminal=r - g) assert res.valeur == pytest.approx(noi1 / (r - g), rel=1e-12) # avec coûts de vente, la valeur baisse de c_v × VA(VT) res2 = f.dcf(noi[:T], r, noi_suivant=noi[T], taux_terminal=r - g, couts_vente=0.03) assert res.valeur - res2.valeur == pytest.approx(0.03 * res.va_terminale, rel=1e-12) def test_levier_tri_fonds_propres(): prix = 2_400_000; noi = [120_000 * 1.02 ** k for k in range(10)] h = f.Hypotheque(0.65 * prix, 0.05, 300) out = f.rendement_fonds_propres_levier(noi, prix, h, 2_900_000 * 0.97) assert out["mise_de_fonds"] == pytest.approx(0.35 * prix) assert 0.05 < out["tri"] < 0.25 assert f.van(out["flux"], out["tri"]) == pytest.approx(0, abs=1e-3) def test_taux_admissible_minimal(): assert f.taux_admissible_minimal(0.045) == pytest.approx(0.065) assert f.taux_admissible_minimal(0.02) == pytest.approx(0.0525)