Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte

Dieser Teil wechselt die Domäne — aber nicht die Methoden. Was hier folgt, ist die konsequente Anwendung dessen, was Sie in den Teilen II und III gelernt haben, auf zwei Gebiete, in denen viel Unsicherheit auf ein unmittelbar messbares Ergebnis trifft: die Energiewirtschaft und die Finanzmärkte.

Er hat drei Abschnitte, und Sie können nach jedem aufhören:

  1. Die Brücke (Kapitel 16) macht die Behauptung nachprüfbar, dass es dieselben Methoden sind. Sie lässt denselben Code einmal über eine Werkstatt und einmal über ein Depot laufen — und benennt die drei Stellen, an denen die Analogie endet.
  2. Die Industrieanwendung (Kapitel 17) führt die Werkzeuge an einem zusammenhängenden Fall aus der Energiewirtschaft zusammen: Kraftwerkseinsatzplanung unter Windunsicherheit. Wer mit Produktion, Logistik oder Energie zu tun hat, findet hier die Vertiefung, die für die Finanzkapitel keinen Ersatz braucht.
  3. Die Finanzanwendung (ab Kapitel 18) ist die spezialisierte Vertiefungsdomäne.

📌 Für wen dieser Teil gedacht ist

Die Finanzkapitel ab Kapitel 18 sind eine Vertiefungsdomäne, kein Pflichtstoff. Wer beruflich mit Logistik, Produktion oder Energie zu tun hat, liest die ersten beiden Kapitel dieses Teils und kann den Rest überspringen, ohne dass etwas fehlt — die Methoden sind dieselben.

Lesen Sie ihn trotzdem, wenn Sie eines der folgenden Themen brauchen: Schätzfehler in Kovarianzmatrizen (das Problem tritt bei jeder Korrelationsschätzung auf, auch bei Lieferzeiten), Risikomaße jenseits der Standardabweichung, oder rollierende Auswertung mit sauberer Trennung von Trainings- und Testzeitraum.

Ein Hinweis vorweg: Die Kapitel ab Kapitel 18 laden aktuelle Kursdaten über yfinance und brauchen dafür einen Internetzugang. Weil die Daten sich täglich ändern, sind die abgedruckten Zahlen dort Beispielläufe, keine reproduzierbaren Werte — anders als im übrigen Buch. Das Brückenkapitel kommt ohne Internet aus.


Kapitel 16: Die Strukturbrücke — dieselbe Mathematik, zwei Welten

📌 Kapitel auf einen Blick

Worum geht es? Um den Nachweis, dass die Verfahren aus Teil II und III unverändert in der Finanzwelt funktionieren — und um die drei Stellen, an denen die Analogie endet.

Voraussetzungen: Kapitel 1 (Allokation als LP), Kapitel 12 (Szenarien) und Abschnitt 22.6 (or_kern.py).

Danach können Sie: ein betriebswirtschaftliches Modell in die andere Domäne übersetzen, ohne den Modellcode anzufassen; Schattenpreise in beiden Welten lesen; und benennen, welche Annahmen dabei nicht mitwandern.

Zeitbedarf: ca. 2,5 Stunden.

Programme:
Strukturbruecke.py

Notebook: bruecke.ipynb — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten


16.1 In 5 Minuten gelöst

🚀 In 5 Minuten gelöst: Was ist ein Risikobudget?

Ein Anleger hat 150 000 € und will sie auf drei Anlageklassen verteilen. Neben dem Kapital gibt es eine zweite knappe Größe: das Risikobudget — eine Kennzahl, die die Bankaufsicht oder das eigene Regelwerk vorgibt und die jede Anlage unterschiedlich stark beansprucht.

Anlage Ertrag je 1 000 € Kapital Risikobudget
Aktien Welt 75 € 1,0 1,00
Anleihen 28 € 1,0 0,22
Immobilienfonds 46 € 1,0 0,55
verfügbar 150 90

Lesen Sie die Tabelle noch einmal — und ersetzen Sie „Kapital” durch „Montagestunden” und „Risikobudget” durch „Plattenmaterial”. Es ist die Schreinerei aus Kapitel 1, Wort für Wort.

Deshalb genügt der vorhandene Code:

from or_kern import Produkt, Produktionsproblem, loese_mit_scipy

depot = Produktionsproblem(
    produkte=[
        Produkt(name="Aktien Welt", deckungsbeitrag=75.0,
                verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 1.00}),
        Produkt(name="Anleihen", deckungsbeitrag=28.0,
                verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 0.22}),
        Produkt(name="Immobilienfonds", deckungsbeitrag=46.0,
                verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 0.55}),
    ],
    kapazitaeten={"Kapital (Tsd. EUR)": 150.0, "Risikobudget": 90.0})

loesung = loese_mit_scipy(depot)
print(loesung.als_bericht())
print(loesung.schattenpreise)

Ausgabe:

Status: optimal | Zielwert: 7,634.62 | Gap: 0.00% | Zeit: 0.00s
{'Kapital (Tsd. EUR)': 14.74, 'Risikobudget': 60.26}

