Aller au contenu

⚙️ Moteur de portefeuille — Modèle mathématique

💡 Vue d'ensemble

Cette page définit formellement le modèle mathématique qui sous-tend le moteur de calcul de portefeuille de LibreFolio. Toutes les autres pages de métriques (NAV, Valeur comptable, P&L période, Rendement sur coût, PRU, Capital versé) font référence à cette page pour leurs règles de calcul précises.


📐 1. Notation et ensembles

Symbole Signification
\(V(u)\) Tous les courtiers visibles par l'utilisateur \(u\)
\(S \subseteq V(u)\) Périmètre de courtiers sélectionné (filtré)
\(A\) Ensemble des actifs avec positions
\(C^*\) Devise cible
\([t_0, t_1]\) Cadre d'évaluation demandé
\(q(a,b,t)\) Quantité de l'actif \(a\) chez le courtier \(b\) à la date \(t\)
\(p(a,t)\) Prix de valorisation de l'actif \(a\) à la date \(t\)
\(\mathrm{fx}(c_1, c_2, t)\) Taux de change de la devise \(c_1\) vers \(c_2\) à la date \(t\)

📐 2. Prix de valorisation

\[ \operatorname{mark}(a,t)= \begin{cases} \text{MARKET}(a,t) & \text{cotation actif-système du jour}\\ \operatorname{avg}(\text{TRADE}(a,t)) & \text{observations ACHAT/VENTE/AJUSTEMENT valorisées du jour}\\ \text{dernière observation avant }t & \text{reportée (LOCF)}\\ \varnothing & \text{aucune observation à la date }t\text{ ou avant} \end{cases} \]
  • Le résolveur unifié est l'unique cerveau de valorisation : MARKET → TRADE_AVG → CARRIED → MISSING.
  • CARRIED correspond à la dernière observation reportée (LOCF). Il transporte les métadonnées d'obsolescence days_back.
  • estimated=True indique une origine TRADE. Une cotation MARKET reportée obsolète est obsolète, pas estimée.
  • Les valorisations restent en devise native ; chaque consommateur les convertit en \(C^*\) à la date de valorisation \(t\).
  • Le taux de change du coût de base reste fixé à la date de transaction.
  • Les liquidités restent dans la devise où elles ont été enregistrées, courtier par courtier, et sont valorisées au taux de \(t\) (§5) ; chaque mouvement de trésorerie reste comptabilisé, et chaque flux de trésorerie externe entre dans le capital, au taux de sa propre date.
  • Le PRU n'est jamais utilisé comme prix de valorisation.

Voir Résolution des prix pour le contrat du résolveur et la sémantique de qualité des données.


📐 3. État de position

Pour chaque position \((a, b)\) avec \(q(a,b,t) > 0\) :

\[ \mathrm{MV}(a,b,t) = \frac{q(a,b,t)}{qbq(a)}\cdot \operatorname{mark}(a,t)\cdot \mathrm{fx}\bigl(\mathrm{ccy}_{mark}, C^*, t\bigr) \]
\[ \mathrm{CB}(a,b,t) = q(a,b,t) \cdot w(a,b,t) \]
\[ \mathrm{UGL}(a,b,t) = \mathrm{MV}(a,b,t) - \mathrm{CB}(a,b,t) \]

Où \(w(a,b,t)\) est le Prix de revient unitaire (PRU) pour la position \((a,b)\) à la date \(t\), conservé en \(C^*\) aux taux historiques (§4) : le coût de base ne prend aucun taux de change à \(t\), donc pour un actif étranger, l'effet de taux de change fait partie de \(\mathrm{UGL}\).


📐 4. Mise à jour itérative du PRU

Maintenu par position \((a,b)\) avec l'état du pool \((\hat{q}, \hat{c})\), le coût \(\hat{c}\) conservé en \(C^*\) :

Acquisition (qté \(> 0\), montant \(P\) payé en devise \(c\) à la date \(d\)) :

\[ \hat{q}_{\text{new}} = \hat{q} + q_{\text{tx}}, \quad \hat{c}_{\text{new}} = \hat{c} + P \cdot \mathrm{fx}(c, C^*, d), \quad w = \frac{\hat{c}_{\text{new}}}{\hat{q}_{\text{new}}} \]

\(P\) est le montant en espèces payé pour un ACHAT, ou le coût de base unitaire imposé multiplié par \(q_{\text{tx}}\) pour un TRANSFERT ou AJUSTEMENT ; \(\mathrm{fx}(C^*, C^*, d) = 1\). Lorsqu'aucun taux n'existe à la date \(d\) ou avant, ou que l'acquisition n'a pas de coût de base, la quantité entre et le coût n'entre pas : le coût de la position est signalé comme incomplet au lieu d'être compté comme zéro.

