Structurer un modèle

L’architecture est une décision à prendre avant de multiplier les fichiers. Elle détermine où les calculs vivent, comment les données circulent et quelle partie examiner lorsqu’un résultat paraît étrange. Pour beaucoup de modèles financiers, séparer le modèle métier, les contrôleurs qui préparent et font circuler les données, la présentation et les fonctions partagées est un bon point de départ.

Pourquoi séparer les responsabilités dans un modèle financier ?

Le modèle-vue-contrôleur (MVC) distingue le calcul, l’affichage et le pilotage des données. Les variantes sont nombreuses ; l’objectif n’est pas de suivre un diagramme à la lettre, mais de donner à chaque module une tâche claire.

Dans une analyse de marge, le contrôleur choisit la période et les séries à utiliser, le modèle applique la formule, et la vue présente la série obtenue. Quand les mêmes calculs servent à un carnet, à un rapport et à une application, cette séparation évite de réécrire la règle métier dans chaque interface.

Schéma — les données passent par le pilotage, les calculs et la présentation, avec des outils partagés
les données passent par le pilotage, les calculs et la présentation, avec des outils partagés

Couche de données

Dans cette architecture, la couche de données correspond au modèle métier : elle transforme des entrées financières en résultats. Une fonction de ratio devrait accepter des nombres ou des séries déjà sélectionnés, sans connaître le nom d’une feuille Excel ni l’API qui a fourni les chiffres. Elle reste ainsi réutilisable avec plusieurs sources.

Pour la marge brute, supposons que le coût des ventes soit stocké comme un montant positif. La formule est alors : (chiffre d’affaires − coût des ventes) / chiffre d’affaires. Dans cet exemple, un chiffre d’affaires nul déclenche une erreur explicite plutôt qu’une division invalide.

import pandas as pd

def calculer_marge_brute(
    chiffre_affaires: pd.Series,
    cout_des_ventes: pd.Series,
) -> pd.Series:
    """Calcule la marge brute en proportion du chiffre d’affaires."""
    if (chiffre_affaires == 0).any():
        raise ValueError("Le chiffre d’affaires ne peut pas être nul.")
    return (chiffre_affaires - cout_des_ventes) / chiffre_affaires

Les colonnes métier peuvent être réparties par domaine : par exemple, les ratios de rentabilité dans un module, la liquidité dans un autre et les mesures de risque dans un troisième. Cette organisation devient utile quand les fonctions d’une même famille partagent un vocabulaire et des entrées.

Les fonctions de calcul simples sont faciles à contrôler : on peut placer la formule métier à côté du code et vérifier plusieurs petits exemples. Elles méritent donc une attention particulière avant qu’elles ne soient utilisées dans de nombreux rapports.

Couche de visualisation

La vue transforme un résultat en tableau, graphique ou rapport. Elle devrait se concentrer sur la présentation : titre, unités, périodes, échelle et légende. Elle n’a pas besoin de recalculer la marge si le modèle l’a déjà produite.

Un tableau de résultats pandas peut suffire comme première vue. Quand le graphique apporte un éclairage supplémentaire, le code de tracé peut être isolé dans une fonction de visualisation :

def tracer_marge(marge: pd.Series):
    axe = marge.plot(kind="bar", color="#557c68")
    axe.set_title("Marge brute par trimestre")
    axe.set_xlabel("Période")
    axe.set_ylabel("Part du chiffre d’affaires")
    return axe

La fonction renvoie l’objet du graphique afin que le rapport appelant puisse ajouter une source, choisir la taille ou enregistrer l’image. Une vue dédiée reste facultative si un tableau clair répond déjà au besoin.

Couche de contrôle

Le contrôleur relie les données spécifiques au projet aux fonctions génériques du modèle. Il peut choisir l’entreprise, les périodes et les colonnes comptables, puis transmettre les séries au calcul de marge. Il coordonne également le choix d’un scénario ou l’enchaînement de plusieurs calculs.

Évitez de placer la formule elle-même dans le contrôleur. Si la même règle apparaît ici et dans un autre module, les deux versions peuvent diverger. Pour une petite application, une fonction d’orchestration suffit ; une classe de contrôleur est utile lorsqu’il faut conserver un état ou exposer plusieurs opérations cohérentes.

def marge_du_rapport(etats_financiers: pd.DataFrame) -> pd.Series:
    chiffre_affaires = etats_financiers["chiffre_affaires"]
    cout_des_ventes = etats_financiers["cout_des_ventes"]
    return calculer_marge_brute(chiffre_affaires, cout_des_ventes)

Ici, le contrôleur connaît les noms de colonnes du jeu de données ; la fonction de calcul, elle, ne les connaît pas. Cette frontière facilite la réutilisation avec une autre source de données.

Couche d’appui

Les fonctions d’appui rassemblent de petites opérations partagées par plusieurs modules : calcul de variation, conversion d’unités, validation de dates ou lecture d’un format courant. Elles évitent de copier la même logique à différents endroits.

Une fonction de croissance peut utiliser pandas après avoir décidé si elle compare une période à la précédente ou à un décalage plus grand :

def calculer_variation_relative(valeurs: pd.Series, decalage: int = 1) -> pd.Series:
    return valeurs.pct_change(periods=decalage, fill_method=None)

Ne faites pas de chaque ligne répétée une fonction d’appui. Placez-y une règle réellement partagée et documentez les choix, notamment le traitement des valeurs manquantes.

Structure et assistants IA

Une architecture reconnaissable aide un assistant à placer une modification au bon endroit. Une demande limitée — ajouter un calcul de marge dans le module métier et un test dans le dossier miroir — est plus facile à examiner qu’une consigne générale de refondre tout le modèle.

Pour relire un changement généré, demandez-vous :

  • La formule est-elle restée dans la couche de calcul ?
  • Les données et les périodes ont-elles été sélectionnées au bon endroit ?
  • Le modèle utilise-t-il une fonction d’appui déjà disponible ?
  • La présentation a-t-elle été modifiée sans changer le sens du calcul ?

Tout combiner

Un exemple de répartition des fichiers pourrait être :

src/rentabilite/
├── donnees.py
├── calculs/
│   └── marges.py
├── controles/
│   └── rapport.py
├── vues/
│   └── graphiques.py
└── outils.py

Avec des données fictives cohérentes, le modèle produit les résultats suivants :

TrimestreChiffre d’affairesCoût des ventesMarge brute
T11 000 €620 €0,380
T21 100 €660 €0,400
T3980 €610 €0,378

Les arrondis sont ici destinés à l’affichage. Le calcul conserve les valeurs précises, et la présentation décide combien de décimales montrer.

Avec cette structure en tête, le chapitre suivant porte sur les conventions de code qui rendent le modèle lisible : Construire un modèle.