Pubié le

Maximiser les rendements de son Portfolio avec la frontière efficiente

Authors

Contexte

Définitions

Lorsque l'on souhaite réaliser un portefeuille de titre financier, on est confronté à un problème de répartition. Comment doit on allouer les parts pour chaque titre ?

Markowitz pose deux hypothèses fondamentales :

  • Les investisseurs cherchent à maximiser les rendements
  • La variance des rendements est indésirable.

On définit un choix de répartition par le vecteur suivant :

w=(w1w2wn)w = \begin{pmatrix} w_1 \\ w_2 \\ ⋮ \\ w_n \end{pmatrix}

L'objectif ici est de trouver la répartition qui génére le plus grand rendement E(R)E(R). Par définition du poids, chaque proposition d'allocation doit valoir un.

i=1nwi=1\sum_{i=1}^{n} w_i = 1

Supposons que YY est une variable aléatoire représentant un titre financier avec des valeurs possibles y1,...,yny_1,...,y_n et des probabilités associées p1,...,pnp_1,...,p_n. Le rendement attendu (espérance) de YY est donnée par :

E(Y)=i=1npiyiE(Y) = \sum_{i=1}^{n} p_iy_i \\
  • Par exemple, une action X a 50% de chance de rapporter 10% et 50% de chance de rapporter 6%. E(Y)=0.510%+0.56%=8% E(Y) = 0.5 * 10\% + 0.5 * 6\% = 8\%