Und jetzt der Punkt. Die Klasse heißt Produktionsproblem, und das Depot passt hinein, ohne dass eine Zeile geändert wurde. Kein Adapter, keine Unterklasse, keine Fallunterscheidung — die Struktur ist dieselbe, weil das Problem dasselbe ist: knappe Größen auf konkurrierende Verwendungen verteilen.

Der interessanteste Teil steht in der zweiten Zeile der Ausgabe. 60,26 € je Einheit Risikobudget — das ist der Dualwert derselben Nebenbedingung, die in der Werkstatt sagt, was eine zusätzliche Montagestunde wert wäre. In der Finanzwelt hat diese Zahl einen eigenen Namen: der Preis des Risikos. Es ist nicht bloß eine Analogie, es ist derselbe Schattenpreis aus Kapitel 5.

🎯 Merksatz Wenn zwei Probleme dieselbe Struktur haben, brauchen sie nicht zwei Modelle, sondern zwei Datensätze. Ob das der Fall ist, erkennt man daran, dass derselbe Code ohne Änderung läuft — nicht daran, dass eine Tabelle es behauptet.

Warum funktioniert das? Weil ein LP nichts über die Bedeutung seiner Zahlen weiß. Es sieht eine Matrix, einen Kapazitätsvektor und einen Zielvektor. Ob in der Matrix Stunden, Kilogramm oder Risikobeiträge stehen, kommt darin nicht vor.


16.2 Lernziele

Nach diesem Kapitel können Sie …

  1. … ein Allokationsproblem aus der einen Domäne in die andere übersetzen, indem Sie die Daten austauschen statt des Codes.
  2. … den Schattenpreis in beiden Welten deuten — als Wert einer Ressource und als Preis des Risikos.
  3. … erklären, warum die CVaR-Formulierung aus Kapitel 20 auf Lieferverzüge genauso passt wie auf Kursverluste.
  4. … die drei Stellen benennen, an denen die Analogie nicht trägt — und begründen, warum das Übertragen von Methoden dort gefährlich wird.

16.3 Das Programm

#!/usr/bin/env python3

# Strukturbruecke.py
"""
Kapitel Bruecke: Derselbe Code, zwei Welten - der Beweis statt der Behauptung.

Der Teil-Auftakt stellt eine Tabelle auf: "Ressourcen auf Produkte verteilen"
entspreche "Kapital auf Anlagen verteilen", "gegen den Worst Case absichern"
entspreche "Absicherung gegen Kursabstuerze". Solche Tabellen stehen in vielen
Buechern. Sie sind billig - und man kann sie pruefen.

Dieses Programm prueft sie. Es fuettert zweimal DENSELBEN Code mit Daten aus
zwei Welten und zeigt die Ergebnisse nebeneinander:

  1. Die Allokation als LP. Das Domaenenmodell aus or_kern.py, einmal mit einer
     Schreinerei (Montagestunden, Plattenmaterial) und einmal mit einem Depot
     (Kapital, Risikobudget). Gleiche Klasse, gleicher Modellbauer, gleiche
     Abnahmepruefung - und der Schattenpreis heisst in der einen Welt
     "Wert einer zusaetzlichen Montagestunde" und in der anderen "Preis des
     Risikos".
  2. Die Absicherung gegen den schlechtesten Fall als CVaR. EINE Funktion,
     einmal mit Kursrenditen und einmal mit Lieferverzuegen.
  3. Was NICHT hinueberreicht. Der ehrliche Teil: Drei Unterschiede, die die
     Analogie begrenzen - und die man kennen muss, bevor man Methoden aus
     der einen Welt in die andere traegt.

ZUR SOLVERWAHL: Teil 1 benutzt 'loese_mit_scipy' und nicht 'loese_mit_glop'.
Das ist kein Zufall - CVXPY laedt fuer Teil 2 highspy, und ortools vertraegt
sich damit nicht im selben Prozess (Kapitel Oekosystem). Wer hier GLOP nimmt,
bekommt beim cvxpy-Import eine Fehlermeldung ueber ein 'undefined symbol'.
Genau deshalb laedt or_kern.py seine Solver erst beim Aufruf.

Benoetigt: numpy, cvxpy, scipy und pydantic (ueber or_kern)
"""

from __future__ import annotations

import numpy as np

from or_kern import (Produkt, Produktionsproblem, loese_mit_scipy,
                     pruefe_loesung)

SAAT = 7
SZENARIEN = 500
ALPHA = 0.95              # die schlechtesten 5 % der Faelle
MAX_ANTEIL = 0.40         # Streuungsgebot in beiden Welten


# --- Teil 1: Dieselbe Klasse, zwei Welten ----------------------------------

