#!/usr/bin/env python3 # Robuste_Optimierung.py """ Kapitel Unsicherheit: Robuste Portfolio-Optimierung. Vergleicht drei Haltungen zur Unsicherheit: (a) nominal - vertraut den Punktschaetzungen blind (b) robust - sichert gegen Box-Unsicherheit ab (Worst Case) (c) stochastisch - optimiert den Erwartungswert ueber Szenarien und misst den "Preis der Robustheit": Wie viel Ertrag kostet die Absicherung im Normalfall - und wie viel Verlust erspart sie im Ernstfall? """ import cvxpy as cp import numpy as np ASSETS = ["Aktien Welt", "Anleihen", "Rohstoffe", "Immobilien"] MU_SCHAETZUNG = np.array([0.085, 0.030, 0.055, 0.060]) # Punktschaetzung UNSICHERHEIT = np.array([0.040, 0.008, 0.045, 0.025]) # +/- delta je Titel VOLA = np.array([0.17, 0.05, 0.22, 0.12]) KORR = np.array([ [1.00, -0.15, 0.35, 0.55], [-0.15, 1.00, -0.05, 0.10], [0.35, -0.05, 1.00, 0.25], [0.55, 0.10, 0.25, 1.00], ]) SIGMA = np.diag(VOLA) @ KORR @ np.diag(VOLA) LAMBDA = 4.0 # Risikoaversion N = len(ASSETS) def optimiere(mu_effektiv): """Standard-Mean-Variance mit vorgegebenem Renditevektor.""" w = cp.Variable(N, nonneg=True) ziel = cp.Maximize(mu_effektiv @ w - 0.5 * LAMBDA * cp.quad_form(w, SIGMA)) problem = cp.Problem(ziel, [cp.sum(w) == 1]) problem.solve() return w.value def kennzahlen(w, mu): ertrag = float(mu @ w) risiko = float(np.sqrt(w @ SIGMA @ w)) return ertrag, risiko if __name__ == "__main__": print("=" * 88) print(" ROBUSTE vs. NOMINALE PORTFOLIO-OPTIMIERUNG") print("=" * 88) print(f"{'Asset':<14} {'Erw. Rendite':>14} {'Unsicherheit':>14} " f"{'Worst Case':>12} {'Volatilitaet':>13}") print("-" * 88) for i, name in enumerate(ASSETS): print(f"{name:<14} {MU_SCHAETZUNG[i]*100:>13.1f} % " f"{'+/- ' + format(UNSICHERHEIT[i]*100, '.1f') + ' %':>14} " f"{(MU_SCHAETZUNG[i]-UNSICHERHEIT[i])*100:>11.1f} % " f"{VOLA[i]*100:>12.1f} %") # (a) nominal: vertraut den Schaetzungen w_nominal = optimiere(MU_SCHAETZUNG) # (b) robust: rechnet mit dem Worst Case der Box-Unsicherheitsmenge w_robust = optimiere(MU_SCHAETZUNG - UNSICHERHEIT) print("\n" + "-" * 88) print(f"{'':<14} {'nominal':>22} {'robust':>22}") print("-" * 88) for i, name in enumerate(ASSETS): print(f"{name:<14} {w_nominal[i]*100:>21.1f} % {w_robust[i]*100:>21.1f} %") # --- Bewertung in beiden Welten -------------------------------------- mu_worst = MU_SCHAETZUNG - UNSICHERHEIT e_nom_gut, r_nom = kennzahlen(w_nominal, MU_SCHAETZUNG) e_rob_gut, r_rob = kennzahlen(w_robust, MU_SCHAETZUNG) e_nom_schlecht, _ = kennzahlen(w_nominal, mu_worst) e_rob_schlecht, _ = kennzahlen(w_robust, mu_worst) print("\n" + "-" * 88) print(f"{'Bewertung':<34} {'nominales Portfolio':>22} {'robustes Portfolio':>22}") print("-" * 88) print(f"{'Ertrag, wenn Schaetzung stimmt':<34} {e_nom_gut*100:>21.2f} % " f"{e_rob_gut*100:>21.2f} %") print(f"{'Ertrag im Worst Case':<34} {e_nom_schlecht*100:>21.2f} % " f"{e_rob_schlecht*100:>21.2f} %") print(f"{'Volatilitaet':<34} {r_nom*100:>21.2f} % {r_rob*100:>21.2f} %") print("\n" + "-" * 88) print(f"Preis der Robustheit (Ertragsverzicht im Normalfall): " f"{(e_nom_gut - e_rob_gut)*100:+.2f} Prozentpunkte") print(f"Nutzen der Robustheit (Vorteil im Worst Case): " f"{(e_rob_schlecht - e_nom_schlecht)*100:+.2f} Prozentpunkte") verhaeltnis = ((e_rob_schlecht - e_nom_schlecht) / max(e_nom_gut - e_rob_gut, 1e-9)) print(f"Verhaeltnis Nutzen/Preis: {verhaeltnis:.2f}") print(" -> Werte > 1 bedeuten: Die Absicherung bringt im Ernstfall mehr,") print(" als sie im Normalfall kostet.") print("=" * 88)