// pip install quantfinance

QuantFinance

Package Python professionnel pour la finance quantitative — pricing, risque, optimisation de portefeuille et backtesting.

Python 3.8+ MIT PyPI CI Coverage

Présentation

QuantFinance est une bibliothèque Python complète pour la finance quantitative. Elle couvre le pricing d'instruments financiers, la gestion des risques, l'optimisation de portefeuille et le backtesting de stratégies.

Pricing

  • Black-Scholes
  • Binomial Tree
  • Monte Carlo
  • Options exotiques
  • Obligations

Risque

  • VaR (4 méthodes)
  • CVaR / ES
  • Sharpe, Sortino, Calmar
  • Max Drawdown
  • Stress Testing

Portefeuille

  • Markowitz
  • Risk Parity
  • Black-Litterman
  • HRP
  • Max Diversification

Backtesting

  • MA Crossover
  • Buy & Hold
  • Momentum
  • Coûts de transaction

Utilitaires

  • Yahoo Finance
  • Données synthétiques
  • RSI, MACD, Bollinger
  • Rééquilibrage

Installation

pip
Source
Extras
bash
pip install quantfinance
bash
git clone https://github.com/Mafoya1er/quantfinance.git
cd quantfinance
pip install -e .
bash
pip install quantfinance[data] # Analyse de données
pip install quantfinance[dev] # Développement
pip install quantfinance[all] # Tout installer

Démarrage rapide

python
from quantfinance.pricing.options importBlackScholes
from quantfinance.portfolio.optimization importPortfolioOptimizer
from quantfinance.risk.var importVaRCalculator
from quantfinance.utils.data importDataLoader

# Pricing d'une option call
bs = BlackScholes(S=100, K=105, T=1, r=0.05, sigma=0.25, option_type='call')
print(f"Prix: {bs.price():.2f}, Delta: {bs.delta():.4f}")

# Optimisation de portefeuille
prices = DataLoader.generate_synthetic_prices(n_assets=5, n_days=756)
returns = prices.pct_change().dropna()
optimizer = PortfolioOptimizer(returns, risk_free_rate=0.02)
result = optimizer.maximize_sharpe()
print(f"Sharpe: {result['sharpe_ratio']:.3f}")

# Value at Risk
var_95 = VaRCalculator.historical_var(returns.iloc[:, 0], 0.95)
print(f"VaR 95%: {var_95:.2%}")

quantfinance.pricing

Module de pricing d'options (vanille et exotiques) et d'obligations.

BlackScholes
Modèle de Black-Scholes pour options européennes — prix et grecques complètes.
__init__(S, K, T, r, sigma, option_type='call')
Initialise le modèle Black-Scholes.
ParamètreTypeDescription
SfloatPrix spot du sous-jacent
KfloatPrix d'exercice (strike)
TfloatMaturité en années
rfloatTaux sans risque annualisé
sigmafloatVolatilité annualisée
option_typestr'call' ou 'put'
python
from quantfinance.pricing.options importBlackScholes

# Option call européenne
bs_call = BlackScholes(S=100, K=105, T=1, r=0.05, sigma=0.25, option_type='call')

# Option put européenne
bs_put = BlackScholes(S=100, K=95, T=0.5, r=0.03, sigma=0.20, option_type='put')
price()
→ float

Calcule le prix théorique de l'option selon Black-Scholes.

python
prix = bs_call.price()
print(f"Prix de l'option: {prix:.2f}") # ex: 10.45
delta()
→ float · ∂V/∂S

Sensibilité du prix au prix du sous-jacent. Entre 0 et 1 pour un call, entre -1 et 0 pour un put.

python
print(f"Delta: {bs_call.delta():.4f}") # ex: 0.4523
print(f"Delta put: {bs_put.delta():.4f}") # ex: -0.3871
gamma()
→ float · ∂²V/∂S²

Convexité — taux de variation du delta par rapport au prix du sous-jacent. Toujours positif.

python
print(f"Gamma: {bs_call.gamma():.6f}") # ex: 0.018432
vega()
→ float · ∂V/∂σ

Sensibilité du prix à la volatilité. Exprimé pour une variation de 1% de sigma.

python
print(f"Vega: {bs_call.vega():.4f}") # ex: 0.3841
theta()
→ float · ∂V/∂t

Décroissance temporelle — perte de valeur par jour écoulé. Généralement négatif.

python
print(f"Theta: {bs_call.theta():.4f}") # ex: -0.0152 (perte de 1.52 cts/jour)
rho()
→ float · ∂V/∂r

Sensibilité au taux sans risque.

