CoolFace
Apppublic

NicoSimu/Liquidite

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
app.py237 linesDownload Raw Back to root
1import gradio as gr2 3WEEKS_PER_MONTH = 52 / 12  # ~4.3334 5# -------------------------6# Helpers7# -------------------------8def euros(x: float) -> str:9    return f"{x:,.0f} €".replace(",", " ").replace(".", ",")10 11def pourcent(x: float, nd=2) -> str:12    return f"{x:.{nd}f} %"13 14def clamp(x: float, lo: float, hi: float) -> float:15    return max(lo, min(hi, x))16 17def f(x, default=0.0) -> float:18    try:19        return float(x)20    except Exception:21        return default22 23# ============================================================24# LOGIQUE "RECETTE" : je fixe le plafond interne -> trésorerie requise25# - Entrées : plafond interne par VL, fenêtre de stress (par défaut 52),26#             loyers via taux de rendement, décote et plafond,27#             financement adossé optionnel28# - Sortie : trésorerie minimale à détenir (montant + % AN)29# ============================================================30def calcul_tresorerie_minimale(31    actif_net_eur: float,32    plafond_interne_par_vl_pct_an: float,33    fenetre_stress_vl: int,34    inclure_loyers: bool,35    taux_rendement_loyers_pct: float,36    decote_loyers_pct: float,37    plafond_loyers_pct_an: float,38    inclure_financement_adosse: bool,39    mois_financement: int,40    decote_financement_pct: float,41):42    actif_net_eur = f(actif_net_eur)43    if actif_net_eur <= 0:44        return "Erreur : l'actif net doit être strictement positif."45 46    fenetre_stress_vl = max(1, int(fenetre_stress_vl))47 48    # 1) Demande maximale implicite (promesse client)49    cap = f(plafond_interne_par_vl_pct_an) / 100.050    sorties_max = actif_net_eur * cap * fenetre_stress_vl  # sorties cumulées sur la fenêtre51 52    # 2) Loyers : via taux de rendement, puis prorata fenêtre, décote, et plafond53    loyers_annualises = 0.054    loyers_bruts_fenetre = 0.055    loyers_retenus = 0.056 57    if inclure_loyers:58        taux = f(taux_rendement_loyers_pct) / 100.059        loyers_annualises = max(0.0, actif_net_eur * taux)60 61        mois_fenetre = fenetre_stress_vl / WEEKS_PER_MONTH62        loyers_bruts_fenetre = loyers_annualises * (mois_fenetre / 12.0)63 64        facteur_loyers = clamp(1.0 - f(decote_loyers_pct) / 100.0, 0.0, 1.0)65        loyers_net_fenetre = loyers_bruts_fenetre * facteur_loyers66 67        # plafond des loyers comptés (anti-"liquidité magique")68        if f(plafond_loyers_pct_an) > 0:69            plafond_loyers_eur = actif_net_eur * (f(plafond_loyers_pct_an) / 100.0)70            loyers_retenus = min(loyers_net_fenetre, plafond_loyers_eur)71        else:72            loyers_retenus = loyers_net_fenetre73 74    # 3) Financement adossé (optionnel)75    financement = 0.076    if inclure_financement_adosse and inclure_loyers and loyers_annualises > 0:77        m = int(clamp(int(mois_financement), 0, 12))78        facteur_fin = clamp(1.0 - f(decote_financement_pct) / 100.0, 0.0, 1.0)79        financement = loyers_annualises * (m / 12.0) * facteur_fin80 81    # 4) Trésorerie minimale = ce qui manque pour honorer les sorties max82    tresorerie_min = max(0.0, sorties_max - loyers_retenus - financement)83 84    # 5) Ratios & contribution des loyers85    sorties_pct = (sorties_max / actif_net_eur) * 100 if actif_net_eur > 0 else 0.086    treso_pct = (tresorerie_min / actif_net_eur) * 100 if actif_net_eur > 0 else 0.087 88    # part des loyers dans le "financement" des sorties (sur la fenêtre)89    ressources = tresorerie_min + loyers_retenus + financement90    part_loyers = (loyers_retenus / ressources) * 100 if ressources > 0 else 0.091    part_fin = (financement / ressources) * 100 if ressources > 0 else 0.092    part_treso = (tresorerie_min / ressources) * 100 if ressources > 0 else 0.093 94    # textes utiles95    plafond_loyers_txt = "Aucun" if f(plafond_loyers_pct_an) == 0 else (pourcent(plafond_loyers_pct_an, 1) + " de l'actif net")96 97    out = []98    out.append(f"Actif net : {euros(actif_net_eur)}")99    out.append(f"Fenêtre de stress : {fenetre_stress_vl} VL (~{fenetre_stress_vl / WEEKS_PER_MONTH:.2f} mois)")100    out.append("")101    out.append("1) Promesse de liquidité (entrée)")102    out.append(f"- Plafond interne : {pourcent(plafond_interne_par_vl_pct_an,3)} de l'actif net PAR VL")103    out.append(f"- Sorties maximales sur la fenêtre : {euros(sorties_max)} ({pourcent(sorties_pct)} de l'actif net)")104    out.append("")105    out.append("2) Hypothèses loyers (si activé)")106    out.append(f"- Taux de rendement (loyers / actif net) : {pourcent(taux_rendement_loyers_pct,2)}")107    out.append(f"- Loyers annualisés (dérivés) : {euros(loyers_annualises)}")108    out.append(f"- Loyers bruts sur la fenêtre : {euros(loyers_bruts_fenetre)}")109    out.append(f"- Décote loyers : {pourcent(decote_loyers_pct,1)}")110    out.append(f"- Plafond des loyers comptés : {plafond_loyers_txt}")111    out.append(f"- Loyers retenus (après prudence) : {euros(loyers_retenus)}")112    out.append("")113    out.append("3) Financement adossé (si activé)")114    out.append(f"- Financement retenu : {euros(financement)}")115    out.append("")116    out.append("✅ Résultat clé (ce que tu cherchais)")117    out.append(f"- Trésorerie minimale à détenir : {euros(tresorerie_min)} ({pourcent(treso_pct)} de l'actif net)")118    out.append("")119    out.append("Poids des composantes dans la couverture des sorties (sur la fenêtre) :")120    out.append(f"- Trésorerie : {pourcent(part_treso,1)}")121    out.append(f"- Loyers futurs : {pourcent(part_loyers,1)}")122    out.append(f"- Financement adossé : {pourcent(part_fin,1)}")123    out.append("")124    out.append("Lecture simple : si ta trésorerie réelle est en-dessous de ce montant, alors ta promesse (plafond interne) n'est pas tenable sans vente d'actifs ou restrictions.")125 126    return "\n".join(out)127 128# ============================================================129# UI130# ============================================================131with gr.Blocks(title="Liquidité ELTIF — recette (plafond -> trésorerie)") as demo:132    gr.Markdown(133        "# Liquidité ELTIF — recette lisible\n"134        "*Tu fixes la promesse de rachat (plafond interne). L’outil calcule la trésorerie minimale à détenir.*\n"135    )136 137    gr.Markdown("## Paramètres du fonds")138    gr.Markdown("*Ces champs décrivent la taille du fonds et l’horizon sur lequel tu veux tester la tenue en cas de stress.*")139 140    with gr.Row():141        an = gr.Number(142            label="Actif net (EUR)",143            value=500_000_000,144            precision=0,145            info="Valeur totale du fonds. Tous les ratios se rapportent à cette base."146        )147        cap = gr.Number(148            label="Plafond interne par VL (% de l'actif net)",149            value=0.2,150            precision=3,151            info="Promesse de rachat par VL (ex : 0,20%). La trésorerie minimale sera calculée en conséquence."152        )153        fen = gr.Slider(154            label="Fenêtre de stress (nombre de VL)",155            minimum=1,156            maximum=52,157            step=1,158            value=52,  # ✅ par défaut 52 comme tu veux159            info="Par défaut 52 VL (~12 mois), car c'est le test sérieux. Tu peux réduire si besoin."160        )161 162    gr.Markdown("## Loyers")163    gr.Markdown("*Au lieu d'un montant, on utilise un taux de rendement (loyers / actif net). Les loyers sont ensuite proratisés à la fenêtre, décotés, et plafonnés.*")164 165    with gr.Row():166        incl_loy = gr.Checkbox(167            label="Inclure les loyers",168            value=True,169            info="Si décoché, tu testes une version dure : seule la trésorerie compte."170        )171        taux_loy = gr.Slider(172            label="Taux de rendement des loyers (% de l'actif net par an)",173            minimum=0,174            maximum=15,175            step=0.1,176            value=7.0,177            info="Ex : 7% = loyers annualisés = 7% de l'actif net."178        )179 180    with gr.Row():181        dec_loy = gr.Slider(182            label="Décote loyers (%)",183            minimum=0,184            maximum=60,185            step=1,186            value=20,187            info="Marge de prudence (vacance, impayés, retards, renégociations)."188        )189        cap_loy = gr.Slider(190            label="Plafond des loyers comptés (% de l'actif net) — 0 = aucun",191            minimum=0,192            maximum=50,193            step=0.5,194            value=10.0,195            info="Anti-'liquidité magique' : limite la part de loyers futurs que tu acceptes de compter dans le test."196        )197 198    gr.Markdown("## Financement adossé aux loyers")199    gr.Markdown("*À activer uniquement si tu as une ligne réellement contractualisée et tirable en stress. Sinon, l’AMF considérera que ça ne vaut rien.*")200 201    with gr.Row():202        fin_on = gr.Checkbox(203            label="Inclure un financement adossé aux loyers",204            value=False,205            info="Active une ressource bancaire/financement court terme adossée aux loyers."206        )207        fin_mois = gr.Slider(208            label="Mois finançables (0..12)",209            minimum=0,210            maximum=12,211            step=1,212            value=6,213            info="Nombre de mois de loyers que tu considères monétisables via la ligne."214        )215        fin_dec = gr.Slider(216            label="Décote sur financement (%)",217            minimum=0,218            maximum=70,219            step=1,220            value=35,221            info="Décote prudente (covenants, clauses de marché, capacité réelle à tirer en crise)."222        )223 224    gr.Markdown("## Calcul")225    gr.Markdown("*Le résultat te donne la trésorerie minimale à détenir pour tenir ta promesse de rachat sur la fenêtre.*")226 227    btn = gr.Button("Calculer la trésorerie minimale à détenir")228    out = gr.Textbox(label="Résultat", lines=28)229 230    btn.click(231        calcul_tresorerie_minimale,232        inputs=[an, cap, fen, incl_loy, taux_loy, dec_loy, cap_loy, fin_on, fin_mois, fin_dec],233        outputs=out234    )235 236demo.launch()237