Die prozentuale Veränderung
Preise lassen sich schwer vergleichen: Eine Bewegung von 5 $ bedeutet bei einer 20-$-Aktie etwas anderes als bei einer 500-$-Aktie. Eine einfache Rendite nimmt die Skala heraus. Sie ist die Preisänderung als Bruchteil des vorherigen Preises, sodass +2 % bei jedem Wert dasselbe bedeutet.
In pandas verschiebt prices.shift(1) jeden Wert um einen Schritt nach unten, sodass jede Zeile den vorherigen Preis sieht. Die Division durch ihn berechnet alle Renditen auf einmal, und die erste ist NaN, weil es keinen Vorgängerpreis gibt. prices.pct_change() tut dasselbe in einem Aufruf.
import pandas as pd
prices = pd.Series([100.0, 102.0, 100.98, 103.0], index=["Mon", "Tue", "Wed", "Thu"])
returns = prices / prices.shift(1) - 1
print(returns.round(4))
# Mon NaN
# Tue 0.02
# Wed -0.01
# Thu 0.02
# dtype: float64
print(prices.pct_change().round(4).tolist()) # [nan, 0.02, -0.01, 0.02]Renditen verketten sich, sie addieren sich nicht
Ein Tag mit +2 % und danach einer mit -1 % ergibt nicht +1 % in zwei Tagen. Jede Rendite gilt für einen anderen Ausgangspreis, daher multiplizieren sich die Wachstumsfaktoren 1 + r: Die Gesamtrendite ist (1 + r1) * (1 + r2) * ... - 1. Lassen Sie das NaN der ersten Zeile weg (prod ignoriert es), dann entspricht das Ergebnis dem Endpreis geteilt durch den Anfangspreis minus eins. Die Renditen zu addieren kommt nah heran, ist aber falsch, und die Lücke wächst mit der Volatilität.
# Compounding the returns gives the same total as the end price over the start price
returns = prices.pct_change().dropna()
print(round((1 + returns).prod() - 1, 6)) # 0.03
print(round(prices.iloc[-1] / prices.iloc[0] - 1, 6)) # 0.03
print(round(returns.sum(), 6)) # 0.030004 close, but not the same
print((1 + returns).cumprod().round(4).tolist()) # [1.02, 1.0098, 1.03] the growth of 1 dollar# Gains and losses are not symmetric: +10% then -10% does not get you back
up, down = 0.10, -0.10
print(round((1 + up) * (1 + down) - 1, 4)) # -0.01
# After a -50% loss you need +100% to recover
print(1 / (1 - 0.5) - 1) # 1.0Über Werte und über Jahre
Einfache Renditen addieren sich über Werte hinweg: Die Rendite eines Portfolios in einer Periode ist die Summe von Gewicht * Rendite seiner Positionen. Bei Series ordnet weights * returns die Einträge nach Label zu, die Reihenfolge der Symbole spielt also keine Rolle (mit .values verlieren Sie diesen Schutz). Über die Zeit addieren sie sich nicht, sondern verketten sich: Für eine Jahresrate aus einem kürzeren Zeitraum wird der Wachstumsfaktor mit Handelstage / Tage potenziert.
# Weights times returns, matched by label (the order of the symbols differs on purpose)
weights = pd.Series({"AAPL": 0.5, "MSFT": 0.3, "TSLA": 0.2})
day = pd.Series({"TSLA": -0.03, "AAPL": 0.012, "MSFT": 0.004})
print(round((weights * day).sum(), 4)) # 0.0012 correct: AAPL with AAPL, TSLA with TSLA
print(round((weights.values * day.values).sum(), 4)) # -0.0106 wrong: position by position, TSLA met AAPL's weight# Annualising: 5% in 60 trading days, if it kept compounding at that pace for a year of 252 days
total, days = 0.05, 60
print(round((1 + total) ** (252 / days) - 1, 4)) # 0.2274daily_returns.- Eine einfache Rendite ist
Preis / Vorgängerpreis - 1:prices / prices.shift(1) - 1oderprices.pct_change(); der erste Eintrag istNaN. - Über die Zeit verketten sich Renditen:
(1 + returns).prod() - 1, niereturns.sum(). - Über Werte hinweg addieren sie sich mit Gewichten:
(weights * returns).sum(), nach Label zugeordnet.
Als Nächstes: Log-Renditen, eine Alternative, die sich über die Zeit addiert und die übliche Wahl für Statistik ist.
Lab: Was bewertet wird
- Mit
simple_returnsPreise in Periodenrenditen umwandeln25 Pkt. - Mit
cumulative_returndie Renditen zu einer Gesamtrendite verketten25 Pkt. - Mit
portfolio_returndie Renditen der Werte zu einer Zahl gewichten25 Pkt. - Mit
annualized_returneine Rendite als Jahresrate ausdrücken25 Pkt.