python
print(f"Rho: {bs_call.rho():.4f}") # ex: 0.2910
implied_volatility(market_price)
→ float

Calcule la volatilité implicite par la méthode de Newton-Raphson à partir du prix de marché observé.

ParamètreTypeDescription
market_pricefloatPrix de marché observé de l'option
python
market_price = 8.50
implied_vol = bs_call.implied_volatility(market_price)
print(f"Volatilité implicite: {implied_vol:.2%}") # ex: 21.34%
BinomialTree
Arbre binomial — options américaines et européennes.
__init__(S, K, T, r, sigma, n_steps=100, option_type='call', american=False)
ParamètreTypeDescription
n_stepsintNombre de pas dans l'arbre (défaut : 100)
americanboolTrue pour activer l'exercice anticipé
python
from quantfinance.pricing.options importBinomialTree

# Option américaine put (exercice anticipé possible)
bt = BinomialTree(S=100, K=105, T=1, r=0.05, sigma=0.25,
 n_steps=200, option_type='put', american=True)
print(f"Prix option américaine: {bt.price():.2f}")

# Comparaison européenne vs américaine
bt_eu = BinomialTree(S=100, K=105, T=1, r=0.05, sigma=0.25, american=False)
print(f"Prime d'exercice anticipé: {bt.price() - bt_eu.price():.2f}")
MonteCarlo
Simulation Monte Carlo — options vanille et exotiques.
price()
→ float

Prix standard par simulation de trajectoires du sous-jacent.

python
from quantfinance.pricing.options importMonteCarlo

mc = MonteCarlo(S=100, K=105, T=1, r=0.05, sigma=0.25, n_simulations=50000)
print(f"Prix MC: {mc.price():.2f}")
asian_price(averaging='arithmetic')
→ float

Prix d'une option asiatique basé sur la moyenne du sous-jacent sur la durée de vie.

python
# Moyenne arithmétique (standard)
prix_asiatique = mc.asian_price(averaging='arithmetic')
print(f"Option asiatique: {prix_asiatique:.2f}")

# Moyenne géométrique
prix_geo = mc.asian_price(averaging='geometric')
print(f"Option asiatique géo: {prix_geo:.2f}")
barrier_price(barrier, barrier_type='up-out')
→ float

Prix d'une option à barrière — s'active ou se désactive si le sous-jacent touche la barrière.

barrier_typeTypeDescription
'up-out'strL'option disparaît si S dépasse la barrière
'up-in'strL'option s'active si S dépasse la barrière
'down-out'strL'option disparaît si S tombe sous la barrière
'down-in'strL'option s'active si S tombe sous la barrière
python
prix_barriere = mc.barrier_price(barrier=120, barrier_type='up-out')
print(f"Option à barrière up-out: {prix_barriere:.2f}")
Bond
Pricing d'obligations — YTM, Duration, Convexité.
__init__(face_value, coupon_rate, maturity, frequency=2)
python
from quantfinance.pricing.bonds importBond

# Obligation 1000€, coupon 5%, maturité 10 ans, semi-annuel
bond = Bond(face_value=1000, coupon_rate=0.05, maturity=10, frequency=2)
price(ytm)
→ float

Prix théorique de l'obligation pour un taux de rendement donné.

python
prix = bond.price(ytm=0.04)
print(f"Prix: {prix:.2f}€") # > 1000 car YTM < coupon
ytm(market_price)
→ float

Yield to Maturity — rendement actuariel jusqu'à maturité.

python
ytm = bond.ytm(market_price=980)
print(f"YTM: {ytm:.2%}") # ex: 5.24%
duration(ytm) / modified_duration(ytm) / convexity(ytm)
→ float

Mesures de sensibilité aux taux d'intérêt.

python
ytm = 0.045
print(f"Duration Macaulay: {bond.duration(ytm):.2f} ans")
print(f"Duration modifiée: {bond.modified_duration(ytm):.2f}")
print(f"Convexité: {bond.convexity(ytm):.2f}")

quantfinance.risk

Module de mesure et d'analyse des risques de marché.

VaRCalculator
Value at Risk et Expected Shortfall — 4 méthodes.
historical_var(returns, confidence)
→ float · Méthode statique

VaR historique non paramétrique — quantile empirique des pertes observées.

python
from quantfinance.risk.var importVaRCalculator

var_95 = VaRCalculator.historical_var(returns, confidence=0.95)
var_99 = VaRCalculator.historical_var(returns, confidence=0.99)
print(f"VaR 95%: {var_95:.2%}")
print(f"VaR 99%: {var_99:.2%}")
parametric_var(returns, confidence)
→ float · Méthode statique

