Version 04 als eigenes Repository
Erster Commit des Strangs "Optimierte Entscheidungsfindung mit Python" (Version 04). Die Historie der 71 Commits bis zur Trennung bleibt im uebergeordneten Repository OR_mit_Python liegen, das ab jetzt nur noch Version_03 (eingefroren) verwaltet und Version_04/ ignoriert. Bewusst kein "git subtree split": Der Pfad Version_04/ existiert erst seit der Verzeichnistrennung, ein Split braechte daher nur 7 der 41 einschlaegigen Commits - eine Teilhistorie, die vollstaendig aussieht und es nicht ist. Stand: 5 Teile, 23 Kapitel, 5 Anhaenge, 292 Abschnitte, 703 Querverweise, 325 Indexmarken, 73 Beispielprogramme, 32 SVGs, 4 Plotly-Figuren, 25 Notebooks, PDF mit 715 Seiten. Zusaetzlich in diesem Commit: * pyproject.toml mit Abhaengigkeitsgruppen finance, large-scale, api, figures, dev, empfehlungen. Die abgedruckte requirements.txt bleibt unveraendert daneben bestehen. ortools steht in der Grundausstattung, highspy erst in [large-scale] - so kann der HiGHS-Symbolkonflikt bei der schlanken Installation gar nicht erst auftreten. * Dabei zwei Funde: graphviz wird von erzeuge_architektur_diagramme.py importiert, fehlt aber in requirements.txt (jetzt in [figures]); pymoo steht in requirements.txt, wird aber von keinem Programm importiert, sondern nur im Kapitel Metaheuristiken empfohlen (jetzt in [empfehlungen]). * NEUER_TITEL.md nach Kritik_und_Verbesserungsvorschlaege/ verschoben - es ist die Vorlage des Titelblatts, kein Bestandteil des Werks. Die beiden Fundstellen in PROGRESS.md und erzeuge_titelseite.py nachgezogen. * PROGRESS.md nannte noch den Untertitel der ersten Fassung; auf den tatsaechlichen aus erzeuge_titelseite.py korrigiert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
commit
b7af2f1d9a
468 changed files with 262141 additions and 0 deletions
99
OR_HTML_04/programme/Robuste_Optimierung.py
Normal file
99
OR_HTML_04/programme/Robuste_Optimierung.py
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
#!/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)
|
||||
Loading…
Reference in a new issue