La variance mesure la dispersion des valeurs autour de la moyenne (l'espérance).

V=i=1npi(yiE(yi))2V = \sum_{i=1}^{n} p_i(y_i-E(y_i))^2 \\

Markowitz utilise l'écart type σ\sigma pour mesurer le risque.

σ=(V)\sigma = \sqrt(V)

Pour un portefeuille R constituée de plusieurs titres R1,...,RnR_1,...,R_n avec des pondérations α1,...,αn\alpha_1,...,\alpha_n, le rendement du portefeuille attendu est :

E(R)=i=1nαiE(Ri)E(R) = \sum_{i=1}^{n} \alpha_iE(R_i)

La covariance mesure la relation entre deux variables aléatoires R1R_1 et R2R_2

cov(R1,R2)=E[(R1E(R1))(R2E(R2))]cov(R_1,R_2) = E[(R_1-E(R_1))(R_2-E(R_2))]

Grâce à cette relation, on va pouvoir obtenir la matrice de covariance entre tous les actifs

Σ=(σ12cov12cov1ncov21σ22cov2ncovn1covn2σn2)\Sigma = \begin{pmatrix} \sigma_1^2 & cov_{12} & ⋯ & cov_{1n} \\ cov_{21} & \sigma_2^2 & ⋯ & cov_{2n} \\ ⋮ & ⋮ & ⋱ & ⋮ \\ cov_{n1} & cov{n2} & ⋯ & \sigma_n^2 \end{pmatrix}

Pour un portefeuille composé de n actifs, avec wiw_i représentant le poids du i-ème actif et covijcov_{ij} représentant la covariance entre les actifs i et j, la variance du portefeuille est :

V(R)=i=1nj=1nwiwjcovijV(R) = \sum_{i=1}^{n}\sum_{j=1}^{n}w_iw_jcov_{ij}

La formule de la variance devient :

V(R)=wTΣwV(R) = w^T \Sigma w

En 1964, William Sharpe définit un nouvel indice, le ratio de Sharpe qui permet de mesurer la performance d'un portefeuille ajustée au risque. Plus ce ratio est important, plus le portefeuille est intéressant car il indique la performance est plus importante que le risque pris.

Sa=E(R)σ(R)S_a = \frac{E(R)}{\sigma(R)}

Il suffit de calculer le ratio de sharpe pour chaque allocation choisie, et d'afficher les poids qui réalise ce maximum.

Application

L'algorithme suivant permet de trouver ces coefficients optimaux. Ils récupérent les données de rendement des titres ainsi que la matrice de corrélation entre tous ses actifs. Puis on va générer aléatoirement un nombre fixe de portefeuille pour trouver celui réalise le plus grand ratio de sharpe ainsi que celui avec le moins de volatilité.

Tux, the Linux mascot
import numpy as np
import matplotlib.pyplot as plt
import mpld3
from mpld3 import plugins

# Data sur le marché : rendements attendus et matrice de covariance
expected_returns = np.array([0.10, 0.12, 0.08])
cov_matrix = np.array([
    [0.04, 0.01, 0.005],
    [0.01, 0.09, 0.002],
    [0.005, 0.002, 0.01]
])

# Générer le portfolio de 10000 portefeuilles aléatoires
num_portfolios = 10000
results = np.zeros((5, num_portfolios))  # Rendement, Volatilité, Sharpe Ratio, Poids de l'actif 1, Poids de l'actif 2

for i in range(num_portfolios):
    weights = np.random.random(3)
    print(weights)
    weights /= np.sum(weights)  # Normaliser pour que la somme des poids fasse 1.
    
    # Calculer le rendement du portefeuille
    portfolio_return = np.dot(weights, expected_returns)
    
    # Calculer la volatilité du portefeuille
    portfolio_variance = np.dot(weights.T, np.dot(cov_matrix, weights))
    portfolio_std_dev = np.sqrt(portfolio_variance)
    
    # Enregistrer les résultats
    results[0, i] = portfolio_return
    results[1, i] = portfolio_std_dev
    results[2, i] = results[0, i] / results[1, i]  # Sharpe Ratio
    results[3, i] = weights[0]
    results[4, i] = weights[1]

# Trouver le portefeuille avec le ratio de Sharpe le plus élevé, et le portefeuille avec la volatilité minimale
max_sharpe_idx = np.argmax(results[2])
max_sharpe_return = results[0, max_sharpe_idx]
max_sharpe_std_dev = results[1, max_sharpe_idx]

min_vol_idx = np.argmin(results[1])
min_vol_return = results[0, min_vol_idx]
min_vol_std_dev = results[1, min_vol_idx]

# Créer le graphique
fig, ax = plt.subplots(figsize=(7,3.5))
scatter = ax.scatter(results[1, :], results[0, :], c=results[2, :], cmap='YlGnBu_r', marker='o')

max_sharpe_scatter = ax.scatter(max_sharpe_std_dev, max_sharpe_return, marker='*', color='r', s=100, label='Max Sharpe Ratio')
min_vol_scatter = ax.scatter(min_vol_std_dev, min_vol_return, marker='*', color='g', s=100, label='Min Volatilité')
ax.set_title('Frontière efficiente')
ax.set_xlabel('Risque (Volatilité)')
ax.set_ylabel('Rendement')
ax.legend()

# Créer des étiquettes pour chaque point
labels = []
for i in range(num_portfolios):
    label = f'Return: {results[0, i]:.2f}, Risk: {results[1, i]:.2f}\nWeights: [{results[3, i]:.2f}, {results[4, i]:.2f}, {1 - results[3, i] - results[4, i]:.2f}]'
    labels.append(label)

tooltip = plugins.PointLabelTooltip(scatter, labels=labels)
plugins.connect(fig, tooltip)

# Ajouter des étiquettes pour les points max_sharpe et min_vol
max_sharpe_label = [f'Return: {max_sharpe_return:.2f}, Risk: {max_sharpe_std_dev:.2f}\nWeights: [{results[3, max_sharpe_idx]:.2f}, {results[4, max_sharpe_idx]:.2f}, {1 - results[3, max_sharpe_idx] - results[4, max_sharpe_idx]:.2f}]']
min_vol_label = [f'Return: {min_vol_return:.2f}, Risk: {min_vol_std_dev:.2f}\nWeights: [{results[3, min_vol_idx]:.2f}, {results[4, min_vol_idx]:.2f}, {1 - results[3, min_vol_idx] - results[4, min_vol_idx]:.2f}]']

max_sharpe_tooltip = plugins.PointLabelTooltip(max_sharpe_scatter, labels=max_sharpe_label)
min_vol_tooltip = plugins.PointLabelTooltip(min_vol_scatter, labels=min_vol_label)

plugins.connect(fig, max_sharpe_tooltip)
plugins.connect(fig, min_vol_tooltip)

# Afficher le graphique
mpld3.show()

Ressources