Réduction (qté \(< 0\)) :

\[ w_{\text{pre}} = \frac{\hat{c}}{\hat{q}}, \quad \hat{q}_{\text{new}} = \hat{q} - |q_{\text{tx}}|, \quad \hat{c}_{\text{new}} = \hat{q}_{\text{new}} \cdot w_{\text{pre}} \]

Division (liée à un événement de division) : \(\hat{q}\) change, \(\hat{c}\) ne change pas.

Ordre

Au sein de la même date : les ajouts sont traités avant les réductions. Cela garantit que la VENTE lit le PRU correct, y compris les ACHATS du même jour.


📐 5. Agrégation du portefeuille

\[ \mathrm{MV}(t) = \sum_{(a,b) \in S} \mathrm{MV}(a,b,t) \]
\[ \mathrm{NAV}(t) = \mathrm{MV}(t) + \mathrm{Cash}(t) + \mathrm{InTransit}(t) \]
\[ \mathrm{Book}(t) = \mathrm{OCB}(t) + \mathrm{Cash}(t) + \mathrm{InTransitBook}(t) \]
\[ \mathrm{UGL}(t) = \mathrm{NAV}(t) - \mathrm{Book}(t) \]

\(\mathrm{Cash}(t)\) représente les liquidités de chaque courtier, conservées dans les devises où elles ont été enregistrées et valorisées au taux de \(t\) :

\[ \mathrm{Cash}(t) = \sum_{b \in S} \sum_{c} N_{b,c}(t) \cdot \mathrm{fx}(c, C^*, t) \]

où \(N_{b,c}(t)\) est le solde du courtier \(b\) dans la devise \(c\) après ses mouvements datés de \(t\) ou antérieurs, au prorata de la part de propriété du courtier, et \(\mathrm{fx}(c, C^*, t)\) est le dernier taux connu à la date \(t\) ou avant (\(1\) pour \(c = C^*\)). Le même terme figure dans la NAV et dans le Book, de sorte que l'effet de change sur les liquidités s'annule dans l'UGL : il est comptabilisé comme résultat de change (§6) et appartient au P&L total.


📐 6. Modèle de trésorerie à trois pools — Par courtier \((K_b, R_b, W)\)

Trois pools d'accumulation suivent la provenance des liquidités. \(K\) et \(R\) sont maintenus par courtier \(b\) ; \(W\) est global (il sort complètement du système).

Pool Portée Signification
\(K_b\) Par courtier Capital externe encore chez le courtier \(b\) sous forme de liquidités
\(R_b\) Par courtier Rendements générés encore chez le courtier \(b\) sous forme de liquidités
\(W\) Global Rendements sortis du système (masqués, restaurables lors d'un nouveau dépôt)

Propriété clé

Un ACHAT chez le courtier \(b_1\) ne peut consommer que \(R_{b_1}\), jamais \(R_{b_2}\). Les liquidités ne se téléportent pas entre courtiers — seuls les transferts explicites déplacent les soldes des pools.

🔁 Règles de mise à jour (par transaction sur le courtier \(b\), ordre chronologique)

Icône et type Formules de mise à jour Logique et description

DÉPÔT
\(D > 0\)
\(r = \min(D,\, W)\)
\(R_b \mathrel{+}= r\)
\(W \mathrel{-}= r\)
\(K_b \mathrel{+}= D - r\)
Restaure d'abord les rendements précédemment retirés depuis le suivi global \(W\), puis ajoute le reste au capital \(K_b\).

RETRAIT
\(X > 0\)
\(k = \min(X,\, K_b)\)
\(K_b \mathrel{-}= k\)
\(\rho = \min(X - k,\, R_b)\)
\(R_b \mathrel{-}= \rho\)
\(W \mathrel{+}= \rho\)
Consomme d'abord le capital \(K_b\), puis déplace les rendements restants \(\rho\) vers le suivi global \(W\).

DIVIDENDE / INTÉRÊT
\(I > 0\)
\(R_b \mathrel{+}= I\) Les rendements augmentent directement le pool de rendements \(R_b\).

FRAIS / IMPÔT
\(F > 0\)
\(R_b \mathrel{-}= F\)
\(\text{si } R_b < 0\text{: } K_b \mathrel{+}= R_b,\; R_b = 0\)
Consomme d'abord les rendements \(R_b\) ; si \(R_b\) devient négatif, il ponctionne le capital \(K_b\).

ACHAT
\(B > 0\)
\(\rho = \min(B,\, R_b)\)
\(R_b \mathrel{-}= \rho\)
\(K_b \mathrel{-}= (B - \rho)\)
Consomme d'abord les rendements \(R_b\), puis ponctionne le reste sur le capital \(K_b\).

VENTE
\(G = P - C\)
\(K_b \mathrel{+}= C\)
\(R_b \mathrel{+}= G\)
\(\text{si } R_b < 0\text{: } K_b \mathrel{+}= R_b, \quad R_b = 0\)
Le coût de base $C =

VIREMENT
(Interne, \(s \to d\), \(X > 0\))
Jambe de départ (\(s\)) :
\(\rho = \min(X,\, R_s)\)
\(R_s \mathrel{-}= \rho\)
\(\kappa = X - \rho\)
\(K_s \mathrel{-}= \kappa\)

Jambe d'arrivée (\(d\)) :
\(K_d \mathrel{+}= \kappa\)
\(R_d \mathrel{+}= \rho\)
Les virements internes déplacent les allocations de pool (\(R_s \to R_d\), \(K_s \to K_d\)) proportionnellement au solde de départ.
Le suivi global \(W\) n'est jamais touché (le capital reste à l'intérieur du système).

