SPB Git
10.6 KB · 193 lines python
1"""Tests du chapitre 7 — servitude par ventes appariées, quotes-parts et charges de copropriété, fonds de prévoyance, exercices.2Tolérances : 1 $ sur les montants cités arrondis au dollar ; 1e-9 sur les identités comptables ; 0,0001 sur les proportions."""3import sys, pathlib, math4import numpy as np, pytest5sys.path.insert(0, str(pathlib.Path(__file__).resolve().parents[1] / "cases"))6import ch07_servitudes_copro as cas7from traite import comparables as cp8from traite import finance as fin91011@pytest.fixture(scope="module")12def res():13    return cas.main()141516class TestServitude:17    def test_paires_citees(self, res):18        p = res["paires"]19        assert p["n"] == 1220        assert p["moyenne"] == pytest.approx(-20333, abs=1)21        assert p["mediane"] == pytest.approx(-19000, abs=1)22        assert p["ecart_type"] == pytest.approx(10026, abs=1)23        assert p["se"] == pytest.approx(2894, abs=1)24        assert p["t"] == pytest.approx(-7.03, abs=0.01)25        assert p["ic95"][0] == pytest.approx(-26703, abs=1) and p["ic95"][1] == pytest.approx(-13963, abs=1)26        assert p["effet_relatif_moyen"] == pytest.approx(-0.0451, abs=0.0001)27        assert p["effet_relatif_ratio"] == pytest.approx(-0.0446, abs=0.0001)28        assert p["effet_semilog"] == pytest.approx(-0.0454, abs=0.0001)29        assert p["sans"].mean() == pytest.approx(455792, abs=1)30        assert all(a < s for a, s in zip(p["avec"], p["sans"]))3132    def test_identites_paires(self, res):33        p = res["paires"]34        # la moyenne des différences est la différence des moyennes ; l'écart-type est celui de l'échantillon (n−1)35        assert p["moyenne"] == pytest.approx(p["avec"].mean() - p["sans"].mean(), abs=1e-9)36        assert p["ecart_type"] == pytest.approx(float(np.std(p["diffs"], ddof=1)), abs=1e-9)37        assert p["se"] == pytest.approx(p["ecart_type"] / math.sqrt(12), abs=1e-9)38        # cohérence avec traite.comparables39        va = cp.ventes_appariees(list(p["avec"]), list(p["sans"]))40        assert va["moyenne"] == pytest.approx(p["moyenne"]) and va["mediane"] == pytest.approx(p["mediane"])4142    def test_modele_bande(self, res):43        b = res["bande"]44        assert b["superficie_bande_m2"] == 120 and b["valeur_bande"] == 21600 and b["effet"] == 1080045        assert b["part_terrain"] == pytest.approx(120 / 700)464748class TestCopropriete:49    def test_quotes_parts(self, res):50        qp = res["qp"]51        assert list(qp["q_dixmilliemes"]) == [935, 1352, 974, 1408, 1196, 1644, 1186, 1305]52        assert int(qp["q_dixmilliemes"].sum()) == 10000 and qp["q"].sum() == pytest.approx(1.0, abs=1e-12)53        assert qp["superficies"].sum() == 71054        assert qp["valeur_relative"].sum() == pytest.approx(716.91, abs=0.01)55        # méthode déclarée : F6 = 110 × 1,04 × 1,0356        assert qp["valeur_relative"][5] == pytest.approx(110 * 1.04 * 1.03)5758    def test_repartition(self, res):59        rep = res["rep"]; qp = res["qp"]60        assert rep["courantes"] == 40000 and rep["auto_assurance"] == 500061        assert rep["general"] == pytest.approx(40000 + res["A_star"] + 5000)62        assert rep["par_fraction_general"].sum() == pytest.approx(rep["general"], abs=1e-6)63        assert rep["par_fraction_restreint"].sum() == pytest.approx(3200) and int(rep["n_stationnements"]) == 464        assert rep["par_fraction_total"].sum() == pytest.approx(rep["total"], abs=1e-6)65        assert rep["total"] == pytest.approx(70091, abs=1)66        # chiffres cités : F4 et F667        assert rep["par_fraction_total"][3] == pytest.approx(10218, abs=1) and rep["par_fraction_total"][5] == pytest.approx(11797, abs=1)68        assert rep["par_fraction_total"][0] == pytest.approx(6254, abs=1)69        assert rep["par_fraction_restreint"][3] == 800 and rep["par_fraction_restreint"][0] == 070        assert rep["par_fraction_fonds"][3] == pytest.approx(qp["q"][3] * res["A_star"])7172    def test_auto_assurance(self):73        assert cas.contribution_auto_assurance(10000, 0) == 500074        assert cas.contribution_auto_assurance(10000, 7000) == 300075        assert cas.contribution_auto_assurance(10000, 12000) == 0767778class TestFondsPrevoyance:79    def test_depenses(self, res):80        D = res["tr_star"]["D"]81        assert sum(c for _, c, a in cas.COMPOSANTES for _ in a) == 47900082        assert D.sum() == pytest.approx(651959, abs=1)83        assert D[14] == pytest.approx(140000 * 1.025 ** 14, abs=1e-6) and D[14] == pytest.approx(197816, abs=1)84        assert D[7] == pytest.approx(89151, abs=1)8586    def test_cotisation_seuil(self, res):87        A = res["A_star"]88        assert A == pytest.approx(21891, abs=1)89        tr = res["tr_star"]90        assert tr["min"] == pytest.approx(0.0, abs=0.01) and tr["annee_min"] == 1491        assert tr["F"][-1] == pytest.approx(239241, abs=1)92        assert tr["A"][-1] == pytest.approx(A * 1.025 ** 24, abs=1e-6)93        # identité de récurrence F_t = F_{t-1}(1+r) + A_t − D_t94        for t in range(1, cas.HORIZON + 1):95            assert tr["F"][t] == pytest.approx(tr["F"][t - 1] * 1.02 + tr["A"][t] - tr["D"][t], abs=1e-6)96        assert res["A_floor"] == pytest.approx(23228, abs=1)9798    def test_scenarios(self, res):99        act, regle = res["tr_act"], res["tr_regle"]100        assert res["regle_0_5"] == 21000101        assert act["premiere_annee_negative"] == 13 and act["min"] == pytest.approx(-233300, abs=1) and act["annee_min"] == 22102        assert act["F"][-1] == pytest.approx(-182783, abs=1)103        assert regle["premiere_annee_negative"] == 14 and regle["min"] == pytest.approx(-16660, abs=1)104105    def test_toiture_sff(self, res):106        fc = res["toiture"]107        assert fc["cout_indexe"] == pytest.approx(89151, abs=1)108        assert fc["sff"] == pytest.approx(fin.facteur_fonds_amortissement(0.02, 7)) and fc["sff"] == pytest.approx(0.13451, abs=1e-5)109        assert fc["versement"] == pytest.approx(11992, abs=1)110        # le versement accumule exactement le coût indexé en 7 ans à 2 %111        acc = sum(fc["versement"] * 1.02 ** (7 - k) for k in range(1, 8))112        assert acc == pytest.approx(fc["cout_indexe"], abs=1e-6)113114    def test_sensibilite(self, res):115        s = res["sens"]116        g = s["grille"]117        assert g[0.02][2] == pytest.approx(res["A_star"], abs=1)118        assert g[0.01][0] == pytest.approx(21683, abs=1) and g[0.01][4] == pytest.approx(24442, abs=1)119        assert g[0.03][0] == pytest.approx(19342, abs=1) and g[0.03][4] == pytest.approx(22099, abs=1)120        # monotonie : A* croît avec π et décroît avec r121        for r in s["rendements"]:122            assert all(np.diff(g[r]) > 0)123        for j in range(len(s["inflations"])):124            assert g[0.01][j] > g[0.02][j] > g[0.03][j]125126    def test_incidence_valeur(self, res):127        inc = res["incidence"]; qp = res["qp"]128        assert inc["solde_requis"] == pytest.approx(235908, abs=1) and inc["deficit"] == pytest.approx(150908, abs=1)129        assert inc["ajustement_par_fraction"][3] == pytest.approx(-21248, abs=1)130        assert inc["ajustement_par_fraction"][5] == pytest.approx(-24809, abs=1)131        assert inc["ajustement_par_fraction"].sum() == pytest.approx(-inc["deficit"], abs=1e-6)132        assert cas.trajectoire(cas.COTISATION_ACTUELLE, inc["solde_requis"])["min"] == pytest.approx(0.0, abs=0.01)133134135class TestExercices:136    def test_ex1_tarifs(self):137        # 1 fiche + 6 actes + 1 plan à 1 $, état certifié 16 $ + 16 $ pour 1 réquisition, copie certifiée 42 $138        assert 8 * 1 + (16 + 16) + 42 == 82139140    def test_ex2_quotes_parts(self):141        s = np.array([65.0, 80.0, 95.0, 120.0]); q = s / s.sum()142        assert q.sum() == pytest.approx(1) and q[3] == pytest.approx(1 / 3)143        charges = q * 54000144        assert charges[0] == pytest.approx(9750) and charges[3] == pytest.approx(18000)145        # variante avec coefficient d'étage 1,05 pour la fraction de 120 m²146        v = s * np.array([1, 1, 1, 1.05]); q2 = v / v.sum()147        assert q2[3] == pytest.approx(126 / 366, abs=1e-9) and (q2 * 54000)[3] == pytest.approx(18590, abs=1)148149    def test_ex3_paires(self):150        sans = np.array([398000, 412500, 430000, 445000, 462000, 489500]); avec = np.array([384000, 395500, 416000, 423000, 448000, 466500])151        va = cp.ventes_appariees(list(avec), list(sans))152        assert va["moyenne"] == pytest.approx(-17333, abs=1) and va["mediane"] == pytest.approx(-15500, abs=1)153        assert va["ecart_type"] == pytest.approx(4179, abs=1)154        se = va["ecart_type"] / math.sqrt(6); t = 2.571155        assert se == pytest.approx(1706, abs=1)156        assert va["moyenne"] - t * se == pytest.approx(-21720, abs=2) and va["moyenne"] + t * se == pytest.approx(-12947, abs=2)157        assert va["moyenne"] / sans.mean() == pytest.approx(-0.0394, abs=0.0001)158159    def test_ex4_bande(self):160        effet = 4 * 35 * 165 * 0.6161        assert effet == pytest.approx(13860)162        assert effet / 20000 == pytest.approx(0.693, abs=0.001)163164    def test_ex5_toiture(self):165        cT = 75000 * 1.025 ** 7; sff = fin.facteur_fonds_amortissement(0.02, 7)166        assert cT == pytest.approx(89151, abs=1) and cT * sff == pytest.approx(11992, abs=1)167        # sans indexation, versement 10 088 $ ; déficit à l'an 7168        a0 = 75000 * sff169        assert a0 == pytest.approx(10088, abs=1)170        assert cT - 75000 == pytest.approx(14151, abs=1)171        # variante r = 3 %172        assert cT * fin.facteur_fonds_amortissement(0.03, 7) == pytest.approx(11635, abs=1)173174    def test_ex6_script(self):175        # l'exercice demande : A* avec r = 3 % et π = 3 % ; A* avec plancher 25 000 $ ; solde initial requis176        assert cas.cotisation_seuil(inflation=0.03, rendement=0.03) == pytest.approx(21409, abs=1)177        assert cas.cotisation_seuil(plancher=25000) == pytest.approx(23228, abs=1)178        assert cas.cotisation_seuil(solde0=150000) == pytest.approx(17304, abs=1)179        assert cas.cotisation_seuil(indexer=False) == pytest.approx(25625, abs=1)180        assert cas.trajectoire(cas.cotisation_seuil(indexer=False), indexer=False)["F"][-1] == pytest.approx(125996, abs=1)181        # F4 : ajustement −q4 D0 vs valeur actualisée (5 %) de la hausse de cotisation indexée sur 10 ans182        q4 = cas.quotes_parts()["q"][3]; h = q4 * (cas.cotisation_seuil() - cas.COTISATION_ACTUELLE)183        assert h == pytest.approx(1393, abs=1)184        pv = sum(h * 1.025 ** (t - 1) / 1.05 ** t for t in range(1, 11))185        assert pv == pytest.approx(11929, abs=1) and sum(h * 1.025 ** (t - 1) for t in range(1, 11)) == pytest.approx(15602, abs=1)186187    def test_ex7_indivision(self):188        V = 1050000.0189        part = V / 3190        assert part == pytest.approx(350000)191        assert part * (1 - 0.08) == pytest.approx(322000) and part * (1 - 0.15) == pytest.approx(297500)192        assert 350000 - 322000 == 28000 and 350000 - 297500 == 52500193
Files · 8cb0de6f5c8171b14f35a0fe50fcc68c0097664d
Menu

Theme