VaR paramétrique sous hypothèse de normalité des rendements.

python
var_param = VaRCalculator.parametric_var(returns, confidence=0.95)
print(f"VaR paramétrique 95%: {var_param:.2%}")
ewma_var(returns, confidence, lambda_=0.94)
→ float · Méthode statique

VaR avec pondération exponentielle — donne plus de poids aux observations récentes (modèle RiskMetrics).

python
# lambda_ = 0.94 est le standard RiskMetrics pour données journalières
var_ewma = VaRCalculator.ewma_var(returns, confidence=0.95, lambda_=0.94)
print(f"VaR EWMA 95%: {var_ewma:.2%}")
monte_carlo_var(returns, confidence, n_sim=10000)
→ float · Méthode statique

VaR par simulation Monte Carlo de trajectoires futures.

python
var_mc = VaRCalculator.monte_carlo_var(returns, confidence=0.95, n_sim=100000)
print(f"VaR Monte Carlo 95%: {var_mc:.2%}")
expected_shortfall(returns, confidence)
→ float · Méthode statique

CVaR / Expected Shortfall — perte moyenne dans les scénarios pires que la VaR. Mesure cohérente du risque.

python
es_95 = VaRCalculator.expected_shortfall(returns, confidence=0.95)
print(f"CVaR 95%: {es_95:.2%}") # toujours >= VaR

# Comparaison des 4 méthodes
for method, val in [
 ("Historique", VaRCalculator.historical_var(returns, 0.95)),
 ("Paramétrique", VaRCalculator.parametric_var(returns, 0.95)),
 ("EWMA", VaRCalculator.ewma_var(returns, 0.95)),
 ("Monte Carlo", VaRCalculator.monte_carlo_var(returns, 0.95)),
]:
 print(f"{method:14s}: {val:.2%}")
RiskMetrics
Ratios de performance et métriques de risque.
sharpe_ratio(returns, rf=0.0)
→ float · Méthode statique

Ratio rendement/risque annualisé. Mesure le rendement excédentaire par unité de volatilité totale.

python
from quantfinance.risk.metrics importRiskMetrics

sharpe = RiskMetrics.sharpe_ratio(returns, rf=0.02)
print(f"Sharpe: {sharpe:.3f}") # > 1 = bon, > 2 = excellent
sortino_ratio(returns, rf=0.0)
→ float · Méthode statique

Comme Sharpe mais ne pénalise que la volatilité négative (downside deviation).

python
sortino = RiskMetrics.sortino_ratio(returns, rf=0.02)
print(f"Sortino: {sortino:.3f}")
calmar_ratio(returns)
→ float · Méthode statique

Rendement annualisé divisé par le drawdown maximum. Mesure la récupération après perte.

python
calmar = RiskMetrics.calmar_ratio(returns)
print(f"Calmar: {calmar:.3f}")
max_drawdown(returns)
→ float · Méthode statique

Perte maximale depuis un pic précédent, exprimée en pourcentage.

python
mdd = RiskMetrics.max_drawdown(returns)
print(f"Max Drawdown: {mdd:.2%}") # ex: -23.45%
information_ratio(returns, benchmark)
→ float · Méthode statique

Alpha annualisé divisé par le tracking error — mesure la valeur ajoutée par rapport à un benchmark.

python
ir = RiskMetrics.information_ratio(portfolio_returns, benchmark_returns)
print(f"Information Ratio: {ir:.3f}")
PerformanceAnalyzer
Analyse complète des performances d'un portefeuille.
__init__(returns, risk_free_rate=0.02)
python
from quantfinance.risk.metrics importPerformanceAnalyzer

analyzer = PerformanceAnalyzer(returns, risk_free_rate=0.02)
summary_statistics()
→ pd.DataFrame

Tableau complet de toutes les métriques de performance et de risque.

python
summary = analyzer.summary_statistics()
print(summary)
# Rendement annualisé : 12.34%
# Volatilité annualisée : 8.92%
# Ratio de Sharpe : 1.382
# Max Drawdown : -15.23%
# VaR 95% : -1.45%
# CVaR 95% : -2.10%
rolling_metrics(window=252)
→ pd.DataFrame

Métriques calculées sur une fenêtre glissante — utile pour suivre l'évolution dans le temps.

python
# Fenêtre de 252 jours (1 an)
rolling = analyzer.rolling_metrics(window=252)
print(rolling[['sharpe', 'volatility']].tail())
plot_performance()
→ Figure

