Python

Python pour la finance — les fonctions de base

×

Recommandés

CSV en Python — du téléchargement au...
Le CSV paraît simple, jusqu’au...
En savoir plus
Python : pandas.to_csv — Exporter propre, fiable...
DataFrame.to_csv devient un contrat d’échange :...
En savoir plus
Python : pandas.read_csv — Guide pratique enrichi
Pourquoi read_csv reste incontournable Parce qu’il...
En savoir plus
Pratique de l’apprentissage automatique avec scikit-learn et...
Cet article vous guide, pas à...
En savoir plus
Python & finance PME — un kit...
Pour une PME, la finance est...
En savoir plus
Python & Pickle : manipuler les fichiers...
Pickle est la bibliothèque standard de...
En savoir plus

Python est devenu l’outil “couteau suisse” des analystes et contrôleurs financiers. Ce guide rassemble les fonctions de base dont vous aurez besoin au quotidien : valeur temps de l’argent, VAN/TRI (et leurs versions avec dates), prêts, obligations, rendements/volatilité/Sharpe, et un mini-kit pour démarrer proprement vos calculs.

Notation : on adopte la convention Excel/financière : entrées d’argent positives, sorties négatives. Les taux périodiques doivent être cohérents avec le nombre de périodes (mensuel, annuel, etc.).


0) Préparer l’environnement

python -m pip install --upgrade pip
pip install numpy pandas
import numpy as np
import pandas as pd
from datetime import datetime, date

1) Valeur temps de l’argent : PV, FV, PMT

Fonctions de base (compatibles avec la logique Excel, y compris le paramètre when pour paiements en début ou fin de période) :

def pv(rate, nper, pmt=0.0, fv=0.0, when='end'):
    """Valeur actuelle d'une série de flux (convention Excel)."""
    when = 1 if when == 'begin' else 0
    if rate == 0:
        return - (pmt * nper + fv)
    return - (pmt * (1 + rate * when) * (1 - (1 + rate)**-nper) / rate + fv * (1 + rate)**-nper)

def fv(rate, nper, pmt=0.0, pv_=0.0, when='end'):
    """Valeur future d'une série de flux (convention Excel)."""
    when = 1 if when == 'begin' else 0
    if rate == 0:
        return - (pv_ + pmt * nper)
    return - (pv_ * (1 + rate)**nper + pmt * (1 + rate * when) * ((1 + rate)**nper - 1) / rate)

def pmt(rate, nper, pv_, fv=0.0, when='end'):
    """Annuité constante (mensualité de prêt, etc.)."""
    when = 1 if when == 'begin' else 0
    if rate == 0:
        return - (pv_ + fv) / nper
    return - (rate * (pv_ * (1 + rate)**nper + fv)) / ((1 + rate * when) * ((1 + rate)**nper - 1))

Exemple (prêt) : 100 000 €, 5% annuel, 20 ans, mensualités (taux mensuel = 0,05/12 ; nper = 20×12).
La mensualité vaut ≈ 659,96 € (sortie de trésorerie).

mensualite = pmt(0.05/12, 20*12, 100_000)

2) VAN & TRI : flux réguliers et irréguliers

2.1 VAN/TRI périodiques (flux à t=0,1,2,…)

def npv(rate, cashflows):
    """VAN classique (incluant le flux t=0)."""
    return sum(cf / (1 + rate)**t for t, cf in enumerate(cashflows))

def irr(cashflows, guess=0.1, tol=1e-8, maxiter=200):
    """TRI via méthode de Newton (flux périodiques)."""
    def f(r):
        return sum(cf / (1 + r)**t for t, cf in enumerate(cashflows))
    def fprime(r):
        return sum(-t * cf / (1 + r)**(t + 1) for t, cf in enumerate(cashflows) if t > 0)

    r = guess
    for _ in range(maxiter):
        y, yp = f(r), fprime(r)
        if yp == 0:
            break
        r_new = r - y / yp
        if abs(r_new - r) < tol:
            return r_new
        r = r_new
    return r