def schreinerei() -> Produktionsproblem:
    """Montagestunden und Plattenmaterial auf Tische und Stuehle verteilen."""
    return Produktionsproblem(
        produkte=[
            Produkt(name="Tisch", deckungsbeitrag=240.0,
                    verbrauch={"Montagestunden": 3.0, "Plattenmaterial": 6.0}),
            Produkt(name="Stuhl", deckungsbeitrag=60.0,
                    verbrauch={"Montagestunden": 1.0, "Plattenmaterial": 1.0}),
            Produkt(name="Regal", deckungsbeitrag=130.0,
                    verbrauch={"Montagestunden": 2.0, "Plattenmaterial": 4.0}),
        ],
        kapazitaeten={"Montagestunden": 150.0, "Plattenmaterial": 240.0})


def depot() -> Produktionsproblem:
    """Kapital und Risikobudget auf Anlageklassen verteilen.

    Eine "Einheit" ist hier 1.000 EUR Anlagesumme. Der Deckungsbeitrag ist der
    erwartete Jahresertrag dieser Einheit, der Verbrauch die beanspruchte
    Kapital- und Risikomenge. Das ist keine Analogie, sondern buchstaeblich
    dasselbe Modell - deshalb passt es in dieselbe Klasse.
    """
    return Produktionsproblem(
        produkte=[
            Produkt(name="Aktien Welt", deckungsbeitrag=75.0,
                    verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 1.00}),
            Produkt(name="Anleihen", deckungsbeitrag=28.0,
                    verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 0.22}),
            Produkt(name="Immobilienfonds", deckungsbeitrag=46.0,
                    verbrauch={"Kapital (Tsd. EUR)": 1.0, "Risikobudget": 0.55}),
        ],
        kapazitaeten={"Kapital (Tsd. EUR)": 150.0, "Risikobudget": 90.0})


def berichte_allokation(titel: str, problem: Produktionsproblem,
                        einheit: str, ertragsname: str) -> None:
    """Ein Bericht fuer beide Welten - nur die Beschriftung wechselt."""
    loesung = loese_mit_scipy(problem)
    beanstandungen = pruefe_loesung(problem, loesung)

    print(f"  {titel}")
    print(f"    {loesung.als_bericht()}")
    for produkt in problem.produkte:
        print(f"      {produkt.name:<18} {loesung.werte[produkt.name]:8.2f} {einheit}")
    print(f"      {ertragsname:<18} {loesung.zielwert:8.2f} EUR")
    for ressource, preis in loesung.schattenpreise.items():
        print(f"      Schattenpreis {ressource:<24} {preis:7.2f} EUR")
    print(f"      Abnahmepruefung: "
          f"{'bestanden' if not beanstandungen else beanstandungen}")


# --- Teil 2: Eine CVaR-Funktion, zwei Welten -------------------------------

def optimiere_cvar(verluste: np.ndarray, ertrag: np.ndarray,
                   mindestertrag: float, alpha: float = ALPHA,
                   max_anteil: float = MAX_ANTEIL):
    """Minimiert den CVaR der Verluste unter einer Mindestertragsbedingung.

    'verluste' hat die Form (Szenarien x Optionen) und enthaelt, was in
    Szenario s passiert, wenn eine Einheit in Option j steckt. Was ein
    "Verlust" ist, entscheidet allein die Einheit der Matrix: Prozentpunkte
    Kursverlust oder Tage Lieferverzug - die Formel sieht keinen Unterschied.

    Das ist die Rockafellar-Uryasev-Formulierung aus dem Kapitel CVaR, hier
    ohne jede Aenderung wiederverwendet.
    """
    import cvxpy as cp

    anzahl_szenarien, anzahl_optionen = verluste.shape
    anteil = cp.Variable(anzahl_optionen, nonneg=True)
    schwelle = cp.Variable()                       # wird im Optimum zum VaR
    ueberschuss = cp.Variable(anzahl_szenarien, nonneg=True)

    cvar = schwelle + (1.0 / (anzahl_szenarien * (1 - alpha))) * cp.sum(ueberschuss)
    problem = cp.Problem(
        cp.Minimize(cvar),
        [ueberschuss >= verluste @ anteil - schwelle,
         cp.sum(anteil) == 1,
         anteil <= max_anteil,
         ertrag @ anteil >= mindestertrag])
    problem.solve()
    if problem.status not in ("optimal", "optimal_inaccurate"):
        raise SystemExit(f"CVaR-Problem nicht loesbar: {problem.status}")
    return anteil.value, float(cvar.value), float(schwelle.value)


def kursszenarien(rng) -> tuple[np.ndarray, np.ndarray, list[str]]:
    """Taegliche Verluste (negative Renditen) von sechs Anlageklassen."""
    namen = ["Aktien Welt", "Aktien EU", "Schwellenlaender",
             "Staatsanleihen", "Unternehmensanl.", "Rohstoffe"]
    rendite_pa = np.array([0.080, 0.065, 0.110, 0.025, 0.045, 0.070])
    schwankung = np.array([0.180, 0.160, 0.260, 0.040, 0.075, 0.210])
    taeglich = rng.normal(rendite_pa / 252, schwankung / np.sqrt(252),
                          (SZENARIEN, len(namen)))
    return -taeglich * 100.0, rendite_pa * 100.0, namen


