Das Risiko für jedes Renditeniveau
Das vorige Thema fand für eine Zielrendite die Gewichte mit der kleinsten Varianz. Tut man das für jedes Ziel und trägt die Volatilität horizontal gegen die Rendite vertikal auf, bilden die Punkte eine Kurve, die Effizienzlinie. Jedes Portfolio aus diesen Assets liegt darauf oder rechts davon. Ein Portfolio innerhalb der Kurve ist verschwenderisch, denn ein anderes bietet dieselbe Rendite mit weniger Risiko oder mehr Rendite bei gleichem Risiko. Markowitz' Einsicht: Anleger sollten nur die Kurve betrachten.
import numpy as np
mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
[0.0060, 0.0100, 0.0020],
[0.0100, 0.0020, 0.0225]])
def target_return_weights(mu, cov, target):
ones = np.ones(len(mu))
s1, sm = np.linalg.solve(cov, ones), np.linalg.solve(cov, mu)
a, b, c = ones @ s1, ones @ sm, mu @ sm
d = a * c - b ** 2
return ((c - b * target) * s1 + (a * target - b) * sm) / d
for target in (0.05, 0.06, 0.07, 0.08, 0.09, 0.10):
w = target_return_weights(mu, cov, target)
print(target, round(float(np.sqrt(w @ cov @ w)), 4))Ihre Form und ihre Spitze
Die Linie ist eine Parabel in (Rendite, Varianz) und eine Hyperbel, wenn man die Volatilität aufträgt. Die Varianz bei Zielrendite t ist (a * t**2 - 2 * b * t + c) / (a * c - b**2), mit denselben vier Zahlen a, b, c wie zuvor. Ihr tiefster Punkt ist das globale Minimum-Varianz-Portfolio bei Rendite b / a: die Spitze der Kurve, das Minimum-Varianz-Portfolio von vor zwei Themen. Nur der Teil von der Spitze aufwärts ist die Effizienzlinie; der untere Ast ist ein Spiegelbild mit weniger Rendite bei gleichem Risiko.
# The tip of the curve: the minimum-variance portfolio and its return
import numpy as np
mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
[0.0060, 0.0100, 0.0020],
[0.0100, 0.0020, 0.0225]])
ones = np.ones(3)
x = np.linalg.solve(cov, ones)
w = x / x.sum()
print(np.round(w, 4), round(float(w @ mu), 4), round(float(np.sqrt(w @ cov @ w)), 4)) # the tip: return, volatility
def closed_form_vol(target):
s1, sm = x, np.linalg.solve(cov, mu)
a, b, c = ones @ s1, ones @ sm, mu @ sm
return float(np.sqrt((a * target ** 2 - 2 * b * target + c) / (a * c - b ** 2)))
for target in (0.05, 0.0642, 0.07):
print(target, round(closed_form_vol(target), 4))Effiziente und ineffiziente Punkte
Dasselbe Risiko erscheint auf der Kurve bei zwei Renditen, eine auf jedem Ast, und nur die höhere lohnt das Halten. Ein Ziel ist also genau dann effizient, wenn es mindestens der Minimum-Varianz-Rendite entspricht. Der Test ist ein einziger Vergleich, entscheidet aber, welche Ziele in eine Auswahlgrafik gehören. Die Suche nach dem besten Kompromiss auf der Linie wird dann zur Wahl eines Punkts, und eine übliche Antwort ist der mit der meisten Rendite je Risikoeinheit über dem risikolosen Zins.
# The point with the highest Sharpe ratio among candidate targets
import numpy as np
mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
[0.0060, 0.0100, 0.0020],
[0.0100, 0.0020, 0.0225]])
rf = 0.02
ones = np.ones(3)
s1, sm = np.linalg.solve(cov, ones), np.linalg.solve(cov, mu)
a, b, c = ones @ s1, ones @ sm, mu @ sm
d = a * c - b ** 2
targets = np.linspace(0.05, 0.10, 11)
vols = np.sqrt((a * targets ** 2 - 2 * b * targets + c) / d)
sharpe = (targets - rf) / vols
for t, v, s in zip(targets, vols, sharpe):
print(round(float(t), 3), round(float(v), 4), round(float(s), 4))
print("best target:", round(float(targets[np.argmax(sharpe)]), 3))# Efficient or not: compare a target with the return of the minimum-variance portfolio
import numpy as np
mu = np.array([0.08, 0.05, 0.10])
cov = np.array([[0.0400, 0.0060, 0.0100],
[0.0060, 0.0100, 0.0020],
[0.0100, 0.0020, 0.0225]])
x = np.linalg.solve(cov, np.ones(3))
tip = float((x / x.sum()) @ mu)
for target in (0.05, 0.06, 0.07, 0.08):
print(target, "efficient" if target >= tip else "inefficient")mu und cov gezeichnet, beide geschätzt, ist also selbst eine Schätzung, und eine schmeichelhafte: Der Optimierer hat unter allen Mischungen jene gewählt, deren geschätzte Renditen zufällig hoch und Risiken niedrig sind. Reale Ergebnisse landen rechts und unterhalb der sichtbaren Linie. Resampling der Daten oder der Vergleich der Linie der ersten Stichprobenhälfte mit ihrer Wertentwicklung in der zweiten zeigt, wie viel der Kurve Glück war. Sehen Sie die Effizienzlinie als Karte der Zielkonflikte, nicht als Versprechen.- Die Effizienzlinie liefert für jede Zielrendite die kleinste Volatilität:
sqrt(w @ cov @ w)mitw = target_return_weights(...); Portfolios darin sind verschwenderisch. - Ihre Spitze ist das globale Minimum-Varianz-Portfolio bei Rendite
b / a; nur Ziele auf oder über dieser Rendite sind effizient. - Wählen Sie einen Punkt nach einem Kriterium wie der höchsten Sharpe-Ratio
(target - rf) / volatility; die Kurve entsteht aus Schätzungen und ist optimistisch.
Als Nächstes: ein vektorisierter Backtester, eine Handelsregel an historischen Kursen testen.
Lab: Was bewertet wird
- Mit
frontier_volsdie Effizienzlinie zeichnen25 Pkt. - Mit
min_variance_returndie Spitze der Kurve finden25 Pkt. - Mit
is_efficientdie gute von der schlechten Hälfte unterscheiden25 Pkt. - Mit
max_sharpe_targetden besten Punkt wählen25 Pkt.