#!/usr/bin/env python3 # erzeuge_var_vs_cvar.py """ Erzeugt das Subadditivitaets-Bild zum Kapitel CVaR: bilder_04/kap_cvar_subadditivitaet.svg ERGAENZT kap_cvar_var_vergleich.svg, ersetzt es nicht: Jenes zeigt, WAS die beiden Kennzahlen sind - eine Schwelle und ein Mittelwert. Dieses zeigt, warum der Unterschied zaehlt. Gerechnet wird das Gegenbeispiel aus Teil 2 von VaR_CVaR_Demo.py: zwei unabhaengige Anleihen, je 100 EUR Nominal, Ausfallwahrscheinlichkeit 4 %, sonst 2 EUR Kupon. Einzeln meldet der VaR fuer jede Anleihe einen GEWINN von 2 EUR - denn 4 % liegen unter der 5-%-Schwelle, der Ausfall faellt also aus der Betrachtung heraus. Im Portfolio steigt die Wahrscheinlichkeit mindestens eines Ausfalls auf 7,8 % und damit UEBER die Schwelle; der VaR springt auf 98 EUR. Das ist die Aussage des Bildes: Der VaR behauptet, Streuung habe das Risiko um 102 EUR ERHOEHT. Der CVaR, der den ganzen Rand mittelt statt ihn abzuschneiden, kommt auf 101,33 EUR gegenueber 159,24 EUR - Streuung senkt das Risiko, wie es sein muss. Die Ziehungsreihenfolge des Buchprogramms wird nachgespielt: default_rng(2026), und Teil 1 des Programms zieht nichts, also sind die beiden Anleihen die ersten Ziehungen. Deshalb stehen im Bild genau die Zahlen der abgedruckten Tabelle; pruefe_gegen_buch() bricht ab, wenn nicht. Aufruf (aus dem Repository-Wurzelverzeichnis): python3 bilder_04/erzeuge_var_vs_cvar.py Benoetigt: numpy, matplotlib """ from __future__ import annotations import os import sys import numpy as np HIER = os.path.dirname(os.path.abspath(__file__)) BASIS = os.path.dirname(HIER) sys.path.insert(0, HIER) sys.path.insert(0, os.path.join( BASIS, "Operations_Research_mit_Python_Version_04_Programme")) from stil_04 import FARBEN, speichere # noqa: E402 import matplotlib.pyplot as plt # noqa: E402 from VaR_CVaR_Demo import cvar_rockafellar, var_quantil # noqa: E402 # Instanz wortgleich zu Teil 2 von VaR_CVaR_Demo.py. SAAT = 2026 ZIEHUNGEN = 2_000_000 AUSFALLWAHRSCHEINLICHKEIT = 0.04 VERLUST_BEI_AUSFALL = 100.0 KUPON = 2.0 # Die im Kapitel abgedruckte Tabelle; nur zum Gegenpruefen. TABELLE_IM_BUCH = { "Anleihe A allein": (-2.00, 79.59), "Anleihe B allein": (-2.00, 79.66), "Portfolio A+B": (98.00, 101.33), "Summe der Einzelwerte": (-4.00, 159.24), } def rechne() -> dict[str, tuple[float, float]]: """Dieselben Ziehungen wie im Buchprogramm - siehe Modulkommentar.""" rng = np.random.default_rng(SAAT) verlust_a = np.where(rng.random(ZIEHUNGEN) < AUSFALLWAHRSCHEINLICHKEIT, VERLUST_BEI_AUSFALL, -KUPON) verlust_b = np.where(rng.random(ZIEHUNGEN) < AUSFALLWAHRSCHEINLICHKEIT, VERLUST_BEI_AUSFALL, -KUPON) werte = {} for name, v in (("Anleihe A allein", verlust_a), ("Anleihe B allein", verlust_b), ("Portfolio A+B", verlust_a + verlust_b)): werte[name] = (var_quantil(v), cvar_rockafellar(v)) werte["Summe der Einzelwerte"] = tuple( werte["Anleihe A allein"][i] + werte["Anleihe B allein"][i] for i in (0, 1)) return werte def pruefe_gegen_buch(werte) -> None: """Die Tafel muss dieselben Zahlen zeigen wie die Tabelle im Kapitel.""" for name, (soll_var, soll_cvar) in TABELLE_IM_BUCH.items(): ist_var, ist_cvar = werte[name] if abs(ist_var - soll_var) > 0.005 or abs(ist_cvar - soll_cvar) > 0.005: raise SystemExit( f"Subadditivitaets-Bild: '{name}' ergibt VaR {ist_var:.2f} / " f"CVaR {ist_cvar:.2f}, das Kapitel druckt {soll_var:.2f} / " f"{soll_cvar:.2f} ab.") # Die Aussage selbst - nicht nur die Zahlen. if not werte["Portfolio A+B"][0] > werte["Summe der Einzelwerte"][0]: raise SystemExit("Subadditivitaets-Bild: Der VaR verletzt die " "Subadditivitaet hier nicht - dann zeigt die Tafel " "das Gegenteil ihrer Überschrift.") if not werte["Portfolio A+B"][1] <= werte["Summe der Einzelwerte"][1]: raise SystemExit("Subadditivitaets-Bild: Der CVaR ist hier nicht " "subadditiv. Das widerspricht dem Kapiteltext.") def euro(wert: float) -> str: return f"{wert:,.2f} €".replace(",", "@").replace(".", ",").replace("@", ".") def zeichne(werte) -> None: figur, achsen = plt.subplots(1, 2, figsize=(10.2, 4.4)) namen = ["Anleihe A allein", "Anleihe B allein", "Summe der Einzelwerte", "Portfolio A+B"] y = np.arange(len(namen))[::-1] for achse, index, titel, urteil, farbe in ( (achsen[0], 0, "VaR 95 %", "Portfolio teurer als die Summe —\nSubadditivität verletzt", FARBEN["fehler"]), (achsen[1], 1, "CVaR 95 %", "Portfolio billiger als die Summe —\nsubadditiv, also kohärent", FARBEN["gut"])): wert = [werte[n][index] for n in namen] farben = [FARBEN["gedaempft"], FARBEN["gedaempft"], FARBEN["zweit"], farbe] achse.barh(y, wert, height=0.6, color=farben, zorder=3) # Balken muessen bei null beginnen, sonst luegt ihre Laenge. Der Rand # links richtet sich nach der Spannweite, damit die Zahl an einem # negativen Balken nicht an der Achsenbeschriftung klebt. spanne = max(wert) - min(0.0, min(wert)) achse.set_xlim(min(0.0, min(wert)) - 0.20 * spanne, max(wert) * 1.22) for stelle, w in enumerate(wert): abstand = 0.015 * spanne achse.text(w + (abstand if w >= 0 else -abstand), y[stelle], euro(w), va="center", ha="left" if w >= 0 else "right", fontsize=9, color=FARBEN["text"], zorder=4) achse.axvline(0, color=FARBEN["text"], linewidth=1.0, zorder=2) achse.set_yticks(y) achse.set_yticklabels(namen if index == 0 else [], fontsize=9, color=FARBEN["text"]) achse.set_title(titel, fontsize=11, color=FARBEN["text"], pad=34) achse.set_xlabel("Verlust in € (negativ = Gewinn)") achse.grid(axis="y", visible=False) # Das Urteil ueber der Achse, nicht in ihr - sonst stoesst es an die # Zahl am obersten Balken. achse.text(0.5, 1.008, urteil, transform=achse.transAxes, fontsize=8.5, color=farbe, ha="center", va="bottom", fontweight="bold") figur.suptitle("Zwei unabhängige Anleihen, je 4 % Ausfallrisiko — " "und zwei Urteile über dieselbe Diversifikation", fontsize=11, color=FARBEN["text"], y=1.02) speichere(figur, "kap_cvar_subadditivitaet") if __name__ == "__main__": werte = rechne() print(f" {'':<24} {'VaR 95%':>10} {'CVaR 95%':>10}") for name in ("Anleihe A allein", "Anleihe B allein", "Portfolio A+B", "Summe der Einzelwerte"): print(f" {name:<24} {werte[name][0]:>10.2f} {werte[name][1]:>10.2f}") pruefe_gegen_buch(werte) print(" stimmt mit der abgedruckten Tabelle überein") print(f" VaR: Portfolio {werte['Portfolio A+B'][0]:.2f} > Summe " f"{werte['Summe der Einzelwerte'][0]:.2f} — verletzt") print(f" CVaR: Portfolio {werte['Portfolio A+B'][1]:.2f} ≤ Summe " f"{werte['Summe der Einzelwerte'][1]:.2f} — kohärent") zeichne(werte)