Graphique de la performance cumulée et des drawdowns.

python
import matplotlib.pyplot as plt
fig = analyzer.plot_performance()
plt.show()
StressTesting
Tests de résistance sous scénarios extrêmes.
historical_scenarios(returns)
→ pd.DataFrame · Méthode statique

Applique des scénarios de crises historiques (2008, COVID-19, etc.) au portefeuille.

python
from quantfinance.risk.stress importStressTesting

results = StressTesting.historical_scenarios(returns)
print(results)
# Crise 2008 : -38.2%
# COVID 2020 : -12.4%
# Dot-com 2000 : -25.1%
custom_scenario(returns, shocks)
→ float · Méthode statique

Applique des chocs personnalisés sur les facteurs de risque.

python
# Scénario : choc de -30% sur les actions, +200bps sur les taux
shocks = {'equity': -0.30, 'rates': 0.02}
pnl = StressTesting.custom_scenario(returns, shocks)
print(f"P&L sous stress: {pnl:.2%}")

quantfinance.portfolio

Module d'optimisation de portefeuille, d'allocation d'actifs et de backtesting.

PortfolioOptimizer
Classe principale pour l'optimisation de portefeuille.
__init__(returns, risk_free_rate=0.02)

Initialise l'optimiseur avec les rendements historiques et le taux sans risque.

python
from quantfinance.portfolio.optimization importPortfolioOptimizer

optimizer = PortfolioOptimizer(returns_df, risk_free_rate=0.03)
equal_weight()
→ Dict

Crée un portefeuille équipondéré (poids égaux pour chaque actif).

python
result = optimizer.equal_weight()
print("Poids:", result['weights'])
print("Rendement:", result['return'])
minimize_volatility(constraints=None)
→ Dict

Minimise la volatilité du portefeuille — portefeuille à variance minimale.

python
result = optimizer.minimize_volatility()
print(f"Volatilité minimale: {result['volatility']:.2%}")
print(f"Rendement associé: {result['return']:.2%}")
print(result['weights'])
maximize_sharpe(constraints=None)
→ Dict

Maximise le ratio de Sharpe — point tangent de la frontière efficiente.

python
result = optimizer.maximize_sharpe()
print(f"Sharpe max: {result['sharpe_ratio']:.3f}")
print(f"Rendement: {result['return']:.2%}")
print(f"Volatilité: {result['volatility']:.2%}")
maximize_return(target_volatility)
→ Dict

Maximise le rendement sous contrainte de volatilité cible.

python
result = optimizer.maximize_return(target_volatility=0.18)
print(f"Rendement max (vol=18%): {result['return']:.2%}")
risk_parity()
→ Dict

Portefeuille de parité de risque — chaque actif contribue également au risque total.

python
result = optimizer.risk_parity()
print("Contribution au risque:", result['risk_contribution'])
print("Poids:", result['weights'])
EfficientFrontier
Calcul et visualisation de la frontière efficiente de Markowitz.
__init__(optimizer)
python
from quantfinance.portfolio.optimization importEfficientFrontier

frontier = EfficientFrontier(optimizer)
calculate_frontier(n_points=50, min_return=None, max_return=None)
→ pd.DataFrame

Calcule les portefeuilles optimaux sur la frontière efficiente.

python
frontier_data = frontier.calculate_frontier(n_points=50)
print(frontier_data.head())
# columns: return, volatility, sharpe_ratio, weights...
plot(show_assets=True, show_optimal=True, figsize=(12,8))
→ Figure

Trace la frontière efficiente avec les actifs individuels et le portefeuille optimal.

python
import matplotlib.pyplot as plt

fig = frontier.plot(show_assets=True, show_optimal=True)
plt.show()
AssetAllocator
Stratégies d'allocation avancées — HRP, min corrélation, max diversification.
__init__(returns)
python
from quantfinance.portfolio.optimization importAssetAllocator

allocator = AssetAllocator(returns_df)
hierarchical_risk_parity()
→ pd.Series

Allocation HRP — utilise la hiérarchie des corrélations pour diversifier sans inversion de matrice.

python
weights = allocator.hierarchical_risk_parity()
print("Poids HRP:", weights)
minimum_correlation()
→ pd.Series

Minimise la corrélation moyenne entre les actifs du portefeuille.

python
weights = allocator.minimum_correlation()
print("Poids min corr:", weights)
maximum_diversification()
→ pd.Series

Maximise le ratio de diversification — rapport volatilité pondérée / volatilité portefeuille.