def lieferszenarien(rng) -> tuple[np.ndarray, np.ndarray, list[str]]:
    """Lieferverzug in Tagen bei sechs Lieferanten.

    Der Aufbau ist bewusst anders als bei den Kursen: Hier gibt es einen
    normalen Verzug UND seltene Totalausfaelle, die 14 Tage kosten. Das ist
    genau die Art fetter Raender, wegen der man in beiden Welten CVaR statt
    Standardabweichung benutzt.
    """
    namen = ["Nordwerk", "Sued-Metall", "Fernost A", "Lokalzulieferer",
             "Fernost B", "Osteuropa"]
    zuverlaessigkeit = np.array([0.94, 0.97, 0.90, 0.99, 0.92, 0.95])
    verzugsstreuung = np.array([3.5, 1.8, 5.0, 0.9, 4.2, 2.8])
    normaler_verzug = np.maximum(0.0, rng.normal(0.5, 1.0, (SZENARIEN, len(namen)))
                                 * verzugsstreuung)
    totalausfall = (rng.random((SZENARIEN, len(namen))) > zuverlaessigkeit) * 14.0
    return normaler_verzug + totalausfall, zuverlaessigkeit, namen


def berichte_cvar(titel: str, verluste, ertrag, namen, mindestertrag,
                  einheit: str, ertragsname: str):
    anteile, cvar, var = optimiere_cvar(verluste, ertrag, mindestertrag)
    mittel = float((verluste @ anteile).mean())
    print(f"  {titel}")
    print(f"    VaR  {ALPHA:.0%}: {var:8.3f} {einheit}")
    print(f"    CVaR {ALPHA:.0%}: {cvar:8.3f} {einheit}      "
          f"(Mittelwert ueber alle Szenarien: {mittel:.3f})")
    print(f"    {ertragsname}: {float(ertrag @ anteile):.3f}")
    print("    Aufteilung:")
    for name, anteil in zip(namen, anteile):
        balken = "#" * int(round(anteil * 40))
        grenze = "  <- an der Streuungsgrenze" if anteil > MAX_ANTEIL - 1e-4 else ""
        print(f"      {name:<18} {anteil:6.1%}  {balken}{grenze}")
    return anteile


