Le logarithme du rapport de prix
Un rendement logarithmique est le logarithme népérien du rapport entre deux prix. Au lieu de P_t / P_{t-1} - 1, on calcule ln(P_t / P_{t-1}). Pour de petits mouvements, les deux sont presque égaux (une hausse de 2 % donne 0,0198 en logs), mais la version logarithmique a des propriétés qui en font le choix standard en statistique et en modélisation. Avec pandas, prenez le logarithme de toute la Series de prix avec np.log, puis appliquez diff : soustraire des logarithmes revient à diviser des prix.
import numpy as np
import pandas as pd
prices = pd.Series([100.0, 102.0, 100.98, 103.0], index=["Mon", "Tue", "Wed", "Thu"])
log_ret = np.log(prices).diff()
print(log_ret.round(4))
# Mon NaN
# Tue 0.0198
# Wed -0.0101
# Thu 0.0198
# dtype: float64
print(prices.pct_change().round(4).tolist()) # [nan, 0.02, -0.01, 0.02] the simple returns, for comparisonLes rendements logarithmiques s'additionnent dans le temps
C'est la principale raison de les utiliser. Comme ln(a) - ln(c) = (ln(a) - ln(b)) + (ln(b) - ln(c)), le rendement logarithmique sur plusieurs périodes est simplement la somme des rendements par période, sans composition. Le rendement sur deux jours est la somme de deux rendements quotidiens, le rendement mensuel est la somme des quotidiens, et exp(total) - 1 ramène le résultat à un rendement simple. Les rendements logarithmiques sont aussi symétriques : un prix qui double puis est divisé par deux a des rendements logarithmiques de +0,6931 et -0,6931, qui s'annulent, alors que les rendements simples étaient +100 % et -50 %.
# The sum of the daily log returns is the log return over the whole period
print(round(log_ret.sum(), 6)) # 0.029559
print(round(np.log(prices.iloc[-1] / prices.iloc[0]), 6)) # 0.029559 end price over start price
# exp(total) - 1 is the simple return of the whole period
print(round(np.exp(log_ret.sum()) - 1, 6)) # 0.03# Symmetry: the price doubles, then halves
doubling = pd.Series([100.0, 200.0, 100.0])
print(doubling.pct_change().tolist()) # [nan, 1.0, -0.5]
print(np.log(doubling).diff().round(4).tolist()) # [nan, 0.6931, -0.6931]Conversion, et mouvements sur plusieurs jours
Pour revenir à un rendement simple, utilisez np.exp(r) - 1 (ou np.expm1(r), plus précis pour de très petites valeurs). Les deux concordent bien pour de petits mouvements et divergent pour de grands : un rendement simple de +50 % vaut 0,4055 en logs. np.log et np.exp travaillent sur une Series entière, donc pas de boucle. Pour mesurer un mouvement sur plusieurs jours, diff accepte un nombre de périodes : np.log(prices).diff(5) est le rendement logarithmique sur cinq jours, égal à la somme de cinq rendements logarithmiques quotidiens.
# Small moves agree with the simple return, big moves do not
for simple in [0.01, 0.1, 0.5, -0.5]:
print(simple, round(np.log1p(simple), 4)) # log1p(r) is ln(1 + r)
# 0.01 0.01
# 0.1 0.0953
# 0.5 0.4055
# -0.5 -0.6931
# Back to simple returns, on the whole Series at once
print((np.exp(log_ret) - 1).round(4).tolist()) # [nan, 0.02, -0.01, 0.02]
# The log return over 2 days: diff takes a number of periods
print(np.log(prices).diff(2).round(4).tolist()) # [nan, nan, 0.0098, 0.0098]- Un rendement logarithmique vaut
ln(prix / prix précédent):np.log(prices).diff(); la première entrée estNaN. - Dans le temps, ils s'additionnent simplement :
log_ret.sum(), etnp.exp(total) - 1donne le rendement simple. diff(k)donne le mouvement surkpériodes ; entre actifs, pondérez les rendements simples, pas les logarithmiques.
À suivre : DatetimeIndex et indexation temporelle, pour donner de vraies dates à une série de prix et la découper par mois ou par jour de la semaine.
Lab : ce qui est noté
- Transformer des prix en rendements logarithmiques avec
log_returns25 pts - Reconvertir des rendements logarithmiques en rendements simples avec
simple_from_log25 pts - Additionner les rendements logarithmiques par période avec
total_log_return25 pts - Mesurer le mouvement sur plusieurs jours avec
multi_day_log_return25 pts