Exemple : flux [-1000, 300, 400, 500, 600] au taux 8% → VAN ≈ 458,65 ; TRI ≈ 24,89%.

2.2 XNPV/XIRR (flux datés, irréguliers)

Quand les dates ne sont pas espacées régulièrement (cas réel : investissements, coupons décalés), utilisez la version “avec dates” :

def xnpv(rate, cashflows, dates):
    """VAN à dates irrégulières (ACT/365 simple)."""
    d0 = dates[0]
    return sum(cf / (1 + rate)**(((d - d0).days) / 365.0) for cf, d in zip(cashflows, dates))

def xirr(cashflows, dates, guess=0.1, tol=1e-8, maxiter=100):
    """TRI à dates irrégulières (Newton)."""
    r = guess
    for _ in range(maxiter):
        d0 = dates[0]
        f  = sum(cf / (1 + r)**(((d - d0).days) / 365.0) for cf, d in zip(cashflows, dates))
        df = sum(-( (d - d0).days / 365.0 ) * cf / (1 + r)**((((d - d0).days) / 365.0) + 1)
                 for cf, d in zip(cashflows, dates))
        if df == 0:
            break
        r_new = r - f / df
        if abs(r_new - r) < tol:
            return r_new
        r = r_new
    return r

💡 Jour-comptage : ici ACT/365 (simple). Selon vos conventions (ACT/360, 30/360, ACT/365F…), adaptez le dénominateur.


3) Prêts : tableau d’amortissement

def amortization_schedule(principal, annual_rate, years, freq=12, when='end'):
    r = annual_rate / freq
    n = years * freq
    pay = pmt(r, n, principal, when=when)

    rows = []
    balance = principal
    for k in range(1, n + 1):
        # Intérêt sur solde début (ou fin si when='begin')
        interest = (balance * r) if when == 'end' else ((balance - pay) * r)
        principal_paid = -pay - interest
        balance -= principal_paid
        rows.append({
            "periode": k,
            "paiement": round(-pay, 2),
            "interets": round(interest, 2),
            "principal": round(principal_paid, 2),
            "solde": round(balance, 2)
        })
    return pd.DataFrame(rows)

Usage : amortization_schedule(100_000, 0.05, 20, 12) renvoie un DataFrame prêt à exporter en Excel.


4) Obligations : prix, rendement, duration, convexité

4.1 Prix (coupon fixe)

def bond_price(face, coupon_rate, ytm, maturity_years, freq=2):
    c = coupon_rate * face / freq
    n = int(round(maturity_years * freq))
    y = ytm / freq
    price = sum(c / (1 + y)**t for t in range(1, n + 1)) + face / (1 + y)**n
    return price

Exemple : nominal 1000 €, coupon 4%, YTM 5%, maturité 5 ans, annuel → prix ≈ 956,71 €.

4.2 Duration (Macaulay/modifiée) & convexité

def bond_duration_convexity(face, coupon_rate, ytm, maturity_years, freq=2):
    c = coupon_rate * face / freq
    n = int(round(maturity_years * freq))
    y = ytm / freq
    times = np.arange(1, n + 1)
    cashflows = np.full(n, c, dtype=float); cashflows[-1] += face
    disc = 1 / (1 + y)**times
    price = np.sum(cashflows * disc)
    macaulay = np.sum(times * cashflows * disc) / price
    modified = macaulay / (1 + y)
    convexity = np.sum(times * (times + 1) * cashflows * disc) / (price * (1 + y)**2)

    # Conversion en années
    return {
        "prix": float(price),
        "duration_macaulay_ans": float(macaulay / freq),
        "duration_modifiee_ans": float(modified / freq),
        "convexite_annee2": float(convexity / (freq**2))
    }

Interprétation rapide : la duration modifiée ≈ variation % du prix pour une petite variation du taux (ΔP/P ≈ −Dur_mod × Δy). La convexité corrige cette approximation pour de plus grands mouvements.