if __name__ == "__main__":
    rng = np.random.default_rng(SAAT)

    print("=" * 80)
    print("  DIESELBE STRUKTUR, ZWEI WELTEN")
    print("=" * 80)

    # --- Teil 1 ----------------------------------------------------------
    print("\n1. Allokation als LP - EINE Klasse, EIN Modellbauer\n")
    berichte_allokation("Werkstatt: Produktionsprogramm", schreinerei(),
                        "Stueck", "Deckungsbeitrag")
    print()
    berichte_allokation("Depot: Anlageaufteilung", depot(),
                        "Tsd.  ", "Erwarteter Ertrag")

    print("\n  Beide Ausgaben stammen aus derselben Funktion "
          "'berichte_allokation'.")
    print("  Ausgetauscht wurden nur die Daten und die Beschriftungen -")
    print("  keine Zeile Modellcode.")
    print()
    print("  Lesen Sie die Schattenpreise nebeneinander: In der Werkstatt sagt")
    print("  er, was eine zusaetzliche Montagestunde wert waere. Im Depot sagt")
    print("  dieselbe Zahl, was eine zusaetzliche Einheit Risikobudget wert")
    print("  waere - der PREIS DES RISIKOS. Das ist kein Sprachbild, sondern")
    print("  derselbe Dualwert derselben Nebenbedingung.")

    # --- Teil 2 ----------------------------------------------------------
    print("\n" + "-" * 80)
    print("2. Absicherung gegen den schlechtesten Fall - EINE CVaR-Funktion\n")

    kurse, rendite, anlagen = kursszenarien(rng)
    anteile_depot = berichte_cvar(
                  "Depot: die schlechtesten 5 % der Handelstage",
                  kurse, rendite, anlagen, 5.5,
                  "% je Tag    ", "Erwartete Jahresrendite (%)")
    print()
    lieferung, zuverlaessig, lieferanten = lieferszenarien(rng)
    anteile_einkauf = berichte_cvar("Einkauf: die schlechtesten 5 % der Bestellungen",
                  lieferung, zuverlaessig, lieferanten, 0.945,
                  "Tage Verzug  ", "Mittlere Zuverlaessigkeit")

    print("\n  Auch hier: eine Funktion, zwei Aufrufe. Die Zielfunktion")
    print("  interessiert sich nicht dafuer, ob in der Matrix Prozentpunkte")
    print("  oder Tage stehen.")
    print()
    print(f"  Interessant ist, WO die Streuungsgrenze von {MAX_ANTEIL:.0%} bindet:")
    print(f"    Depot   : {(anteile_depot > MAX_ANTEIL - 1e-4).sum()} von "
          f"{len(anteile_depot)} Posten am Anschlag")
    print(f"    Einkauf : {(anteile_einkauf > MAX_ANTEIL - 1e-4).sum()} von "
          f"{len(anteile_einkauf)} Posten am Anschlag")
    print()
    print("  Im Einkauf zieht es die Loesung an den sicheren Lokalzulieferer,")
    print("  bis die Grenze sie stoppt. Im Depot nicht - dort verhindert die")
    print("  Mindestrendite, dass alles in Anleihen wandert. Zwei verschiedene")
    print("  Bremsen also, und sie stehen an verschiedenen Stellen des Modells:")
    print("  einmal in einer Nebenbedingung ueber die Anteile, einmal in einer")
    print("  ueber den Ertrag. Wer eine Struktur uebertraegt, uebertraegt eben")
    print("  nicht automatisch mit, WELCHE Bedingung am Ende bindet.")

    # --- Teil 3: Der ehrliche Teil ---------------------------------------
    print("\n" + "=" * 80)
    print("  WAS NICHT HINUEBERREICHT")
    print("=" * 80)
    print("Die Struktur traegt. Drei Unterschiede tragen NICHT mit, und wer sie")
    print("uebersieht, macht aus einer nuetzlichen Analogie einen Fehler:")
    print()
    print("1. WOHER DIE ZAHLEN KOMMEN.")
    print("   In der Werkstatt ist der Verbrauch je Tisch gemessen - drei")
    print("   Montagestunden sind drei Montagestunden. Im Depot ist die")
    print("   erwartete Rendite GESCHAETZT, und zwar mit einem Fehler, der")
    print("   groesser sein kann als die Unterschiede zwischen den Anlagen")
    print("   (Kapitel Markowitz, Renditeschaetzung_Falle.py). Dieselbe")
    print("   Optimierung ist im einen Fall Planung und im anderen")
    print("   Fehlerverstaerkung.")
    print()
    print("2. OB DIE VERGANGENHEIT ETWAS UEBER DIE ZUKUNFT SAGT.")
    print("   Lieferzeiten haben physikalische Ursachen: Entfernung, Zoll,")
    print("   Kapazitaet. Sie aendern sich langsam und nachvollziehbar.")
    print("   Kursrenditen entstehen aus dem Verhalten von Marktteilnehmern,")
    print("   die selbst auf Modelle reagieren - dort verschwindet ein")
    print("   erkanntes Muster oft genau deshalb, weil es erkannt wurde")
    print("   (Kapitel Handelsmaschine, Data_Snooping.py).")
    print()
    print("3. OB TEILBARKEIT ERLAUBT IST.")
    print("   37,4 % eines Aktienfonds sind ein normaler Auftrag. 37,4 % eines")
    print("   Lieferanten sind es nicht - Vertraege, Mindestabnahmen und")
    print("   Ruestzeiten machen Einkaufsentscheidungen ganzzahlig. Genau")
    print("   deshalb ist Teil II voller MILP und Teil V fast frei davon.")
    print()
    print("Die Bruecke traegt also die MODELLE, nicht die Annahmen. Wer sie")
    print("benutzt, spart sich das Lernen der Methoden - nicht das Nachdenken")
    print("ueber die Daten.")
    print("=" * 80)

Erwartete Ausgabe:

================================================================================
  DIESELBE STRUKTUR, ZWEI WELTEN
================================================================================

1. Allokation als LP - EINE Klasse, EIN Modellbauer

  Werkstatt: Produktionsprogramm
    Status: optimal | Zielwert: 10,800.00 | Gap: 0.00% | Zeit: 0.00s
      Tisch                 30.00 Stueck
      Stuhl                 60.00 Stueck
      Regal                  0.00 Stueck
      Deckungsbeitrag    10800.00 EUR
      Schattenpreis Montagestunden             40.00 EUR
      Schattenpreis Plattenmaterial            20.00 EUR
      Abnahmepruefung: bestanden

  Depot: Anlageaufteilung
    Status: optimal | Zielwert: 7,634.62 | Gap: 0.00% | Zeit: 0.00s
      Aktien Welt           73.08 Tsd.  
      Anleihen              76.92 Tsd.  
      Immobilienfonds        0.00 Tsd.  
      Erwarteter Ertrag   7634.62 EUR
      Schattenpreis Kapital (Tsd. EUR)         14.74 EUR
      Schattenpreis Risikobudget               60.26 EUR
      Abnahmepruefung: bestanden

  Beide Ausgaben stammen aus derselben Funktion 'berichte_allokation'.
  Ausgetauscht wurden nur die Daten und die Beschriftungen -
  keine Zeile Modellcode.

  Lesen Sie die Schattenpreise nebeneinander: In der Werkstatt sagt
  er, was eine zusaetzliche Montagestunde wert waere. Im Depot sagt
  dieselbe Zahl, was eine zusaetzliche Einheit Risikobudget wert
  waere - der PREIS DES RISIKOS. Das ist kein Sprachbild, sondern
  derselbe Dualwert derselben Nebenbedingung.