Si les dates de départ et d'arrivée diffèrent, le virement est en transit : soustrait de \(s\) au jour de départ, ajouté à \(d\) au jour d'arrivée. Entre ces dates, \(\sum K_b + \sum R_b < \mathrm{Cash}_{\text{like}}\) du montant en transit — géré par réconciliation proportionnelle.

💱 Résultat de change sur les liquidités en devises

Chaque mouvement de trésorerie entre dans les liquidités du courtier, et dans ses pools, au taux de sa propre date ; le solde détenu ensuite suit le taux (§5). Chaque jour \(t\) où le taux d'une devise étrangère détenue en liquidités dans \(S\) varie — y compris son premier taux connu —, après les mouvements de trésorerie de ce jour et avant que les transactions de ce jour ne mettent à jour les pools, chaque courtier \(b\) détenant des liquidités en devises est réévalué :

\[ \varepsilon_b(t) = \sum_{c} N_{b,c}(t) \cdot \mathrm{fx}(c, C^*, t) - \mathrm{Cash}^{\text{replay}}_b(t) \]
\[ \mathrm{Cash}^{\text{replay}}_b \mathrel{+}= \varepsilon_b, \qquad R_b \mathrel{+}= \varepsilon_b, \qquad \text{si } R_b < 0\text{: } K_b \mathrel{+}= R_b,\; R_b = 0 \]

\(\mathrm{Cash}^{\text{replay}}_b(t)\) représente les liquidités que le rejeu porte pour \(b\) : leur valeur à la réévaluation précédente plus chaque mouvement ultérieur au taux de sa propre date. Un gain va aux rendements ; une perte consomme d'abord les rendements puis le capital, exactement comme des FRAIS. La référence de capital ne bouge pas : un résultat de change est un résultat, pas un apport ; il figure donc dans la NAV, le P&L total, le ROI, le TWRR et le MWRR, jamais dans le capital versé.

🧮 Agrégation pour la sortie

\[ \mathrm{CashFromCapital}(t) = \sum_{b \in S} K_b(t) \]
\[ \mathrm{CashFromReturns}(t) = \sum_{b \in S} R_b(t) \]

⚖️ Invariant de réconciliation

\[ \mathrm{Cash}_{\text{like}}(t) \approx \sum_{b \in S} K_b(t) + \sum_{b \in S} R_b(t) \]

Les deux pools sont d'abord bornés à zéro pour chaque courtier. Si les pools ne sont pas vides et que la dérive dépasse encore \(0.01\), les pools cumulés sont mis à l'échelle de \(\mathrm{Cash}_{\text{like}}\), les liquidités plus les liquidités en transit — une mise à l'échelle globale, et non par courtier, chaque résultat étant borné à zéro :

\[ \mathrm{CashFromCapital}(t) = \sum_{b \in S} K_b(t) \]

Sinon, \(\mathrm{CashFromCapital}(t) = \min\bigl(\sum_b K_b(t),\, \mathrm{Cash}_{\text{like}}(t)\bigr)\) et \(\mathrm{CashFromReturns}\) est le reste, borné à zéro. Les résultats de change sur les liquidités au repos sont comptabilisés dans les pools (ci-dessus) : la dérive restante vient donc du timing en transit — les liquidités en transit, et la variation de leur taux de change pendant le trajet, ne sont dans les pools d'aucun courtier.