4.3 Rendement à l’échéance (YTM) à partir du prix

Sans SciPy, une dichotomie est robuste :

def ytm_from_price(face, coupon_rate, price, maturity_years, freq=2, tol=1e-8, maxiter=200):
    lo, hi = 0.0, 1.0  # 0% à 100% de rendement initialement
    for _ in range(maxiter):
        mid = (lo + hi) / 2
        p = bond_price(face, coupon_rate, mid, maturity_years, freq)
        if abs(p - price) < tol:
            return mid
        if p > price:
            lo = mid
        else:
            hi = mid
    return (lo + hi) / 2

5) Dates & conventions : YEARFRAC (simple)

Pour XNPV/XIRR, nous avons utilisé ACT/365. Voici un utilitaire explicite :

def yearfrac_act365(d1, d2):
    return (d2 - d1).days / 365.0

En contexte obligataire, choisissez la bonne convention (30/360 US/EU, ACT/360, ACT/365F…). Les résultats (prix/duration) varient selon ce choix.


6) Marchés : rendements, volatilité, Sharpe

def log_returns(prices: np.ndarray):
    """Prix -> rendements log (taux continus)."""
    prices = np.asarray(prices, dtype=float)
    return np.log(prices[1:] / prices[:-1])

def annualize_ret_vol(mu, sigma, periods=252):
    """Annualisation pour des séries quotidiennes (252 j ouvrés)."""
    return mu * periods, sigma * np.sqrt(periods)

def sharpe_ratio(returns, rf=0.0, periods=252):
    """Sharpe annualisé (rf = taux sans risque annualisé)."""
    r = np.asarray(returns, dtype=float)
    mu, sigma = r.mean(), r.std(ddof=1)
    mu_a, sigma_a = annualize_ret_vol(mu, sigma, periods=periods)
    return (mu_a - rf) / sigma_a if sigma_a != 0 else np.nan

Astuce : pour des rendements mensuels, remplacez periods=12. Pour des rendements hebdo, periods=52.


7) Portefeuilles : moyenne, variance, bêta (CAPM)

def portfolio_metrics(weights, mean_returns, cov_matrix, rf=0.0, periods=252):
    w = np.asarray(weights, dtype=float)
    mu = np.dot(w, mean_returns)            # rendement espéré (périodique)
    var = float(np.dot(w, np.dot(cov_matrix, w)))  # variance (périodique)
    mu_a, vol_a = mu * periods, np.sqrt(var) * np.sqrt(periods)
    sharpe = (mu_a - rf) / vol_a if vol_a != 0 else np.nan
    return {"ret_ann": mu_a, "vol_ann": vol_a, "sharpe": sharpe}

def capm_beta(asset_returns, market_returns):
    """β = Cov(Ra, Rm)/Var(Rm)."""
    a = np.asarray(asset_returns); m = np.asarray(market_returns)
    cov = np.cov(a, m, ddof=1)[0,1]
    var_m = np.var(m, ddof=1)
    return cov / var_m if var_m != 0 else np.nan

8) Mini-cas fil rouge (express)

  1. DCF projet : flux [-1000, 300, 400, 500, 600] → VAN à 8% ≈ 458,65, TRI ≈ 24,89%.
  2. Obligation : 1000 €, 4%, 5 ans, YTM 5% → 956,71 €, duration modifiée ≈ 4,40 ans.
  3. Prêt immo : 100 000 €, 5% sur 20 ans mensuel → 659,96 €/mois, tableau d’amortissement via amortization_schedule.
  4. Portefeuille : calculez le Sharpe avec sharpe_ratio(returns, rf=…) ; ajustez la fréquence.