python
weights = allocator.maximum_diversification()
print("Poids max div:", weights)
Rebalancer
Gestion du rééquilibrage de portefeuille.
__init__(target_weights, prices, initial_capital=100000.0, transaction_cost=0.001)
python
from quantfinance.portfolio.rebalancing importRebalancer

rebalancer = Rebalancer(target_weights, prices_df,
 initial_capital=100000, transaction_cost=0.001)
periodic_rebalancing(frequency='monthly')
→ pd.DataFrame

Rééquilibre le portefeuille à intervalles fixes. Fréquences : 'daily', 'weekly', 'monthly', 'quarterly', 'yearly'.

python
results = rebalancer.periodic_rebalancing(frequency='quarterly')
print(results.head())
threshold_rebalancing(threshold=0.05)
→ pd.DataFrame

Rééquilibre uniquement quand un poids dévie de plus de threshold par rapport à la cible.

python
# Rééquilibre si déviation > 3%
results = rebalancer.threshold_rebalancing(threshold=0.03)
print(results.head())
Backtester
Framework de backtesting de stratégies de trading.
__init__(data, strategy, initial_capital=10000.0, commission=0.001)
python
from quantfinance.portfolio.backtesting importBacktester, MovingAverageCrossover

strategy = MovingAverageCrossover(short_window=20, long_window=50)
backtester = Backtester(prices_df, strategy,
 initial_capital=10000, commission=0.0005)
run() / run_backtest()
→ Dict

Exécute le backtest et retourne les métriques de performance.

python
results = backtester.run()
print(f"Rendement total: {results['Total Return']:.2%}")
print(f"Ratio de Sharpe: {results['Sharpe Ratio']:.3f}")
print(f"Max Drawdown: {results['Max Drawdown']:.2%}")
print(f"Win Rate: {results['Win Rate']:.1%}")
plot_results(figsize=(14,10))
→ Figure

Trace la courbe de capital, les signaux de trading et les drawdowns.

python
import matplotlib.pyplot as plt
fig = backtester.plot_results()
plt.show()
Stratégies de trading
Stratégies prêtes à l'emploi pour le backtesting.
BuyAndHoldStrategy
Stratégie passive

Achète au début et conserve jusqu'à la fin. Sert de benchmark de référence.

python
from quantfinance.portfolio.backtesting importBuyAndHoldStrategy

strategy = BuyAndHoldStrategy()
signals = strategy.generate_signals(prices_df)
print(signals.head()) # Toujours 1 (long)
MovingAverageCrossover(short_window=20, long_window=50)
Stratégie de tendance

Signal d'achat quand la MA courte croise la MA longue à la hausse, et de vente à la baisse.

python
from quantfinance.portfolio.backtesting importMovingAverageCrossover

strategy = MovingAverageCrossover(short_window=10, long_window=30)
signals = strategy.generate_signals(prices_df)
print(signals.head()) # 1 = long, -1 = short, 0 = neutre

# Backtest complet
bt = Backtester(prices_df, strategy, initial_capital=50000)
results = bt.run()
print(f"Rendement: {results['Total Return']:.2%}")

quantfinance.utils

Utilitaires pour le chargement de données et le calcul d'indicateurs techniques.

DataLoader
Chargement et génération de données financières.
load_csv(path)
→ pd.DataFrame · Méthode statique

Charge un fichier CSV local de prix ou de rendements.

python
from quantfinance.utils.data importDataLoader

prices = DataLoader.load_csv('data/prices.csv')
print(prices.head())
load_yahoo(tickers, start, end)
→ pd.DataFrame · Méthode statique

Télécharge les données historiques depuis Yahoo Finance.

python
prices = DataLoader.load_yahoo(
 tickers=['AAPL', 'MSFT', 'GOOGL', 'AMZN'],
 start='2020-01-01',
 end='2024-01-01'
)
returns = prices.pct_change().dropna()
print(returns.describe())
generate_synthetic_prices(n_assets, n_days)
→ pd.DataFrame · Méthode statique

Génère des prix synthétiques par mouvement brownien géométrique (GBM).

python
# 5 actifs sur 3 ans de données journalières
prices = DataLoader.generate_synthetic_prices(n_assets=5, n_days=252*3)
returns = prices.pct_change().dropna()
generate_ohlcv_data(n_days)
→ pd.DataFrame · Méthode statique

Génère des données OHLCV (Open, High, Low, Close, Volume) pour le backtesting.

python
data = DataLoader.generate_ohlcv_data(n_days=500)
print(data.columns.tolist())
# ['Open', 'High', 'Low', 'Close', 'Volume']
clean_data(df)
→ pd.DataFrame · Méthode statique