📐 7. Contribution de période

Pour la période \([t_0, t_1]\), par position \((a,b)\) :

\[ \Delta\mathrm{UGL}(a,b) = \mathrm{UGL}(a,b,t_1) - \mathrm{UGL}(a,b,t_0) \]
\[ \mathrm{PnL}(a,b) = \Delta\mathrm{UGL}(a,b) + \mathrm{Realized}(a,b) + \mathrm{Income}(a,b) - \mathrm{FeesTaxes}(a,b) \]

Ensemble des positions contributrices :

\[ \mathcal{P} = \text{positions avec activité ACHAT/VENTE/AJUSTEMENT/TRANSFERT ou quantité limite} \]

Le P&L période au niveau du portefeuille expose aussi un résidu :

\[ \mathrm{Other} = \mathrm{PnL}_{period} - \Delta\mathrm{UGL} - \mathrm{Realized} - \mathrm{Income} + \mathrm{FeesTaxes} \]

Comme l'UGL ne peut pas contenir le résultat de change sur les liquidités (§5), Other se décompose en résultat de change de chaque courtier sur la période et en ce qui reste :

\[ \mathrm{Other} = \sum_{b \in S} \mathrm{FXonCash}_b + \mathrm{Residual} \]

Les frais/revenus non alloués sans asset_id sont regroupés par courtier comme autres effets de période, de même que \(\mathrm{FXonCash}_b\) : une ligne par courtier détenant des liquidités en devises, affichée lorsque son résultat n'est pas nul. Voir PnL de la période — Effet de change sur les liquidités.


📐 8. Plus-value/moins-value réalisée

Lors d'une VENTE de \(|q_s|\) unités depuis la position \((a,b)\) à la date \(t\) :

\[ C = |q_s| \cdot w_{\text{pre}}(a,b) \]
\[ \mathrm{Realized} = P_{\text{sell}} \cdot \mathrm{fx}(\mathrm{ccy}_{\text{sell}}, C^*, t) - C \]

Où \(w_{\text{pre}}\) est le PRU en \(C^*\) avant la réduction du pool (même valeur utilisée par la règle de vente à 3 pools ci-dessus) : les unités vendues sortent à leur coût historique, sans conversion à la date de vente. Une vente dont le produit ne peut pas être converti, ou prélevée sur une position dont le coût est incomplet, est exclue du P&L réalisé.


📐 9. Architecture Pré-frame / Frame

Phase Plage de dates Calcule
Pré-frame \([t_{\mathrm{first}},\ t_0)\) Liquidités, qté, PRU, pools — aucune évaluation de marché
Frame \([t_0,\ t_1]\) Quotidien complet : prix, FX, états de position, états de portefeuille

Les transactions pré-frame mettent à jour les accumulateurs (registre de liquidités, pools PRU, K/R/W à 3 pools) sans aucune évaluation de marché : aucun prix n'est lu et aucune position n'est valorisée. Chaque mouvement est converti au taux de sa propre date, comme dans le frame, et chaque jour où le taux d'une devise étrangère détenue varie, les liquidités sont réévaluées au taux de ce jour et le résultat de change entre dans les pools (§6), de sorte que les pools sont justes à \(t_0\). Cela permet un cache efficace basé sur les plages.


📐 10. Métriques de performance (couche 2)

Calculées après les états quotidiens, en une passe séparée :

Métrique Formule Référence
P&L total \(\mathrm{NAV}(t) - \text{CapitalBaseline}(t)\) Capital versé
P&L période \(\mathrm{NAV}(t_1) - \mathrm{NAV}(t_0) - \text{ECF}_{[t_0,t_1]}\) P&L période
TWRR \(\prod_i (1 + r_i) - 1\) (chaîne de sous-périodes) TWRR
MWRR XIRR résolvant \(\sum \frac{CF_i}{(1+r)^{d_i/365}} = 0\) MWRR
ROI simple \((\mathrm{NAV} - \text{NetInvested}) / \text{NetInvested}\) ROI
Rendement annualisé net \((1+r_{\mathrm{net}})^{365/d}-1\), supprimé en dessous de 30 jours Rendement annualisé net
Rendement sur coût Revenu brut de transaction sur 365 jours glissants par unité historique éligible, divisé par le PRU unitaire résiduel à \(t_1\) Rendement sur coût
Effet timing \(\text{MWRR}_{\text{cum}} - \text{TWRR}_{\text{cum}}\) Effet timing

🔗 Voir aussi