9) Bonnes pratiques (vraiment utiles)

  • Cohérence temporelle : un taux mensuel s’applique à des flux mensuels, etc.
  • Signes : décidez d’une convention et tenez-vous-y (entrées +, sorties −).
  • Vectorisez (NumPy) : plus rapide et plus lisible.
  • Conventions de dates : documentez le day-count utilisé (audit).
  • Robustesse numérique : préférez dichotomie ou Brent aux Newton “à l’aveugle” quand la dérivée peut s’annuler.
  • Tests : figez des cas de test (prix d’obligation connu, VAN/TRI de référence) pour éviter les régressions.

10) À emporter : votre “boîte à outils”

  • Valeur temps : pv, fv, pmt
  • VAN/TRI : npv, irr, xnpv/xirr pour dates réelles
  • Prêts : amortization_schedule (DataFrame)
  • Obligations : bond_price, ytm_from_price, bond_duration_convexity
  • Marchés/portefeuilles : log_returns, sharpe_ratio, portfolio_metrics, capm_beta
  • Dates : yearfrac_act365 (adaptez selon conventions)

Cas débutant « machine à café » : passer du calcul à l’aide à la décision

un notebook structuré en sections, 2) des visualisations lisibles (cash-flow vs cumul, point mort), 3) des sensibilités à 2 facteurs (marge et taux du prêt) + un pack d’exports (CSV/Excel/PNG).
Tout est prêt à copier-coller dans votre notebook.

1) Plan propre du notebook (sections et conventions)

Cellule 1 — Imports & fonctions utilitaires

import numpy as np, pandas as pd, matplotlib.pyplot as plt
from dataclasses import dataclass

# --- Fonctions financières de base ---
def pmt(rate, nper, pv_, fv=0.0, when='end'):
    when_v = 1 if when == 'begin' else 0
    if rate == 0: return - (pv_ + fv) / nper
    return - (rate * (pv_ * (1 + rate)**nper + fv)) / ((1 + rate * when_v) * ((1 + rate)**nper - 1))

def npv(rate, cashflows):
    return sum(cf / (1 + rate)**t for t, cf in enumerate(cashflows))

def irr(cashflows, guess=0.1, tol=1e-8, maxiter=200):
    def f(r):  return sum(cf / (1 + r)**t for t, cf in enumerate(cashflows))
    def fp(r): return sum(-t * cf / (1 + r)**(t+1) for t, cf in enumerate(cashflows) if t>0)
    r=guess
    for _ in range(maxiter):
        y, yp = f(r), fp(r)
        if yp == 0: break
        r_new = r - y/yp
        if abs(r_new-r) < tol: return r_new
        r = r_new
    return r

def payback_period(cashflows):
    cum = 0.0
    for t, cf in enumerate(cashflows):
        cum += cf
        if cum >= 0:
            return t
    return None  # jamais amorti dans l’horizon étudié

Cellule 2 — Hypothèses de base (cas Lina)

# Hypothèses (mensuelles sauf mention)
prix_machine = 5_000.0
taux_pret_ann = 0.06
n_mois = 36
taux_actual_ann = 0.10
valeur_residuelle = 500.0

# Résultats opérationnels
marge_suppl = 350.0      # marge brute mensuelle attendue
maintenance  = 20.0      # coût mensuel de maintenance
net_avant_dette = marge_suppl - maintenance   # 330 €/mois

# Taux convertis
r_pret_m = taux_pret_ann/12
r_act_m  = taux_actual_ann/12

Cellule 3 — Calculs & tableau d’amortissement

# Mensualité
mensualite = -pmt(r_pret_m, n_mois, prix_machine)  # valeur positive à payer
flux_net = net_avant_dette - mensualite

# Flux (t=0 achat ; t=1..35 flux_net ; t=36 flux_net + revente)
cashflows = [-prix_machine] + [flux_net]*35 + [flux_net + valeur_residuelle]

# Indicateurs
van = npv(r_act_m, cashflows)
tri_ann = (1 + irr(cashflows, guess=0.02))**12 - 1
pm = payback_period(cashflows)

