CoolFace
Apppublic

gl29/kervent-projections

sourceHugging Faceupdated 6mo agoView on Hugging Face
0likes
test_entity_simulation.py287 linesDownload Raw Back to tests
1# test_entity_simulation.py2import json3from pathlib import Path4 5import numpy as np6 7from core import GlobalConfig, SimContext, run_entity_simulation8from ledger import (9    BaseLedgerConfig,10    CapExItem,11    CapExLedger,12    DebtServiceModel,13    OpCoEntity,14    OpExLedger,15    PersonEntity,16    Phase,17    RevenueModel,18    RevenueStream,19    SCIEntity,20    TaxModel,21    TaxTables,22)23 24 25def _ctx(max_t: int = 12, vat_franchise_active: bool = True) -> SimContext:26    return SimContext(27        max_t=max_t,28        calendar_months=np.arange(max_t) % 12,29        global_cfg=GlobalConfig(30            inflation_rate=0.0,31            tax_tables=TaxTables(),32            vat_franchise_active=vat_franchise_active,33        ),34    )35 36 37def test_defaults_profile_runs_smoke() -> None:38    defaults_path = Path(__file__).resolve().parents[1] / "defaults.json"39    defaults = json.loads(defaults_path.read_text())40    ctx = SimContext(41        max_t=24,42        calendar_months=np.arange(24) % 12,43        global_cfg=GlobalConfig(44            inflation_rate=defaults["global"].get("inflation_rate", 0.0),45            tax_tables=TaxTables.from_dict(defaults["global"].get("tax_tables", {})),46            vat_franchise_active=defaults["global"].get("vat_franchise_active", True),47        ),48    )49    base_configs = {50        mid: BaseLedgerConfig.from_dict(model["config"])51        for mid, model in defaults["models"].items()52    }53    phases = [Phase.from_dict(data) for data in defaults["phases"]]54    result = run_entity_simulation(55        ctx,56        phases,57        base_configs,58        SCIEntity.from_dict(defaults["entities"]["sci"]),59        OpCoEntity.from_dict(defaults["entities"]["opco"]),60        [PersonEntity.from_dict(data) for data in defaults["entities"]["persons"]],61    )62 63    assert len(result.consolidated_treasury) == 2464    assert len(result.sci_treasury) == 2465    assert len(result.opco_treasury) == 2466    assert result.person_treasuries67 68 69def test_sci_ir_tax_is_allocated_to_people_not_sci_cash() -> None:70    ctx = _ctx()71    model = BaseLedgerConfig(72        revenue=RevenueModel(73            streams=[74                RevenueStream(75                    name="rentable",76                    price=10000.0,77                    base_occ=1.0,78                    peak_occ=1.0,79                    occ_curve_type="flat",80                    units=1,81                )82            ]83        ),84        capex=CapExLedger(),85        opex=OpExLedger(),86        tax=TaxModel(),87    )88    phase = Phase(89        id="phase_1",90        name="Launch",91        trigger={"type": "immediate"},92        new_loan=DebtServiceModel(),93        active_models=["model"],94        model_configs={},95    )96    sci = SCIEntity(97        tax_regime="ir",98        monthly_rent=5000.0,99        dividend_reserve=1_000_000_000.0,100        initial_capital=100_000.0,101    )102    person = PersonEntity(103        name="Family",104        ownership_share=1.0,105        living_expenses=OpExLedger(),106    )107 108    result = run_entity_simulation(109        ctx,110        [phase],111        {"model": model},112        sci,113        OpCoEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),114        [person],115    )116 117    assert result.annual_sci_tax == 0.0118    assert result.annual_person_tax["Family"] > 0.0119    # sci ends with initial + 12 * 5000 = 100000 + 60000120    assert result.sci_treasury[-1] == 160000.0121 122 123def test_loan_entity_sci_moves_capex_and_depreciation_to_sci() -> None:124    ctx = _ctx()125    model = BaseLedgerConfig(126        revenue=RevenueModel(),127        capex=CapExLedger(128            items=[CapExItem(name="equip", amount=10000.0, useful_life_years=5)]129        ),130        opex=OpExLedger(),131        tax=TaxModel(),132    )133 134    sci_result = run_entity_simulation(135        ctx,136        [137            Phase(138                id="phase_1",139                name="Launch",140                trigger={"type": "immediate"},141                new_loan=DebtServiceModel(bank_pct=0.8, interest=0.05, loan_years=5),142                active_models=["model"],143                model_configs={},144                loan_entity="SCI",145            )146        ],147        {"model": model},148        SCIEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),149        OpCoEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),150        [],151    )152    opco_result = run_entity_simulation(153        ctx,154        [155            Phase(156                id="phase_1",157                name="Launch",158                trigger={"type": "immediate"},159                new_loan=DebtServiceModel(bank_pct=0.8, interest=0.05, loan_years=5),160                active_models=["model"],161                model_configs={},162                loan_entity="OpCo",163            )164        ],165        {"model": model},166        SCIEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),167        OpCoEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),168        [],169    )170 171    assert sci_result.opco_capex[0] == 0.0172    assert sci_result.sci_treasury[0] < 100_000.0173    assert float(np.sum(sci_result.sci_debt[:12])) > 0.0174    assert float(np.sum(sci_result.sci_depreciation[:12])) > 0.0175    assert float(np.sum(sci_result.opco_loan_payments[:12])) == 0.0176 177    assert opco_result.opco_capex[0] > 0.0178    assert float(np.sum(opco_result.opco_loan_payments[:12])) > 0.0179    assert float(np.sum(opco_result.sci_depreciation[:12])) == 0.0180 181 182def test_dscr_counts_opco_debt_service() -> None:183    ctx = _ctx()184    model = BaseLedgerConfig(185        revenue=RevenueModel(186            streams=[187                RevenueStream(188                    name="rev",189                    price=2000.0,190                    base_occ=1.0,191                    peak_occ=1.0,192                    occ_curve_type="flat",193                    units=1,194                )195            ]196        ),197        capex=CapExLedger(198            items=[CapExItem(name="equip", amount=12000.0, useful_life_years=5)]199        ),200        opex=OpExLedger(),201        tax=TaxModel(),202    )203    result = run_entity_simulation(204        ctx,205        [206            Phase(207                id="phase_1",208                name="Launch",209                trigger={"type": "immediate"},210                new_loan=DebtServiceModel(bank_pct=1.0, interest=0.12, loan_years=1),211                active_models=["model"],212                model_configs={},213                loan_entity="OpCo",214            )215        ],216        {"model": model},217        SCIEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),218        OpCoEntity(dividend_reserve=1_000_000_000.0, initial_capital=100_000.0),219        [],220    )221 222    assert float(np.sum(result.opco_loan_payments[:12])) > 0.0223    assert result.dscr < 99.9224 225 226def test_delayed_baseline_capex_creates_real_sci_cash_outflow() -> None:227    ctx = _ctx(vat_franchise_active=True)228    baseline_capex = CapExLedger(229        items=[230            CapExItem(231                name="delayed", amount=10000.0, start_delay=6, useful_life_years=5232            )233        ]234    )235    sci = SCIEntity(dividend_reserve=1_000_000_000.0)236    sci.capex = baseline_capex237    result = run_entity_simulation(238        ctx,239        [240            Phase(241                id="phase_1",242                name="Launch",243                trigger={"type": "immediate"},244                new_loan=DebtServiceModel(bank_pct=0.8, interest=0.05, loan_years=5),245                active_models=[],246                model_configs={},247            )248        ],249        {},250        sci,251        OpCoEntity(dividend_reserve=1_000_000_000.0),252        [],253    )254 255    assert result.sci_treasury[5] == 0.0256    assert result.sci_treasury[6] < 0.0257    assert result.sci_debt[6] > 0.0258 259 260def test_external_income_increases_person_cash_before_tax() -> None:261    ctx = _ctx()262    person = PersonEntity(263        name="Family",264        external_monthly_income=1000.0,265        living_expenses=OpExLedger(),266    )267    result = run_entity_simulation(268        ctx,269        [270            Phase(271                id="phase_1",272                name="Launch",273                trigger={"type": "immediate"},274                new_loan=DebtServiceModel(),275                active_models=[],276                model_configs={},277            )278        ],279        {},280        SCIEntity(dividend_reserve=1_000_000_000.0),281        OpCoEntity(dividend_reserve=1_000_000_000.0),282        [person],283    )284 285    assert result.person_treasuries["Family"][-1] > 11000.0286    assert result.annual_person_tax["Family"] > 0.0287