Nettoie les données financières : valeurs manquantes, outliers, données incohérentes.

python
raw_data = DataLoader.load_csv('data/raw_prices.csv')
clean = DataLoader.clean_data(raw_data)
print(f"Lignes supprimées: {len(raw_data) - len(clean)}")
TechnicalIndicators
Indicateurs techniques classiques sur séries de prix.
sma(prices, window) / ema(prices, window)
→ pd.Series · Méthodes statiques

Moyennes mobiles simple (SMA) et exponentielle (EMA).

python
from quantfinance.utils.indicators importTechnicalIndicators

close = prices['Close']
sma_20 = TechnicalIndicators.sma(close, window=20)
ema_50 = TechnicalIndicators.ema(close, window=50)

# Signal de croisement
signal = (sma_20 > ema_50).astype(int)
rsi(prices, window=14)
→ pd.Series · Méthode statique

Relative Strength Index — oscillateur entre 0 et 100. Suracheté > 70, survendu < 30.

python
rsi = TechnicalIndicators.rsi(close, window=14)
print(f"RSI actuel: {rsi.iloc[-1]:.1f}")
survendu = rsi[rsi <30]
surachete = rsi[rsi >70]
macd(prices)
→ Dict · Méthode statique

MACD (12,26,9) — retourne la ligne MACD, la ligne signal et l'histogramme.

python
macd = TechnicalIndicators.macd(close)
print(macd['macd'].tail())
print(macd['signal'].tail())
print(macd['histogram'].tail())
bollinger_bands(prices, window=20)
→ Dict · Méthode statique

Bandes de Bollinger (±2 écarts-types autour de la SMA).

python
bands = TechnicalIndicators.bollinger_bands(close, window=20)
print(f"Bande haute: {bands['upper'].iloc[-1]:.2f}")
print(f"Bande centrale: {bands['middle'].iloc[-1]:.2f}")
print(f"Bande basse: {bands['lower'].iloc[-1]:.2f}")

Formules mathématiques

Les équations fondamentales implémentées dans QuantFinance.

Black-Scholes

Prix d'un call européen
C = S · N(d₁) − K · erT · N(d₂)
d₁ = [ln(S/K) + (r + σ²/2)T] / (σT)
d₂ = d₁σT
S = Prix spot  ·  K = Strike  ·  T = Maturité  ·  r = Taux sans risque  ·  σ = Volatilité  ·  N(·) = Loi normale cumulée

Les Grecques