# Tableau d’amortissement (intérêt/principal/solde)
def amortization_schedule(principal, annual_rate, months):
    r = annual_rate/12
    pay = mensualite
    rows=[]; balance=principal
    for k in range(1, months+1):
        interest = balance * r
        principal_paid = pay - interest
        balance -= principal_paid
        rows.append({"mois":k, "paiement":round(pay,2), "intérêts":round(interest,2),
                     "principal":round(principal_paid,2), "solde":round(balance,2)})
    return pd.DataFrame(rows)

amort = amortization_schedule(prix_machine, taux_pret_ann, n_mois)

# Résumé lisible
resume = pd.DataFrame({
    "Métrique":[
        "Mensualité du prêt (€)",
        "Flux net mensuel après dette (€)",
        "VAN (10% ann.) (€)",
        "TRI annualisé (%)",
        "Point mort (mois)"
    ],
    "Valeur":[round(mensualite,2), round(flux_net,2), round(van,2), round(100*tri_ann,2), pm]
})
resume

Cellule 4 — Visualisations (cash-flow vs cumul + point mort)

# Série cash-flow
series = pd.DataFrame({
    "mois": np.arange(0, n_mois+1),
    "cashflow": cashflows
})
series["cumul"] = series["cashflow"].cumsum()

# Graphe 1: Cash-flow mensuel
plt.figure()
plt.bar(series["mois"], series["cashflow"])
plt.axhline(0, linestyle="--")
plt.title("Cash-flow mensuel (machine à café)")
plt.xlabel("Mois"); plt.ylabel("€")
plt.tight_layout(); plt.show()

# Graphe 2: Cumul & point mort
plt.figure()
plt.plot(series["mois"], series["cumul"], marker="o")
plt.axhline(0, linestyle="--")
if pm is not None:
    plt.axvline(pm, linestyle=":")
    plt.text(pm, series.loc[series["mois"]==pm, "cumul"].values[0], f"  Point mort: M{pm}")
plt.title("Cumul des flux — point mort")
plt.xlabel("Mois"); plt.ylabel("€ cumulés")
plt.tight_layout(); plt.show()

Cellule 5 — Sensibilités (2 facteurs) + exports

# Grille: Net avant dette ∈ [260..400], Taux prêt ann. ∈ [3%..9%]
nets = np.arange(260, 401, 20)      # €/mois
tauxs = np.arange(0.03, 0.091, 0.01) # annuel

def eval_case(net, taux_ann):
    r_p = taux_ann/12
    pay = -pmt(r_p, n_mois, prix_machine)
    cf  = net - pay
    cfs = [-prix_machine] + [cf]*35 + [cf + valeur_residuelle]
    return npv(r_act_m, cfs)

mat = np.zeros((len(nets), len(tauxs)))
for i, net in enumerate(nets):
    for j, t in enumerate(tauxs):
        mat[i, j] = eval_case(net, t)

# Heatmap VAN
plt.figure()
plt.imshow(mat, origin="lower", aspect="auto",
           extent=[tauxs[0]*100, tauxs[-1]*100, nets[0], nets[-1]])
plt.colorbar(label="VAN (€)")
plt.xlabel("Taux du prêt (ann. %)"); plt.ylabel("Net avant dette (€/mois)")
plt.title("Sensibilité 2D — VAN (€)")
plt.tight_layout(); plt.show()

# Exports
series.to_csv("cashflows_series.csv", index=False)
resume.to_csv("resume_indicateurs.csv", index=False)
amort.to_excel("amortissement_pret.xlsx", index=False)

2) Lecture métier des résultats (mode “expliquez-moi simplement”)

  • Mensualité152,11 € → c’est votre “loyer” de la machine.
  • Flux net après dette+177,89 €/mois → ce que la machine rapporte après avoir payé la mensualité.
  • Point mort29 mois → à partir de là, vous avez “récupéré” l’investissement (sur 36 mois, avec revente).
  • VAN (10 %)+884 € → à votre coût du capital, le projet crée de la valeur.
  • TRI annualisé23 % → bien au-dessus de 10 % requis, intéressant.

