El logaritmo del cociente de precios
Un rendimiento logarítmico es el logaritmo natural del cociente entre dos precios. En lugar de P_t / P_{t-1} - 1 se calcula ln(P_t / P_{t-1}). Para movimientos pequeños ambos son casi iguales (una subida del 2 % es 0,0198 en logaritmos), pero la versión logarítmica tiene propiedades que la hacen la elección estándar en estadística y modelado. En pandas, toma el logaritmo de toda la Series de precios con np.log y aplica diff: restar logaritmos es dividir precios.
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 comparisonLos rendimientos logarítmicos se suman en el tiempo
Esta es la razón principal para usarlos. Como ln(a) - ln(c) = (ln(a) - ln(b)) + (ln(b) - ln(c)), el rendimiento logarítmico de varios periodos es simplemente la suma de los rendimientos por periodo, sin composición. El rendimiento de dos días es la suma de dos diarios, el mensual es la suma de los diarios, y exp(total) - 1 devuelve el resultado a un rendimiento simple. Los rendimientos logarítmicos también son simétricos: un precio que se duplica y luego se reduce a la mitad tiene rendimientos logarítmicos de +0,6931 y -0,6931, que se anulan, mientras que los simples eran +100 % y -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]Conversión y movimientos de varios días
Para volver a un rendimiento simple usa np.exp(r) - 1 (o np.expm1(r), más preciso para valores muy pequeños). Ambos coinciden bien en movimientos pequeños y se separan en los grandes: un rendimiento simple de +50 % es 0,4055 en logaritmos. np.log y np.exp trabajan sobre una Series entera, así que no hay bucle. Para medir un movimiento de varios días, diff acepta un número de periodos: np.log(prices).diff(5) es el rendimiento logarítmico de cinco días, igual a la suma de cinco rendimientos logarítmicos diarios.
# 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 rendimiento logarítmico es
ln(precio / precio anterior):np.log(prices).diff(); la primera entrada esNaN. - En el tiempo simplemente se suman:
log_ret.sum(), ynp.exp(total) - 1da el rendimiento simple. diff(k)da el movimiento dekperiodos; entre activos, pondera los rendimientos simples, no los logarítmicos.
A continuación: DatetimeIndex e indexación temporal, para dar fechas reales a una serie de precios y cortarla por mes o por día de la semana.
Lab: qué se califica
- Convertir precios en rendimientos logarítmicos con
log_returns25 pts - Convertir rendimientos logarítmicos en simples con
simple_from_log25 pts - Sumar los rendimientos logarítmicos por periodo con
total_log_return25 pts - Medir el movimiento de varios días con
multi_day_log_return25 pts