--------------------------------------------------------------------------------
2. Absicherung gegen den schlechtesten Fall - EINE CVaR-Funktion

  Depot: die schlechtesten 5 % der Handelstage
    VaR  95%:    0.521 % je Tag    
    CVaR 95%:    0.644 % je Tag          (Mittelwert ueber alle Szenarien: -0.011)
    Erwartete Jahresrendite (%): 5.500
    Aufteilung:
      Aktien Welt         14.9%  ######
      Aktien EU           12.2%  #####
      Schwellenlaender     9.6%  ####
      Staatsanleihen      26.8%  ###########
      Unternehmensanl.    30.7%  ############
      Rohstoffe            5.7%  ##

  Einkauf: die schlechtesten 5 % der Bestellungen
    VaR  95%:    4.351 Tage Verzug  
    CVaR 95%:    5.716 Tage Verzug        (Mittelwert ueber alle Szenarien: 2.072)
    Mittlere Zuverlaessigkeit: 0.963
    Aufteilung:
      Nordwerk            12.6%  #####
      Sued-Metall         22.1%  #########
      Fernost A            6.2%  ##
      Lokalzulieferer     40.0%  ################  <- an der Streuungsgrenze
      Fernost B            8.7%  ###
      Osteuropa           10.4%  ####

  Auch hier: eine Funktion, zwei Aufrufe. Die Zielfunktion
  interessiert sich nicht dafuer, ob in der Matrix Prozentpunkte
  oder Tage stehen.

  Interessant ist, WO die Streuungsgrenze von 40% bindet:
    Depot   : 0 von 6 Posten am Anschlag
    Einkauf : 1 von 6 Posten am Anschlag

  Im Einkauf zieht es die Loesung an den sicheren Lokalzulieferer,
  bis die Grenze sie stoppt. Im Depot nicht - dort verhindert die
  Mindestrendite, dass alles in Anleihen wandert. Zwei verschiedene
  Bremsen also, und sie stehen an verschiedenen Stellen des Modells:
  einmal in einer Nebenbedingung ueber die Anteile, einmal in einer
  ueber den Ertrag. Wer eine Struktur uebertraegt, uebertraegt eben
  nicht automatisch mit, WELCHE Bedingung am Ende bindet.

================================================================================
  WAS NICHT HINUEBERREICHT
================================================================================
Die Struktur traegt. Drei Unterschiede tragen NICHT mit, und wer sie
uebersieht, macht aus einer nuetzlichen Analogie einen Fehler:

1. WOHER DIE ZAHLEN KOMMEN.
   In der Werkstatt ist der Verbrauch je Tisch gemessen - drei
   Montagestunden sind drei Montagestunden. Im Depot ist die
   erwartete Rendite GESCHAETZT, und zwar mit einem Fehler, der
   groesser sein kann als die Unterschiede zwischen den Anlagen
   (Kapitel Markowitz, Renditeschaetzung_Falle.py). Dieselbe
   Optimierung ist im einen Fall Planung und im anderen
   Fehlerverstaerkung.

2. OB DIE VERGANGENHEIT ETWAS UEBER DIE ZUKUNFT SAGT.
   Lieferzeiten haben physikalische Ursachen: Entfernung, Zoll,
   Kapazitaet. Sie aendern sich langsam und nachvollziehbar.
   Kursrenditen entstehen aus dem Verhalten von Marktteilnehmern,
   die selbst auf Modelle reagieren - dort verschwindet ein
   erkanntes Muster oft genau deshalb, weil es erkannt wurde
   (Kapitel Handelsmaschine, Data_Snooping.py).

3. OB TEILBARKEIT ERLAUBT IST.
   37,4 % eines Aktienfonds sind ein normaler Auftrag. 37,4 % eines
   Lieferanten sind es nicht - Vertraege, Mindestabnahmen und
   Ruestzeiten machen Einkaufsentscheidungen ganzzahlig. Genau
   deshalb ist Teil II voller MILP und Teil V fast frei davon.

Die Bruecke traegt also die MODELLE, nicht die Annahmen. Wer sie
benutzt, spart sich das Lernen der Methoden - nicht das Nachdenken
ueber die Daten.
================================================================================

16.4 Die zwei Brücken im Einzelnen

Erste Brücke: Allokation

Werkstatt Depot im Modell
Montagestunden, Plattenmaterial Kapital, Risikobudget kapazitaeten
Tisch, Stuhl, Regal Aktien, Anleihen, Immobilien produkte
Verbrauch je Stück Beanspruchung je 1 000 € verbrauch
Deckungsbeitrag je Stück Erwarteter Ertrag je 1 000 € deckungsbeitrag
Wert einer Montagestunde Preis des Risikos schattenpreise

Die letzte Zeile ist die, die sich zu merken lohnt. Ein Portfoliomanager, der sagt „unser Risikobudget ist zu knapp bemessen”, trifft dieselbe Aussage wie ein Fertigungsleiter, der eine zweite Schicht fordert — und beide können sie mit derselben Zahl belegen.

Zweite Brücke: Absicherung gegen den schlechtesten Fall

Hier ist die Übereinstimmung noch enger, weil sie nicht nur die Struktur betrifft, sondern die Formel. Die Rockafellar-Uryasev-Formulierung aus Kapitel 20