Sensibilités du prix de l'option
Δ (Delta) = N(d₁)
Γ (Gamma) = N′(d₁) / (S · σ · √T)
ν (Vega) = S · N′(d₁) · √T
Θ (Theta) = −[S·N′(d₁σ / (2√T)] − r·K·erT·N(d₂)
ρ (Rho) = K·T·erT·N(d₂)
N′(·) = densité de la loi normale standard

Value at Risk

VaR paramétrique (hypothèse de normalité)
VaRα = μzα · σ
CVaRα = μσ · φ(zα) / (1 − α)
zα = quantile de la loi normale pour α  ·  φ = densité normale  ·  α = niveau de confiance (ex: 0.95)

EWMA — RiskMetrics

Variance à pondération exponentielle décroissante
σ²t = λ · σ²t−1 + (1 − λ) · t−1
λ = facteur de décroissance (0.94 pour données journalières, 0.97 pour hebdomadaires)

Ratios de performance

Sharpe · Sortino · Calmar
Sharpe = (RpRf) / σp
Sortino = (RpRf) / σdownside
Calmar = Rannualisé / |MDD|
Rp = rendement portefeuille  ·  Rf = taux sans risque  ·  MDD = Maximum Drawdown

Optimisation de Markowitz

Problème d'optimisation quadratique
min wΣw    s.c.    w1 = 1,   w ≥ 0
max S(w) = (wμRf) / √(wΣw)
w = vecteur de poids  ·  Σ = matrice de covariance  ·  μ = rendements espérés

Duration & Convexité (Obligations)

Sensibilité aux taux d'intérêt
DMacaulay = Σ [ t · Ct · eyt ] / P
Dmodifiée = DMacaulay / (1 + y/m)
ΔP/P ≈ −Dmod · Δy + ½ · Convexité · (Δy
y = YTM  ·  m = fréquence de coupon  ·  P = prix de l'obligation

Comparaison des méthodes

Pricing d'options

MéthodeOptions américainesOptions exotiquesVitessePrécisionRecommandé pour
BlackScholes Européennes seul. ExacteOptions européennes vanille, calcul des grecques
BinomialTree Exercice anticipé ConvergeOptions américaines, options avec dividendes discrets
MonteCarlo Avec LSM Path-dependent StochastiqueOptions asiatiques, barrières, structures complexes

Calcul de VaR

MéthodeHypothèseQueues épaissesDonnées requisesVitesseRecommandé quand
historical_varAucune Capturées500+ joursDistribution non-normale, données historiques abondantes
parametric_varNormalité des rendements Sous-estimées30+ joursCalcul rapide, portefeuilles bien diversifiés
ewma_varNormalité + vol. variable Sous-estimées100+ joursVolatilité qui change dans le temps (λ = 0.94)
monte_carlo_varGBM ou modèle custom ConfigurablesParamètresPrécision maximale, portefeuilles non-linéaires

Optimisation de portefeuille

MéthodeInversion matriceRobustesseGrands universComplexitéRecommandé quand
equal_weightNulleBenchmark simple, manque de données
minimize_volatilityFaibleMinimiser le risque absolu, profil défensif
maximize_sharpeMoyenneRendement/risque optimal, rendements prévisibles
risk_parityMoyenneÉgaliser la contribution au risque de chaque actif
hierarchical_risk_parityÉlevéeGrands univers, instabilité numérique, fonds alternatifs

Stratégies de backtesting

StratégieTypeParamètresMarchés favorablesRisque sur-optimisation
BuyAndHoldPassiveAucunTendances longuesNul
MovingAverageCrossoverTendanceshort_window, long_windowMarchés directionnelsÉlevé (2 params)

Exemples complets

Des workflows de bout en bout combinant plusieurs modules.

Analyse complète d'une option
Pricing, grecques, volatilité implicite et comparaison des 3 modèles
python
from quantfinance.pricing.options importBlackScholes, BinomialTree, MonteCarlo

# Paramètres communs
params = dict(S=100, K=105, T=1, r=0.05, sigma=0.25, option_type='call')

# Comparaison des 3 modèles
bs = BlackScholes(**params)
bt = BinomialTree(**params, n_steps=500)
mc = MonteCarlo(**params, n_simulations=100000)

print(" Comparaison des prix ")
print(f"Black-Scholes : {bs.price():.4f}")
print(f"Binomial Tree : {bt.price():.4f}")
print(f"Monte Carlo : {mc.price():.4f}")

# Toutes les grecques d'un coup
print("
 Grecques ")
for name, val in {'Delta': bs.delta(), 'Gamma': bs.gamma(),
 'Vega': bs.vega(), 'Theta': bs.theta(), 'Rho': bs.rho()}.items():
 print(f" {name:6s}: {val:.6f}")

# Volatilité implicite depuis un prix de marché
impl_vol = bs.implied_volatility(market_price=9.50)
print(f"
Vol. implicite: {impl_vol:.2%}")

# Smile de volatilité
print("
 Smile de volatilité ")
for K in [85, 90, 95, 100, 105, 110, 115]:
 b = BlackScholes(S=100, K=K, T=1, r=0.05, sigma=0.25)
 print(f" K={K:3d}: prix={b.price():.2f} delta={b.delta():.3f}")
Pipeline données → optimisation → risque
Chargement, 4 stratégies d'optimisation, analyse de risque complète
python
from quantfinance.utils.data importDataLoader
from quantfinance.portfolio.optimization importPortfolioOptimizer, EfficientFrontier
from quantfinance.risk.var importVaRCalculator
from quantfinance.risk.metrics importRiskMetrics, PerformanceAnalyzer

# 1. Données
prices = DataLoader.generate_synthetic_prices(n_assets=6, n_days=756)
returns = prices.pct_change().dropna()

# 2. Comparer les 4 stratégies d'optimisation
opt = PortfolioOptimizer(returns, risk_free_rate=0.02)
strategies = {
 'Équipondéré': opt.equal_weight(),
 'Min Volatilité': opt.minimize_volatility(),
 'Max Sharpe': opt.maximize_sharpe(),
 'Risk Parity': opt.risk_parity(),
}
print(f"{'Stratégie':20s} {'Rendement':>10s} {'Volatilité':>12s} {'Sharpe':>8s}")
for name, res in strategies.items():
 print(f"{name:20s} {res['return']:>10.2%} {res['volatility']:>12.2%} {res['sharpe_ratio']:>8.3f}")

# 3. Analyse de risque sur le meilleur portefeuille
w = strategies['Max Sharpe']['weights']
port_ret = (returns * w).sum(axis=1)

print("
 Risque (Max Sharpe) ")
print(f"VaR 95% Historique : {VaRCalculator.historical_var(port_ret, 0.95):.2%}")
print(f"VaR 95% Paramétrique: {VaRCalculator.parametric_var(port_ret, 0.95):.2%}")
print(f"CVaR 95% : {VaRCalculator.expected_shortfall(port_ret, 0.95):.2%}")

# 4. Rapport complet
analyzer = PerformanceAnalyzer(port_ret, risk_free_rate=0.02)
print("
 Performance ")
print(analyzer.summary_statistics())
Backtest MA Crossover vs Buy & Hold
Comparaison de paramètres avec indicateurs techniques et coûts de transaction
python
from quantfinance.utils.data importDataLoader
from quantfinance.utils.indicators importTechnicalIndicators
from quantfinance.portfolio.backtesting importBacktester, MovingAverageCrossover, BuyAndHoldStrategy

# Données OHLCV sur 3 ans
data = DataLoader.generate_ohlcv_data(n_days=756)
close = data['Close']

# Indicateurs techniques pour analyse préalable
rsi = TechnicalIndicators.rsi(close)
macd = TechnicalIndicators.macd(close)
bands = TechnicalIndicators.bollinger_bands(close)
print(f"RSI: {rsi.iloc[-1]:.1f} | MACD: {macd['macd'].iloc[-1]:.4f}")
print(f"Bollinger: [{bands['lower'].iloc[-1]:.2f} — {bands['upper'].iloc[-1]:.2f}]")

# Comparer plusieurs combinaisons de MA
print(f"
{'Stratégie':18s} {'Rendement':>10s} {'Sharpe':>8s} {'Max DD':>8s}")
for short, long in [(10, 30), (20, 50), (50, 200)]:
 bt = Backtester(data, MovingAverageCrossover(short, long),
 initial_capital=10000, commission=0.001)
 res = bt.run()
 print(f"MA({short},{long}){' ':10s} {res['Total Return']:>10.2%} {res['Sharpe Ratio']:>8.3f} {res['Max Drawdown']:>8.2%}")

# Benchmark Buy & Hold
bh = Backtester(data, BuyAndHoldStrategy(), initial_capital=10000)
res = bh.run()
print(f"Buy & Hold {res['Total Return']:>10.2%} {res['Sharpe Ratio']:>8.3f} {res['Max Drawdown']:>8.2%}")
Stress testing & rééquilibrage
Tests de résistance sur crises historiques et rééquilibrage automatique
python
from quantfinance.utils.data importDataLoader
from quantfinance.portfolio.optimization importPortfolioOptimizer
from quantfinance.portfolio.rebalancing importRebalancer
from quantfinance.risk.stress importStressTesting

# Setup
prices = DataLoader.generate_synthetic_prices(n_assets=4, n_days=756)
returns = prices.pct_change().dropna()
opt = PortfolioOptimizer(returns, risk_free_rate=0.02)
weights = opt.maximize_sharpe()['weights']

# Stress testing sur crises historiques
print(" Scénarios historiques ")
scenarios = StressTesting.historical_scenarios(returns)
print(scenarios)

# Scénario personnalisé : crash actions + hausse taux
shocks = {'equity': -0.35, 'rates': 0.025, 'credit': 0.015}
pnl = StressTesting.custom_scenario(returns, shocks)
print(f"
Scénario crise 2008 amplifié: {pnl:.2%}")

# Rééquilibrage périodique vs par seuil
rebalancer = Rebalancer(weights, prices, initial_capital=100000, transaction_cost=0.001)

res_monthly = rebalancer.periodic_rebalancing(frequency='monthly')
res_threshold = rebalancer.threshold_rebalancing(threshold=0.05)

print(f"
Rééquilibrage mensuel — transactions: {len(res_monthly)}")
print(f"Rééquilibrage seuil 5% — transactions: {len(res_threshold)}")

Tests

bash
# Tous les tests
pytest

# Avec rapport de couverture HTML
pytest --cov=quantfinance --cov-report=html

# Tests rapides uniquement
pytest -m "not slow"

# Un module spécifique
pytest tests/test_pricing.py -v

Guide de contribution

  1. Fork le dépôt sur GitHub
  2. Créez une branche : git checkout -b feature/ma-fonctionnalite
  3. Implémentez avec des tests unitaires
  4. Committez : git commit -m 'feat: description'
  5. Pushez : git push origin feature/ma-fonctionnalite
  6. Ouvrez une Pull Request sur GitHub

Auteur : Marcel ALOEKPO

Made with for quantitative finance · MIT License