Règle de pouce : si votre marge nette avant dette baisse de 50 € (ex. passages plus faibles), la VAN peut basculer négative. Surveillez 3 choses les 2 premiers mois : volume matin, taux de casse/maintenance, panier moyen.


3) Trame “banquier/partenaire” (3 bullets qui rassurent)

  • Stabilité des flux : historique de ventes matin + retours clients (photos/avis) → hypothèses crédibles.
  • Coussin de sécurité : VAN testée en scénario prudent (net 280 €/mois) + réserve de trésorerie = 3 mensualités.
  • Sortie : valeur résiduelle réaliste 500 € (devis revendeur ou cote), horizon 36 mois.

4) Contrôles rapides (éviter les erreurs classiques)

  • Cohérence des unités : flux mensuelstaux mensuels (taux/12).
  • Signes : achat négatif au mois 0, encaissements positifs.
  • Horizon : la revente est bien au mois 36 (pas au 35).
  • Sanity check : si net_avant_dette ≤ mensualité, le flux net doit être ≤ 0.
assert mensualite > 0, "La mensualité doit être positive (sortie de trésorerie)"
assert len(cashflows) == n_mois + 1, "Horizon incohérent"

5) Exercices-guides (pour débutants)

  1. Sans revente (valeur résiduelle = 0) : que deviennent VAN/TRI/point mort ?
  2. Hausse des taux (+2 pts sur le prêt) : refaites la heatmap → zone de VAN négative s’agrandit ?
  3. Saisonnalité : baisse de 30 % en juillet-août → modélisez 2 mois plus faibles (modifier cashflows).
  4. Affichage : exportez resume_indicateurs.csv et amortissement_pret.xlsx et ajoutez-les dans un e-mail synthétique “banquier”.

6) Ce que vous pouvez réutiliser demain

  • Les fonctions pmt, npv, irr, payback_period pour tout mini-investissement (vitrine réfrigérée, vélo-cargo, four mixte…).
  • La heatmap 2D pour visualiser votre zone de sécurité (marge vs taux).
  • Le tableau d’amortissement pour discuter assurance/anticiper un rachat par anticipation.

Recommandés

CSV en Python — du téléchargement au...
Le CSV paraît simple, jusqu’au...
En savoir plus
Python : pandas.to_csv — Exporter propre, fiable...
DataFrame.to_csv devient un contrat d’échange :...
En savoir plus
Python : pandas.read_csv — Guide pratique enrichi
Pourquoi read_csv reste incontournable Parce qu’il...
En savoir plus
Pratique de l’apprentissage automatique avec scikit-learn et...
Cet article vous guide, pas à...
En savoir plus
Python & finance PME — un kit...
Pour une PME, la finance est...
En savoir plus
Python & Pickle : manipuler les fichiers...
Pickle est la bibliothèque standard de...
En savoir plus
AZ

Recent Posts

Outils interactifs : Cartographie des processus et grille d’audit interne ISO 9001

Deux outils concrets pour piloter la qualité sans alourdir vos équipes Un système qualité n’avance…

6 heures ago

Exemple de fiche de préparation de chantier

Un chantier se gagne souvent avant même l’arrivée des équipes. Quand tout est clair dès…

1 jour ago

Texte argumentatif sur le mariage forcé

Le mariage a du sens quand il repose sur une décision libre, mûrie et partagée.…

1 jour ago

Étude de cas en droit : Modèle Word à suivre

Une étude de cas réussie commence par une structure sûre. Ce modèle Word vous guide…

4 jours ago

Soft skills : la liste A à Z interactive pour trier, comparer et choisir vos compétences clés

Les soft skills se repèrent vite sur une fiche, mais elles ne pèsent vraiment que…

4 jours ago

Comparateur de verres progressifs

Outil de comparaison et repérage des offres étudiantes Choisir des verres progressifs ressemble rarement à…

5 jours ago

This website uses cookies.