\min_{w,\;\gamma} \;\; \gamma + \frac{1}{S(1-\alpha)} \sum_{s=1}^{S} u_s \qquad \text{mit} \qquad u_s \ge \ell_s^\top w - \gamma, \quad u_s \ge 0

steht im Programm genau einmal und wird zweimal aufgerufen.

🔤 Formel-Übersetzer

Mathematik im Depot im Einkauf
w Anteil je Anlageklasse Bestellanteil je Lieferant
\ell_s Verlust in Szenario s, in Prozentpunkten Verzug in Szenario s, in Tagen
\gamma der VaR: „so schlimm wird es an 95 % der Tage höchstens” „so viele Tage Verzug höchstens bei 95 % der Bestellungen”
u_s Überschuss über diese Schwelle dasselbe
Zielwert CVaR: Mittelwert der schlechtesten 5 % dasselbe

Ohne Formel gesagt: „Sorge dafür, dass die schlimmsten fünf Prozent der Fälle im Mittel so glimpflich wie möglich ausgehen.” Dieser Satz enthält kein Wort, das nur an der Börse Sinn ergibt.

Beide Anwendungen haben denselben Grund, warum sie CVaR und nicht die Standardabweichung benutzen: fette Ränder. Ein Lieferant fällt selten aus, aber wenn, dann kostet es vierzehn Tage — genau die Verteilungsform, bei der die Standardabweichung in die Irre führt (Kapitel 20).

Wo die Lösungen sich unterscheiden

Der Vergleich fördert nebenbei einen Unterschied zutage, den man nicht erwartet: Die Streuungsgrenze von 40 % bindet nur im Einkauf, nicht im Depot.

Im Einkauf zieht es die Lösung an den sicheren Lokalzulieferer, bis die Grenze sie stoppt. Im Depot verhindert stattdessen die Mindestrendite, dass alles in Anleihen wandert. Zwei verschiedene Bremsen also — und sie sitzen an verschiedenen Stellen des Modells.

Das ist die feine, wichtige Lehre dieses Abschnitts: Wer eine Struktur überträgt, überträgt nicht automatisch mit, welche Nebenbedingung am Ende bindet. Und das ist die Information, die im Gespräch mit dem Fachbereich zählt.


16.5 Wo die Brücke endet

Eine Analogie ist nur so nützlich wie die Grenzen, die man ihr zieht. Drei Dinge wandern nicht mit:

Werkstatt / Einkauf Depot
Woher die Zahlen kommen gemessen: drei Montagestunden sind drei Montagestunden geschätzt: die erwartete Rendite hat einen Fehler, der größer sein kann als die Unterschiede zwischen den Anlagen (Kapitel 19)
Ob die Vergangenheit trägt Lieferzeiten haben physikalische Ursachen und ändern sich langsam Kurse entstehen aus dem Verhalten von Marktteilnehmern, die auf Modelle reagieren — ein erkanntes Muster verschwindet oft deshalb, weil es erkannt wurde (Kapitel 21)
Ob Teilbarkeit erlaubt ist 37,4 % eines Lieferanten gibt es nicht: Verträge, Mindestabnahmen, Rüstzeiten → MILP 37,4 % eines Fonds sind ein normaler Auftrag → LP/QP genügt

⚠️ Der Fehler, vor dem diese Tabelle schützt

Dieselbe Optimierung ist in der Werkstatt Planung und im Depot Fehlerverstärkung. Ein LP sucht die Ecke des zulässigen Bereichs mit dem besten Zielwert — und wenn die Zielkoeffizienten Schätzungen mit großem Fehler sind, sucht es genau die Ecke, an der der Schätzfehler am günstigsten aussieht.

In der Werkstatt gibt es dieses Problem nicht, weil der Deckungsbeitrag je Tisch aus der Kalkulation kommt und nicht aus einer Zeitreihe. Deshalb ist Shrinkage (Kapitel 18) im Depot Pflicht und in der Fertigung unbekannt.

Die dritte Zeile erklärt außerdem eine Beobachtung, die aufmerksamen Lesern längst aufgefallen ist: Teil II ist voller MILP, Teil IV fast frei davon. Das liegt nicht an der Methode, sondern daran, was in der jeweiligen Welt teilbar ist.


16.6 Übungsaufgaben

Lösungen: Abschnitt A.16.

Aufgabe 16.1 ⭐ — Die dritte Ressource. Ergänzen Sie im Depot eine dritte knappe Größe: „Liquidität” (wie schnell sich eine Anlage verkaufen lässt). Was entspricht ihr in der Werkstatt?

Aufgabe 16.2 ⭐ — Den Schattenpreis lesen. Das Risikobudget hat einen Schattenpreis von 60,26 €. Die Aufsicht bietet an, es gegen eine Gebühr um 10 Einheiten zu erhöhen. Bis zu welcher Gebühr lohnt sich das — und welche Einschränkung dieser Aussage kennen Sie aus Abschnitt 5.9?

