O logaritmo da razão de preços
Um retorno logarítmico é o logaritmo natural da razão entre dois preços. Em vez de P_t / P_{t-1} - 1 calcula-se ln(P_t / P_{t-1}). Para movimentos pequenos os dois são quase iguais (uma alta de 2% é 0,0198 em logaritmos), mas a versão logarítmica tem propriedades que a tornam a escolha padrão em estatística e modelagem. No pandas, tire o logaritmo de toda a Series de preços com np.log e aplique diff: subtrair logaritmos é dividir preços.
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 comparisonOs retornos logarítmicos se somam no tempo
Esta é a principal razão para usá-los. Como ln(a) - ln(c) = (ln(a) - ln(b)) + (ln(b) - ln(c)), o retorno logarítmico de vários períodos é simplesmente a soma dos retornos de cada período, sem composição. O retorno de dois dias é a soma de dois diários, o mensal é a soma dos diários, e exp(total) - 1 devolve o resultado a um retorno simples. Os retornos logarítmicos também são simétricos: um preço que dobra e depois cai pela metade tem retornos logarítmicos de +0,6931 e -0,6931, que se anulam, enquanto os simples eram +100% e -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]Conversão e movimentos de vários dias
Para voltar a um retorno simples use np.exp(r) - 1 (ou np.expm1(r), mais preciso para valores minúsculos). Os dois concordam bem em movimentos pequenos e se afastam nos grandes: um retorno simples de +50% vale 0,4055 em logaritmos. np.log e np.exp trabalham numa Series inteira, então não há laço. Para medir um movimento de vários dias, diff aceita um número de períodos: np.log(prices).diff(5) é o retorno logarítmico de cinco dias, igual à soma de cinco retornos logarítmicos diários.
# 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]- Um retorno logarítmico é
ln(preço / preço anterior):np.log(prices).diff(); a primeira entrada éNaN. - No tempo simplesmente se somam:
log_ret.sum(), enp.exp(total) - 1dá o retorno simples. diff(k)dá o movimento dekperíodos; entre ativos, pondere os retornos simples, não os logarítmicos.
A seguir: DatetimeIndex e indexação temporal, para dar datas reais a uma série de preços e fatiá-la por mês ou dia da semana.
Lab: o que é avaliado
- Transformar preços em retornos logarítmicos com
log_returns25 pts - Converter retornos logarítmicos em simples com
simple_from_log25 pts - Somar os retornos logarítmicos por período com
total_log_return25 pts - Medir o movimento de vários dias com
multi_day_log_return25 pts