Aufgabe 16.3 ⭐⭐ — Die Brücke rückwärts. Übertragen Sie die Ledoit-Wolf-Shrinkage aus Kapitel 18 auf Lieferzeiten: Wo genau träte dort dasselbe Problem auf, und welche Daten bräuchten Sie?

Aufgabe 16.4 ⭐⭐ — CVaR mit Ganzzahligkeit. Der Einkauf darf höchstens drei Lieferanten beauftragen. Ergänzen Sie die entsprechende Kardinalitätsbedingung (Kapitel 6, Muster 5) und vergleichen Sie den CVaR mit dem teilbaren Fall. Was kostet die Ganzzahligkeit?

Aufgabe 16.5 ⭐⭐⭐ — Die eigene Brücke. Nehmen Sie ein Modell aus Ihrem Arbeitsumfeld und suchen Sie die Entsprechung in der jeweils anderen Welt. Prüfen Sie mit der Tabelle aus Abschnitt 16.5, ob die Übertragung trägt — und schreiben Sie auf, welche der drei Zeilen im Weg steht.


16.7 Finde den Denkfehler

🐛 „Das ist doch dasselbe Problem”

Eine Logistikerin liest dieses Kapitel und überträgt es zurück. Ihre Aufgabe: Aus 40 möglichen Lieferanten sollen die besten ausgewählt und Bestellmengen zugeteilt werden. Sie geht vor wie im Portfoliomanagement:

„Ich habe für jeden Lieferanten aus den letzten drei Jahren die mittlere Lieferzeit und > die Kovarianzmatrix der Lieferzeiten geschätzt. Dann habe ich das Markowitz-Modell > angewendet: minimiere die Varianz der Gesamtlieferzeit bei vorgegebener mittlerer > Lieferzeit. Das Ergebnis ist ein sauber diversifiziertes Lieferantenportfolio.”

Das Vorgehen ist methodisch sauber, die Rechnung stimmt, und das Ergebnis sieht plausibel aus.

Zwei Dinge daran sind falsch — und das eine ist genau der Fehler, vor dem dieses Kapitel warnt, das andere ein zusätzlicher, den es nicht behandelt. Welche?


16.8 Micro-Quiz

❓ Drei Fragen

1. Warum passt das Depot in die Klasse Produktionsproblem, ohne dass etwas geändert werden muss? a) Weil Produktionsproblem mit Any-Typen arbeitet und alles akzeptiert. b) Weil beide Probleme dieselbe Struktur haben: knappe Größen auf konkurrierende Verwendungen verteilen. c) Weil Pydantic die Feldnamen automatisch übersetzt.

2. Der Schattenpreis des Risikobudgets beträgt 60,26 €. Was bedeutet das? a) Das Risikobudget ist zu 60,26 % ausgelastet. b) Eine zusätzliche Einheit Risikobudget würde den erwarteten Ertrag um rund 60,26 € erhöhen — solange die Basis sich nicht ändert. c) Jede Einheit Risikobudget kostet die Bank 60,26 €.

3. Welche der drei Grenzen der Analogie ist der Grund dafür, dass Teil II voller MILP ist und Teil IV fast frei davon? a) Woher die Zahlen kommen. b) Ob die Vergangenheit etwas über die Zukunft sagt. c) Ob Teilbarkeit erlaubt ist.


16.9 Selbsttest

  1. Nennen Sie drei Größenpaare, die in Werkstatt und Depot einander entsprechen.
  2. Warum weiß ein LP nichts über die Bedeutung seiner Zahlen — und warum ist das hier ein Vorteil?
  3. Erklären Sie den „Preis des Risikos” jemandem, der nur Fertigungsplanung kennt.
  4. Warum benutzen beide Welten CVaR statt der Standardabweichung? Nennen Sie die Verteilungseigenschaft, um die es geht.
  5. Sie wollen ein Verfahren aus der Finanzwelt in die Produktion übertragen. Welche drei Fragen stellen Sie vorher?

16.10 Zusammenfassung

  • Die Behauptung „dieselben Methoden, andere Domäne” ist prüfbar — und hier geprüft: Ein Depot passt unverändert in die Klasse Produktionsproblem, und eine CVaR-Funktion bearbeitet Kursverluste wie Lieferverzüge.
  • Der Schattenpreis einer Ressource heißt in der Finanzwelt Preis des Risikos. Es ist derselbe Dualwert derselben Nebenbedingung.
  • Die CVaR-Formulierung enthält kein Wort, das nur an der Börse Sinn ergibt. Beide Welten benutzen sie aus demselben Grund: fette Ränder, bei denen die Standardabweichung in die Irre führt.
  • Übertragen wird die Struktur, nicht die Annahmen. Drei Dinge wandern nicht mit: woher die Zahlen kommen (gemessen gegen geschätzt), ob die Vergangenheit trägt (Physik gegen reagierende Marktteilnehmer) und ob Teilbarkeit erlaubt ist (MILP gegen LP).
  • Die erste dieser Grenzen ist die gefährlichste: Dieselbe Optimierung ist in der Werkstatt Planung und im Depot Fehlerverstärkung.