operations_research/OR_HTML_04/dynamische-programmierung.html

994 lines
116 KiB
HTML
Raw Normal View History

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>
2026-09-08 01:20:09 +02:00
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution · Optimierte Entscheidungsfindung mit Python</title>
<script>
(function () {
try {
var t = localStorage.getItem("or-theme");
if (t) document.documentElement.setAttribute("data-theme", t);
} catch (e) {}
})();
</script>
<link rel="stylesheet" href="assets/highlight.css" />
<link rel="stylesheet" href="katex/katex.min.css" />
<script defer="" src="katex/katex.min.js"></script>
<script>document.addEventListener("DOMContentLoaded", function () {
var mathElements = document.getElementsByClassName("math");
var macros = [];
for (var i = 0; i < mathElements.length; i++) {
var texText = mathElements[i].firstChild;
if (mathElements[i].tagName == "SPAN") {
katex.render(texText.data, mathElements[i], {
displayMode: mathElements[i].classList.contains('display'),
throwOnError: false,
macros: macros,
fleqn: false
});
}}
// Der Browser springt zu einem #anker in der URL schon beim ersten Rendern
// an, BEVOR die KaTeX-Formeln oben im Text ihre finale Hoehe bekommen -
// durch den Reflow landet der Anker danach zu weit unten. Nach dem
// Formel-Rendering hier erneut zum Anker springen, das behebt es.
if (location.hash) {
var ziel = document.getElementById(decodeURIComponent(location.hash.slice(1)));
if (ziel) ziel.scrollIntoView({behavior: "instant", block: "start"});
}
});
</script>
<link rel="stylesheet" href="assets/site.css" />
</head>
<body>
<svg style="display:none" aria-hidden="true"><symbol id="icon-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/>
</symbol>
<symbol id="icon-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="11" cy="11" r="7"/><line x1="21" y1="21" x2="16.2" y2="16.2"/>
</symbol>
<symbol id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="12" cy="12" r="4.5"/>
<line x1="12" y1="1.5" x2="12" y2="4"/><line x1="12" y1="20" x2="12" y2="22.5"/>
<line x1="1.5" y1="12" x2="4" y2="12"/><line x1="20" y1="12" x2="22.5" y2="12"/>
<line x1="4.5" y1="4.5" x2="6.2" y2="6.2"/><line x1="17.8" y1="17.8" x2="19.5" y2="19.5"/>
<line x1="19.5" y1="4.5" x2="17.8" y2="6.2"/><line x1="6.2" y1="17.8" x2="4.5" y2="19.5"/>
</symbol>
<symbol id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M20 14.5A8.5 8.5 0 1 1 9.5 4a6.8 6.8 0 0 0 10.5 10.5z"/>
</symbol>
<symbol id="icon-chevron-left" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="15 4 7 12 15 20"/>
</symbol>
<symbol id="icon-chevron-right" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="9 4 17 12 9 20"/>
</symbol>
<symbol id="icon-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 13 9.5 18.5 20 6"/>
</symbol>
<symbol id="icon-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M18 13.5V19a1.5 1.5 0 0 1-1.5 1.5H5A1.5 1.5 0 0 1 3.5 19V7A1.5 1.5 0 0 1 5 5.5h5.5"/>
<polyline points="14.5 3.5 20.5 3.5 20.5 9.5"/><line x1="11" y1="13" x2="20" y2="4"/>
</symbol>
<symbol id="icon-book" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 5.5A2 2 0 0 1 6 4h6v16H6a2 2 0 0 0-2 1.5z"/>
<path d="M20 5.5A2 2 0 0 0 18 4h-6v16h6a2 2 0 0 1 2 1.5z"/>
</symbol>
<symbol id="icon-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/>
</symbol>
<symbol id="icon-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M12 3v12"/><polyline points="7 10 12 15 17 10"/><path d="M4 19.5h16"/>
</symbol></svg>
<header class="site-header">
<button type="button" class="icon-btn" id="sidebar-toggle" aria-label="Menü öffnen"><svg class="icon" aria-hidden="true"><use href="#icon-menu"></use></svg></button>
<a class="brand" href="index.html"><svg class="icon" aria-hidden="true"><use href="#icon-book"></use></svg> <span>Optimierte Entscheidungsfindung mit Python</span></a>
<div class="site-search">
<input id="suche-eingabe" type="search" placeholder="Suchen …" aria-label="Suche" autocomplete="off" />
<svg class="icon such-icon" aria-hidden="true"><use href="#icon-search"></use></svg>
<div id="suche-ergebnisse" class="suche-ergebnisse" hidden></div>
</div>
<button type="button" class="icon-btn" id="theme-toggle" aria-label="Darstellung umschalten">
<svg class="icon icon-sun" aria-hidden="true"><use href="#icon-sun"></use></svg><svg class="icon icon-moon" aria-hidden="true"><use href="#icon-moon"></use></svg>
</button>
</header>
<div class="site-body">
<div class="sidebar-overlay" id="sidebar-overlay" hidden></div>
Phase 8.1: Synthese-Seiten je Teil - und die gebrochene Lesekette Setzt Paket 5 aus Verbesserungen_02.md um (den Teil, der nicht zurueckgestellt wurde). Fuenf neue Dateien 19_/29_/39_/49_/52_Synthese_*.md, je eine am Ende eines Teils, mit eigener Website-Seite ueber SONDERSEITEN - sie tragen bewusst keine "# Kapitel:"-Ueberschrift, weil sie keine Kapitel sind, sondern der Rueckblick auf einen Teil. Der Entwurf musste sich abgrenzen: Die Teil-Einleitungen haben bereits Entscheidungsdiagramme. Eine zweite Matrix am Teil-Ende waere eine Dopplung gewesen. Die Synthesen leisten deshalb, was eine Einleitung nicht kann - den Vergleich ueber die Kapitel hinweg (Verfahren nebeneinander, mit der Spalte "wo es aufhoert"), eine Tabelle "was dieser Teil gemessen hat" (Behauptung gegen Messung gegen Fundstelle) und drei Fehler, die der Teil verhindert. Zitiert wird ausschliesslich, was im Buch tatsaechlich gerechnet wird. Drei Funde beim Einbau: * Teil III sagte "die drei Kapitel dieses Teils", hat aber fuenf. Phase 3 hatte Mehrziel und Predict-then-Optimize hinzugefuegt, die Einleitung blieb stehen. * 50_Praxis.md verwies auf die Projektwerkstatt mit "acht eigene Anwendungen" - sie hat elf. * Und der eigentliche Fund: Die Lesekette der Quelldateien fuehrte an ACHT Kapiteln vorbei. 12_Python_Oekosystem zeigte direkt auf 20_Lineare_Programmierung, 23_Graphen direkt auf 30_QP, 32_Dynamische direkt auf 40_Finanzdaten, 50_Praxis direkt auf die Projektwerkstatt. Wer der Kette folgte, uebersprang acht von 23 Kapiteln - darunter Metaheuristiken, Spaltengenerierung, Strukturbruecke, Supply-Chain und das ganze Testing-Kapitel. Zehn weitere Dateien hatten gar keine Navigationszeile. Zur Reichweite, damit sie nicht ueberschaetzt wird: Diese Zeilen stehen nur in den Quelldateien. entferne_navigation() streicht sie aus dem Gesamtdokument, und die Website baut ihre Vor/Zurueck-Knoepfe selbst aus DATEIEN. PDF und Website waren nie betroffen - wohl aber jeder, der die Markdown-Dateien im Repository liest, und das wird nach der Veroeffentlichung der Normalfall sein. Die Kette ist jetzt ueber alle 35 Uebergaenge geschlossen, und --check bewacht sie: Fehlt eine Zeile oder zeigt sie an der in DATEIEN folgenden Datei vorbei, ist der Lauf rot. Gegengetestet mit beiden Bruchformen. Stand: 36 Dateien, 296 Abschnitte, 815 Querverweise, 328 Indexmarken, 76 Programme (unveraendert), 33 pytest-Tests, PDF 758 Seiten. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 12:10:02 +02:00
<nav class="sidebar" id="sidebar" aria-label="Kapitelnavigation"><div class="sidebar-inhalt"><details class="sidebar-gruppe"><summary>Einstieg</summary><ul><li data-kapitel="vorwort.html"><a href="vorwort.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Vorwort & Lesehilfe</span></a></li><li data-kapitel="notation.html"><a href="notation.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Notation & Abkürzungen</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil I: Grundlagen des Operations Research</summary><ul><li data-kapitel="einfuehrung.html"><a href="einfuehrung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 1: Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung</span></a></li><li data-kapitel="fundament.html"><a href="fundament.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 2: Das mathematische Fundament — Vektoren, Matrizen, Konvexität</span></a></li><li data-kapitel="oekosystem.html"><a href="oekosystem.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 3: Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten</span></a></li><li data-kapitel="modellierung.html"><a href="modellierung.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 4: Vom Management-Wunsch zum Modell</span></a></li><li data-kapitel="synthese-grundlagen.html"><a href="synthese-grundlagen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil I</span></a></li></ul></details><details class="sidebar-gruppe"><summary>Teil II: Die Kernverfahren der deterministischen Optimierung</summary><ul><li data-kapitel="lp.html"><a href="lp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 5: Lineare Programmierung — Simplex, Dualität und Schattenpreise</span></a></li><li data-kapitel="milp.html"><a href="milp.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 6: Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound</span></a></li><li data-kapitel="cpsat.html"><a href="cpsat.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 7: Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung</span></a></li><li data-kapitel="graphen.html"><a href="graphen.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 8: Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP</span></a></li><li data-kapitel="metaheuristiken.html"><a href="metaheuristiken.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 9: Metaheuristiken — wenn der exakte Solver aussteigt</span></a></li><li data-kapitel="dekomposition.html"><a href="dekomposition.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Kapitel 10: Spaltengenerierung — das Modell umbauen statt die Lösung raten</span></a></li><li data-kapitel="synthese-kernverfahren.html"><a href="synthese-kernverfahren.html"><span class="fortschritt-haken"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg></span><span>Synthese Teil II</span></a></li></ul></details><details class="sidebar-gruppe" open><summary>Teil III: Nichtlinearität, Unsicherheit und mehrperiod
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>
2026-09-08 01:20:09 +02:00
<main class="content">
<nav class="breadcrumb" aria-label="Breadcrumb"><a href="index.html">Start</a> <span>Teil III</span> <span>Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</span></nav>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="unsicherheit.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a><a class="prev-next-knopf prev-next-next" href="mehrziel.html"><span><small>Weiter</small>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
<article>
<h1 id="kap-dynamische-programmierung">Kapitel 13: Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution</h1>
<div class="card card-blick">
<blockquote>
<p><strong>📌 Kapitel auf einen Blick</strong></p>
<p><strong>Worum geht es?</strong> Um Entscheidungsketten: Was heute klug ist, hängt davon ab, was morgen noch möglich sein wird. Die Bellman-Gleichung löst solche Probleme durch Rückwärtsrechnen.</p>
<p><strong>Voraussetzungen:</strong> <a href="unsicherheit.html#kap-unsicherheit">Kapitel 12</a> (Erwartungswerte). Rekursion in Python.</p>
<p><strong>Danach können Sie:</strong> Ein mehrperiodiges Problem in Zustände und Stufen zerlegen, die Wertfunktion durch Rückwärtsinduktion berechnen, die optimale Strategie ableiten — und prüfen, ob Ihr Zustand vollständig genug ist.</p>
<p><strong>Zeitbedarf:</strong> ca. 5 Stunden.</p>
<p><strong>Programme:</strong><br />
<code>Bellman_Minimalbeispiel.py</code><br />
<code>Mehrperiodige_Order_Execution.py</code></p>
Veroeffentlichung: Lizenzen, README, Colab-Entscheidung, JupyterLab im Image Sechs Dinge, alle fuer das jetzt oeffentliche Repository. LIZENZEN. LICENSE traegt die MIT-Lizenz fuer den Programmcode (alle .py, Notebooks, Dockerfile, pyproject.toml), LICENSE-TEXT.md die CC BY-SA 4.0 fuer Buchtext, PDF, Website und Diagramme. Beide Dateien nennen ausdruecklich, was sie abdecken und was nicht - ein Codeblock im Buchtext bleibt Programmcode und steht unter MIT. Vorher hatte das Repository gar keine Lizenz, womit standardmaessig "alle Rechte vorbehalten" galt und niemand die 76 Programme haette weiterverwenden duerfen. COLAB. COLAB_BASIS_URL steht jetzt auf "". Der Platzhalter zeigte auf GitHub, das Repository liegt auf einer eigenen Forgejo-Instanz - und Colab oeffnet Notebooks NUR aus GitHub, Google Drive oder einem Upload. Die URL-Form colab.research.google.com/github/... ist fest auf GitHub verdrahtet; eine selbstgehostete Adresse dort einzusetzen ergaebe keinen Link zum eigenen Server, sondern einen toten GitHub-Link. Der Kommentar im Quelltext ging von GitHub aus und war damit selbst irrefuehrend; er ist ersetzt. Verloren geht nichts: Die 25 Notebooks liegen neben der Website und bekommen einen echten Download-Link, jetzt mit dem Hinweis, was man damit tut - "herunterladen und in Jupyter oeffnen, in Colab hochladen oder mit dem Kurs-Image starten". JUPYTERLAB IM KURS-IMAGE. Neue pyproject-Gruppe [notebook] mit jupyterlab, die Notebooks werden ins Image kopiert, und ein kleiner Startbefehl macht beide Betriebsarten ohne --entrypoint moeglich: ohne Argument JupyterLab, mit Argument ein einzelnes Programm. Gebaut und geprueft - Rucksack.py laeuft, JupyterLab antwortet mit HTTP 200 und zeigt alle 25 Notebooks. Image 1,31 -> 1,46 GB. Es laeuft ohne Token, deshalb im README die Portfreigabe an 127.0.0.1 gebunden. README KOMPLETT NEU. Es war die Bau-Anleitung eines privaten Verzeichnisses und ist jetzt die Visitenkarte eines oeffentlichen Repositorys: was das Buch ist, wo man es liest, drei Wege die Beispiele auszufuehren (Container, schlanke Installation, alles auf einmal), was hier liegt, wie man baut, die Colab-Frage, die Lizenzen und wie man mitwirkt. Alle relativen Links geprueft: 0 tot. .gitattributes. Ohne die Datei entschied core.autocrlf des jeweiligen Rechners, was beim Klonen passiert - ein Windows-Leser bekam CRLF-Rauschen in jedem Diff. Jetzt: im Repository immer LF, im Arbeitsverzeichnis passend zum System, Binaerdateien ausdruecklich ausgenommen. Der Bestand war bereits sauber (git add --renormalize aendert null Dateien). Zusaetzlich sind die erzeugten Verzeichnisse als linguist-generated markiert, sonst zaehlt die Sprachstatistik das Repository als HTML-Projekt. PROGRESS.md: Remote-Repository als erledigt markiert, der Colab-Befund festgehalten. CLAUDE.md um Veroeffentlichung, Lizenzen und die neuen Dateien ergaenzt. Geprueft: --check ohne Fehler, 0 tote README-Links, 76 Programme unveraendert, 33 pytest-Tests, pyproject baut mit acht Gruppen, PDF 759 Seiten (eine weniger - die Colab-Zeile entfaellt in 25 Kapiteln). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 14:54:04 +02:00
<p><strong>Notebook:</strong> <a href="Notebooks_04/dynamische-programmierung.ipynb">dynamische-programmierung.ipynb</a> — herunterladen und in Jupyter öffnen, in Colab hochladen oder mit dem Kurs-Image starten</p>
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>
2026-09-08 01:20:09 +02:00
</blockquote>
</div>
<hr />
<h2 id="sec:dynamische-programmierung-schnellstart">13.1 In 5 Minuten gelöst</h2>
<div class="card card-schnellstart">
<blockquote>
<p><strong>🚀 In 5 Minuten gelöst: Der Weg, der falsch anfängt</strong></p>
<p>Ein Fahrer soll vom Depot zum Kunden. Es gibt zwei Zwischenetappen, jeweils mit zwei Möglichkeiten. Die Zahlen sind Fahrminuten:</p>
<table>
<thead>
<tr class="header">
<th>von</th>
<th>nach</th>
<th style="text-align: right;">Minuten</th>
<th></th>
<th>von</th>
<th>nach</th>
<th style="text-align: right;">Minuten</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Start</td>
<td><strong>Nord</strong></td>
<td style="text-align: right;"><strong>4</strong></td>
<td></td>
<td>Süd</td>
<td>Ost</td>
<td style="text-align: right;">3</td>
</tr>
<tr class="even">
<td>Start</td>
<td>Süd</td>
<td style="text-align: right;">6</td>
<td></td>
<td>Süd</td>
<td>West</td>
<td style="text-align: right;">7</td>
</tr>
<tr class="odd">
<td>Nord</td>
<td>Ost</td>
<td style="text-align: right;">14</td>
<td></td>
<td>Ost</td>
<td>Ziel</td>
<td style="text-align: right;">5</td>
</tr>
<tr class="even">
<td>Nord</td>
<td>West</td>
<td style="text-align: right;">9</td>
<td></td>
<td>West</td>
<td>Ziel</td>
<td style="text-align: right;">8</td>
</tr>
</tbody>
</table>
<div class="sourceCode" id="cb1"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb1-1"><a href="#cb1-1" aria-hidden="true" tabindex="-1"></a>kanten <span class="op">=</span> {</span>
<span id="cb1-2"><a href="#cb1-2" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Start&quot;</span>: {<span class="st">&quot;Nord&quot;</span>: <span class="dv">4</span>, <span class="st">&quot;Sued&quot;</span>: <span class="dv">6</span>},</span>
<span id="cb1-3"><a href="#cb1-3" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Nord&quot;</span>: {<span class="st">&quot;Ost&quot;</span>: <span class="dv">14</span>, <span class="st">&quot;West&quot;</span>: <span class="dv">9</span>},</span>
<span id="cb1-4"><a href="#cb1-4" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Sued&quot;</span>: {<span class="st">&quot;Ost&quot;</span>: <span class="dv">3</span>, <span class="st">&quot;West&quot;</span>: <span class="dv">7</span>},</span>
<span id="cb1-5"><a href="#cb1-5" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Ost&quot;</span>: {<span class="st">&quot;Ziel&quot;</span>: <span class="dv">5</span>},</span>
<span id="cb1-6"><a href="#cb1-6" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;West&quot;</span>: {<span class="st">&quot;Ziel&quot;</span>: <span class="dv">8</span>},</span>
<span id="cb1-7"><a href="#cb1-7" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Ziel&quot;</span>: {},</span>
<span id="cb1-8"><a href="#cb1-8" aria-hidden="true" tabindex="-1"></a>}</span>
<span id="cb1-9"><a href="#cb1-9" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-10"><a href="#cb1-10" aria-hidden="true" tabindex="-1"></a>wert, plan <span class="op">=</span> {<span class="st">&quot;Ziel&quot;</span>: <span class="dv">0</span>}, {} <span class="co"># Restkosten ab jedem Ort</span></span>
<span id="cb1-11"><a href="#cb1-11" aria-hidden="true" tabindex="-1"></a><span class="cf">for</span> ort <span class="kw">in</span> [<span class="st">&quot;Ost&quot;</span>, <span class="st">&quot;West&quot;</span>, <span class="st">&quot;Nord&quot;</span>, <span class="st">&quot;Sued&quot;</span>, <span class="st">&quot;Start&quot;</span>]: <span class="co"># RUECKWAERTS!</span></span>
<span id="cb1-12"><a href="#cb1-12" aria-hidden="true" tabindex="-1"></a> wert[ort], plan[ort] <span class="op">=</span> <span class="bu">min</span>((k <span class="op">+</span> wert[z], z) <span class="cf">for</span> z, k <span class="kw">in</span> kanten[ort].items())</span>
<span id="cb1-13"><a href="#cb1-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb1-14"><a href="#cb1-14" aria-hidden="true" tabindex="-1"></a>weg, ort <span class="op">=</span> [<span class="st">&quot;Start&quot;</span>], <span class="st">&quot;Start&quot;</span></span>
<span id="cb1-15"><a href="#cb1-15" aria-hidden="true" tabindex="-1"></a><span class="cf">while</span> ort <span class="op">!=</span> <span class="st">&quot;Ziel&quot;</span>:</span>
<span id="cb1-16"><a href="#cb1-16" aria-hidden="true" tabindex="-1"></a> ort <span class="op">=</span> plan[ort]</span>
<span id="cb1-17"><a href="#cb1-17" aria-hidden="true" tabindex="-1"></a> weg.append(ort)</span>
<span id="cb1-18"><a href="#cb1-18" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot; -&gt; &quot;</span>.join(weg), <span class="st">&quot;=&quot;</span>, wert[<span class="st">&quot;Start&quot;</span>], <span class="st">&quot;min&quot;</span>)</span>
<span id="cb1-19"><a href="#cb1-19" aria-hidden="true" tabindex="-1"></a><span class="bu">print</span>(<span class="st">&quot;Restkosten je Ort:&quot;</span>, wert)</span></code></pre></div>
<p><strong>Ausgabe:</strong></p>
<pre><code>Start -&gt; Sued -&gt; Ost -&gt; Ziel = 14 min
Restkosten je Ort: {&#39;Ziel&#39;: 0, &#39;Ost&#39;: 5, &#39;West&#39;: 8, &#39;Nord&#39;: 17, &#39;Sued&#39;: 8, &#39;Start&#39;: 14}</code></pre>
</blockquote>
</div>
<p><strong>Der optimale Weg beginnt mit dem <em>teureren</em> ersten Abschnitt.</strong> Start → Süd kostet 6 Minuten, Start → Nord nur 4. Wer vorne spart, landet bei 21 Minuten statt 14 — <strong>50 % mehr</strong>:</p>
<table>
<colgroup>
<col style="width: 30%" />
<col style="width: 30%" />
<col style="width: 40%" />
</colgroup>
<thead>
<tr class="header">
<th>Vorgehen</th>
<th>Route</th>
<th style="text-align: right;">Dauer</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Gierig: „nimm immer die kürzeste nächste Strecke“</td>
<td>Start → Nord → West → Ziel</td>
<td style="text-align: right;">21 min</td>
</tr>
<tr class="even">
<td><strong>Rückwärtsinduktion</strong></td>
<td>Start → <strong>Süd</strong> → Ost → Ziel</td>
<td style="text-align: right;"><strong>14 min</strong></td>
</tr>
</tbody>
</table>
<p>Der Grund steht in der zweiten Ausgabezeile. <code>Restkosten je Ort</code> ist die <strong>Wertfunktion</strong>: Sie sagt für jeden Ort, was der ganze Rest von dort aus noch kostet, wenn man ab dort optimal weiterfährt. Und dort steht das Entscheidende: Von Nord aus sind es noch <strong>17</strong> Minuten, von Süd nur <strong>8</strong>. Die vier gesparten Minuten am Anfang werden später mit neun zusätzlichen bezahlt.</p>
<blockquote>
<p><strong>🎯 Merksatz</strong> Eine Entscheidung ist nie für sich zu bewerten, sondern nur <strong>zusammen mit allem, was danach kommt</strong>. Genau das leistet die Wertfunktion: Sie fasst die gesamte Zukunft eines Zustands in einer einzigen Zahl zusammen. Und weil man die Zukunft kennen muss, bevor man die Gegenwart bewerten kann, rechnet dynamische Programmierung <strong>rückwärts</strong>.</p>
</blockquote>
<p><strong>Warum funktioniert das?</strong> Weil man den Rest des Weges nicht neu durchdenken muss, sobald man einmal an einem Ort steht: Wie man dorthin gekommen ist, ist für die Zukunft egal. Diese Eigenschaft heißt <strong>Bellmansches Optimalitätsprinzip</strong> und ist der Grund, warum aus einem Problem mit exponentiell vielen Wegen eine Rechnung mit wenigen Zeilen wird. Bei fünf Orten fällt das kaum auf — bei fünfzig ist es der Unterschied zwischen Sekunden und Jahren.</p>
<hr />
<h2 id="sec:dynamische-programmierung-lernziele">13.2 Lernziele</h2>
<p>Nach diesem Kapitel können Sie …</p>
<ol type="1">
<li>… das Bellmansche Optimalitätsprinzip formulieren und erklären, warum es gilt.</li>
<li>… Zustand, Stufe, Aktion und Wertfunktion für ein gegebenes Problem benennen.</li>
<li>… eine Rückwärtsinduktion von Hand für ein kleines Problem durchführen.</li>
<li>… das Almgren-Chriss-Ausführungsproblem lösen und die Lösung interpretieren.</li>
<li>… einschätzen, wann DP funktioniert und wann der „Fluch der Dimensionalität“ zuschlägt.</li>
<li>… die Wertfunktion als <strong>Nachschlagetabelle</strong> lesen: Sie liefert keine Planfolge, sondern eine Regel, die auch bei Abweichungen gilt.</li>
<li>… einen Zustandsraum auf Vollständigkeit prüfen und erkennen, wann eine Kostenart Vorgeschichte erfordert.</li>
</ol>
<hr />
<h2 id="sec:dynamische-programmierung-das-bellmansche-optimalitaetsprinzip">13.3 Das Bellmansche Optimalitätsprinzip</h2>
<p>Richard Bellman formulierte 1957 das Grundprinzip der <strong>Dynamischen Programmierung (DP)</strong>:</p>
<blockquote>
<p><strong>Optimalitätsprinzip.</strong> Eine optimale Strategie hat die Eigenschaft, dass — unabhängig vom Anfangszustand und der Anfangsentscheidung — die verbleibenden Entscheidungen eine optimale Strategie bezüglich des Zustands bilden, der aus der ersten Entscheidung resultiert.</p>
</blockquote>
<p><strong>Ohne Fachsprache:</strong> <em>Jedes Teilstück einer optimalen Route ist selbst eine optimale Route.</em> Wenn der beste Weg von Hamburg nach München über Kassel führt, dann ist der Teil von Kassel nach München auch der beste Weg von Kassel nach München. Sonst könnte man ihn ersetzen und wäre insgesamt besser.</p>
<figure>
<img src="bilder_04/kap10_zustandskette.svg" alt="Abb. 13.1: Sequenzielle Zustands-/Aktionskette" />
<figcaption aria-hidden="true">Abb. 13.1: Sequenzielle Zustands-/Aktionskette</figcaption>
</figure>
<h3 id="die-vier-bausteine-eines-dp-modells">Die vier Bausteine eines DP-Modells</h3>
<table>
<colgroup>
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
<col style="width: 25%" />
</colgroup>
<thead>
<tr class="header">
<th>Baustein</th>
<th>Symbol</th>
<th>Frage</th>
<th>Beispiel Orderausführung</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><strong>Stufe</strong></td>
<td><span class="math inline">t</span></td>
<td>Wann wird entschieden?</td>
<td>Handelsperiode 0, 1, …, 5</td>
</tr>
<tr class="even">
<td><strong>Zustand</strong></td>
<td><span class="math inline">S_t</span></td>
<td>Was beschreibt die Lage vollständig?</td>
<td>Verbleibende Aktien <span class="math inline">X_t</span></td>
</tr>
<tr class="odd">
<td><strong>Aktion</strong></td>
<td><span class="math inline">a_t</span></td>
<td>Was darf man tun?</td>
<td>Verkaufe <span class="math inline">n_t</span> Stück</td>
</tr>
<tr class="even">
<td><strong>Wertfunktion</strong></td>
<td><span class="math inline">V_t(S_t)</span></td>
<td>Was kosten die optimalen Restentscheidungen?</td>
<td>Minimale Restkosten</td>
</tr>
</tbody>
</table>
<blockquote>
<p><strong>🎯 Die Kunst liegt in der Zustandsdefinition</strong> Der Zustand muss <strong>alles</strong> enthalten, was für die Zukunft relevant ist — und <strong>nichts mehr</strong>. Zu wenig: Das Modell ist falsch (Markov-Eigenschaft verletzt). Zu viel: Der Zustandsraum explodiert. Diese Abwägung ist die eigentliche Modellierungsleistung bei DP.</p>
</blockquote>
<h3 id="die-bellman-gleichung">Die Bellman-Gleichung</h3>
<p><span class="math display">
V_t(S_t) = \min_{a_t\in\mathcal{A}(S_t)}\Big\{\,C(S_t,a_t) \;+\; \gamma\cdot\mathbb{E}\big[V_{t+1}(S_{t+1})\mid S_t,a_t\big]\Big\}
</span></p>
<p>mit der Endbedingung <span class="math inline">V_T(S_T) = g(S_T)</span>.</p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <span class="math inline">V_t(S_t)</span> — „Was kostet mich der Rest, wenn ich zum Zeitpunkt <span class="math inline">t</span> im Zustand <span class="math inline">S_t</span> bin und ab jetzt optimal handle?“ * <span class="math inline">C(S_t,a_t)</span> — die <strong>sofortigen</strong> Kosten der Aktion <span class="math inline">a_t</span>. * <span class="math inline">V_{t+1}(S_{t+1})</span> — die Restkosten <strong>danach</strong>, wieder optimal. * <span class="math inline">\gamma</span> — Diskontfaktor (bei kurzen Horizonten meist <span class="math inline">\gamma = 1</span>). * <span class="math inline">\mathbb{E}[\cdot]</span> — Erwartungswert, falls der Übergang zufällig ist.</p>
<p><strong>Ohne Formel gesagt:</strong> „Der Wert von hier ist: die Kosten des nächsten Schritts plus der Wert von dort — und zwar für denjenigen nächsten Schritt, bei dem diese Summe am kleinsten ist.“</p>
<p><strong>Warum rückwärts?</strong> Weil <span class="math inline">V_{t+1}</span> bekannt sein muss, bevor man <span class="math inline">V_t</span> berechnen kann. Am Ende (<span class="math inline">t = T</span>) ist der Wert bekannt — dort fängt man an und arbeitet sich nach vorn.</p>
</blockquote>
<div class="card card-formel">
<blockquote>
<p><strong>🔤 Formel-Übersetzer: die Bellman-Gleichung Baustein für Baustein</strong></p>
<table>
<colgroup>
<col style="width: 50%" />
<col style="width: 50%" />
</colgroup>
<thead>
<tr class="header">
<th>Mathematik</th>
<th>Alltagssprache</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td><span class="math inline">S_t</span></td>
<td>„Alles, was ich jetzt über meine Lage wissen muss.“ Restmenge, Lagerbestand, Position — <strong>und sonst nichts</strong>.</td>
</tr>
<tr class="even">
<td><span class="math inline">a_t</span></td>
<td>„Was tue ich jetzt?“ Die Entscheidung dieser Stufe.</td>
</tr>
<tr class="odd">
<td><span class="math inline">\mathcal{A}(S_t)</span></td>
<td>„Was ist von hier aus überhaupt erlaubt?“ Die zulässigen Aktionen hängen vom Zustand ab.</td>
</tr>
<tr class="even">
<td><span class="math inline">C(S_t, a_t)</span></td>
<td>„Was kostet mich das sofort?“</td>
</tr>
<tr class="odd">
<td><span class="math inline">V_{t+1}(S_{t+1})</span></td>
<td>„Was kostet mich der ganze Rest danach — wenn ich ab dort optimal handle?“</td>
</tr>
<tr class="even">
<td><span class="math inline">\mathbb{E}[\cdot]</span></td>
<td>„… gemittelt über das, was der Zufall dazwischenwirft.“ Entfällt bei sicheren Übergängen.</td>
</tr>
<tr class="odd">
<td><span class="math inline">\gamma</span></td>
<td>„Wie viel ist mir ein Euro nächstes Jahr heute wert?“ Bei kurzen Horizonten meist 1.</td>
</tr>
<tr class="even">
<td><span class="math inline">\min_{a_t}\{\dots\}</span></td>
<td>„Und von allen erlaubten Aktionen nehme ich die, bei der <em>Sofortkosten plus Rest</em> am kleinsten ist.“</td>
</tr>
<tr class="odd">
<td><span class="math inline">V_T(S_T) = g(S_T)</span></td>
<td>„Am Ende steht fest, was die Endlage wert ist.“ Hier fängt die Rechnung an.</td>
</tr>
</tbody>
</table>
<p><strong>Die ganze Gleichung in einem Satz:</strong> <em>Der Wert von hier ist: was der nächste Schritt kostet, plus was es von dort aus noch kostet — für denjenigen nächsten Schritt, bei dem diese Summe am kleinsten ist.</em></p>
<p><strong>Und die Zeile, die man sich merken sollte:</strong> Sobald Sie <span class="math inline">V_t</span> für alle Zustände kennen, ist die optimale Strategie eine reine <strong>Nachschlagetabelle</strong> — für jeden Zustand steht darin, was zu tun ist. Das ist der praktische Ertrag der ganzen Rechnung: nicht ein Plan, sondern eine <strong>Regel</strong>, die auch dann noch gilt, wenn es anders kommt als gedacht.</p>
</blockquote>
</div>
<blockquote>
<p><strong>✏️ Handrechnung 13.1: Rückwärtsinduktion von Hand</strong></p>
<p><strong>Problem:</strong> Sie müssen 3 Einheiten in 2 Perioden verkaufen. Sofortkosten je Periode: <span class="math inline">C(n) = n^2</span> (großer Verkauf drückt den Preis überproportional). In der letzten Periode muss alles weg.</p>
<p><strong>Schritt 1 — Endstufe <span class="math inline">t = 2</span>:</strong> Es sind keine Perioden mehr übrig, also <span class="math inline">V_2(0) = 0</span>, alle anderen Zustände sind unzulässig.</p>
<p><strong>Schritt 2 — Stufe <span class="math inline">t = 1</span></strong> (letzte Verkaufsperiode, alles muss weg):</p>
<table>
<thead>
<tr class="header">
<th>Zustand <span class="math inline">X_1</span></th>
<th>erzwungene Aktion</th>
<th>Kosten</th>
<th><span class="math inline">V_1</span></th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
</tr>
<tr class="even">
<td>1</td>
<td>1</td>
<td>1</td>
<td>1</td>
</tr>
<tr class="odd">
<td>2</td>
<td>2</td>
<td>4</td>
<td>4</td>
</tr>
<tr class="even">
<td>3</td>
<td>3</td>
<td>9</td>
<td>9</td>
</tr>
</tbody>
</table>
<p><strong>Schritt 3 — Stufe <span class="math inline">t = 0</span></strong> (Start mit <span class="math inline">X_0 = 3</span>), alle Aktionen prüfen:</p>
<table>
<colgroup>
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
<col style="width: 20%" />
</colgroup>
<thead>
<tr class="header">
<th>Aktion <span class="math inline">n_0</span></th>
<th>Sofortkosten <span class="math inline">n_0^2</span></th>
<th>Rest <span class="math inline">X_1</span></th>
<th><span class="math inline">V_1(X_1)</span></th>
<th>Summe</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>0</td>
<td>0</td>
<td>3</td>
<td>9</td>
<td>9</td>
</tr>
<tr class="even">
<td>1</td>
<td>1</td>
<td>2</td>
<td>4</td>
<td>5</td>
</tr>
<tr class="odd">
<td><strong>2</strong></td>
<td><strong>4</strong></td>
<td><strong>1</strong></td>
<td><strong>1</strong></td>
<td><strong>5</strong></td>
</tr>
<tr class="even">
<td>3</td>
<td>9</td>
<td>0</td>
<td>0</td>
<td>9</td>
</tr>
</tbody>
</table>
<p><strong>Ergebnis:</strong> <span class="math inline">V_0(3) = 5</span>, erreicht durch <span class="math inline">n_0 = 1</span> (dann <span class="math inline">n_1 = 2</span>) <strong>oder</strong> <span class="math inline">n_0 = 2</span> (dann <span class="math inline">n_1 = 1</span>). Zwei gleichwertige Optima.</p>
<p><strong>Die Lehre:</strong> Alles auf einmal zu verkaufen kostet 9, gleichmäßiges Aufteilen nur 5. Das ist der Kern des Ausführungsproblems: <strong>Bei überproportionalen Kosten lohnt sich Stückelung.</strong> Und beachten Sie, dass wir nur <span class="math inline">4 + 4 = 8</span> Kombinationen bewertet haben statt aller Verkaufspfade — bei größeren Problemen ist dieser Unterschied dramatisch.</p>
</blockquote>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Bellman_Minimalbeispiel.py</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Dynamische Programmierung: Die Handrechnung zur Rueckwaertsinduktion als Code.</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a><span class="co">Zeigt die Wertfunktionstabelle und die optimale Politik Schritt fuer Schritt.</span></span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb3-8"><a href="#cb3-8" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-9"><a href="#cb3-9" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb3-10"><a href="#cb3-10" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-11"><a href="#cb3-11" aria-hidden="true" tabindex="-1"></a>GESAMT <span class="op">=</span> <span class="dv">3</span> <span class="co"># zu verkaufende Einheiten</span></span>
<span id="cb3-12"><a href="#cb3-12" aria-hidden="true" tabindex="-1"></a>PERIODEN <span class="op">=</span> <span class="dv">2</span> <span class="co"># Anzahl Verkaufsperioden</span></span>
<span id="cb3-13"><a href="#cb3-13" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-14"><a href="#cb3-14" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-15"><a href="#cb3-15" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> kosten(menge: <span class="bu">int</span>) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb3-16"><a href="#cb3-16" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Ueberproportionale Marktauswirkung: doppelte Menge kostet vierfach.&quot;&quot;&quot;</span></span>
<span id="cb3-17"><a href="#cb3-17" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> <span class="bu">float</span>(menge <span class="op">**</span> <span class="dv">2</span>)</span>
<span id="cb3-18"><a href="#cb3-18" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-19"><a href="#cb3-19" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-20"><a href="#cb3-20" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_rueckwaerts():</span>
<span id="cb3-21"><a href="#cb3-21" aria-hidden="true" tabindex="-1"></a> <span class="co"># V[t, x] = minimale Restkosten, wenn zu Beginn von Periode t noch x Stueck offen sind</span></span>
<span id="cb3-22"><a href="#cb3-22" aria-hidden="true" tabindex="-1"></a> V <span class="op">=</span> np.full((PERIODEN <span class="op">+</span> <span class="dv">1</span>, GESAMT <span class="op">+</span> <span class="dv">1</span>), np.inf)</span>
<span id="cb3-23"><a href="#cb3-23" aria-hidden="true" tabindex="-1"></a> politik <span class="op">=</span> np.zeros((PERIODEN, GESAMT <span class="op">+</span> <span class="dv">1</span>), dtype<span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb3-24"><a href="#cb3-24" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-25"><a href="#cb3-25" aria-hidden="true" tabindex="-1"></a> <span class="co"># Endbedingung: nach der letzten Periode darf nichts mehr offen sein</span></span>
<span id="cb3-26"><a href="#cb3-26" aria-hidden="true" tabindex="-1"></a> V[PERIODEN, <span class="dv">0</span>] <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb3-27"><a href="#cb3-27" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-28"><a href="#cb3-28" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-29"><a href="#cb3-29" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; RUECKWAERTSINDUKTION SCHRITT FUER SCHRITT&quot;</span>)</span>
<span id="cb3-30"><a href="#cb3-30" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-31"><a href="#cb3-31" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-32"><a href="#cb3-32" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN <span class="op">-</span> <span class="dv">1</span>, <span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">1</span>):</span>
<span id="cb3-33"><a href="#cb3-33" aria-hidden="true" tabindex="-1"></a> letzte_periode <span class="op">=</span> (t <span class="op">==</span> PERIODEN <span class="op">-</span> <span class="dv">1</span>)</span>
<span id="cb3-34"><a href="#cb3-34" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Stufe t = </span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">&quot;</span> <span class="op">+</span> (<span class="st">&quot; (letzte Periode: alles muss weg)&quot;</span> <span class="cf">if</span> letzte_periode</span>
<span id="cb3-35"><a href="#cb3-35" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span> <span class="st">&quot; (freie Wahl der Menge)&quot;</span>))</span>
<span id="cb3-36"><a href="#cb3-36" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span><span class="st">&#39;Zustand x&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;beste Aktion&#39;</span><span class="sc">:&gt;12}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Sofortkosten&#39;</span><span class="sc">:&gt;13}</span><span class="ss"> | &quot;</span></span>
<span id="cb3-37"><a href="#cb3-37" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;V[t+1]&#39;</span><span class="sc">:&gt;9}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;V[t]&#39;</span><span class="sc">:&gt;8}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-38"><a href="#cb3-38" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; &quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">62</span>)</span>
<span id="cb3-39"><a href="#cb3-39" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-40"><a href="#cb3-40" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> x <span class="kw">in</span> <span class="bu">range</span>(GESAMT <span class="op">+</span> <span class="dv">1</span>):</span>
<span id="cb3-41"><a href="#cb3-41" aria-hidden="true" tabindex="-1"></a> aktionen <span class="op">=</span> [x] <span class="cf">if</span> letzte_periode <span class="cf">else</span> <span class="bu">range</span>(x <span class="op">+</span> <span class="dv">1</span>)</span>
<span id="cb3-42"><a href="#cb3-42" aria-hidden="true" tabindex="-1"></a> bester_wert, beste_aktion, beste_teile <span class="op">=</span> np.inf, <span class="dv">0</span>, (<span class="fl">0.0</span>, <span class="fl">0.0</span>)</span>
<span id="cb3-43"><a href="#cb3-43" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-44"><a href="#cb3-44" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> n <span class="kw">in</span> aktionen:</span>
<span id="cb3-45"><a href="#cb3-45" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> x <span class="op">-</span> n</span>
<span id="cb3-46"><a href="#cb3-46" aria-hidden="true" tabindex="-1"></a> sofort <span class="op">=</span> kosten(n)</span>
<span id="cb3-47"><a href="#cb3-47" aria-hidden="true" tabindex="-1"></a> zukunft <span class="op">=</span> V[t <span class="op">+</span> <span class="dv">1</span>, rest]</span>
<span id="cb3-48"><a href="#cb3-48" aria-hidden="true" tabindex="-1"></a> gesamt <span class="op">=</span> sofort <span class="op">+</span> zukunft</span>
<span id="cb3-49"><a href="#cb3-49" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> gesamt <span class="op">&lt;</span> bester_wert:</span>
<span id="cb3-50"><a href="#cb3-50" aria-hidden="true" tabindex="-1"></a> bester_wert, beste_aktion <span class="op">=</span> gesamt, n</span>
<span id="cb3-51"><a href="#cb3-51" aria-hidden="true" tabindex="-1"></a> beste_teile <span class="op">=</span> (sofort, zukunft)</span>
<span id="cb3-52"><a href="#cb3-52" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-53"><a href="#cb3-53" aria-hidden="true" tabindex="-1"></a> V[t, x] <span class="op">=</span> bester_wert</span>
<span id="cb3-54"><a href="#cb3-54" aria-hidden="true" tabindex="-1"></a> politik[t, x] <span class="op">=</span> beste_aktion</span>
<span id="cb3-55"><a href="#cb3-55" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; </span><span class="sc">{</span>x<span class="sc">:&gt;10}</span><span class="ss"> | </span><span class="sc">{</span>beste_aktion<span class="sc">:&gt;12}</span><span class="ss"> | </span><span class="sc">{</span>beste_teile[<span class="dv">0</span>]<span class="sc">:&gt;13.1f}</span><span class="ss"> | &quot;</span></span>
<span id="cb3-56"><a href="#cb3-56" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>beste_teile[<span class="dv">1</span>]<span class="sc">:&gt;9.1f}</span><span class="ss"> | </span><span class="sc">{</span>bester_wert<span class="sc">:&gt;8.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-57"><a href="#cb3-57" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-58"><a href="#cb3-58" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> V, politik</span>
<span id="cb3-59"><a href="#cb3-59" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-60"><a href="#cb3-60" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-61"><a href="#cb3-61" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb3-62"><a href="#cb3-62" aria-hidden="true" tabindex="-1"></a> V, politik <span class="op">=</span> loese_rueckwaerts()</span>
<span id="cb3-63"><a href="#cb3-63" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-64"><a href="#cb3-64" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vorwaertspfad: der optimalen Politik folgen ---------------------</span></span>
<span id="cb3-65"><a href="#cb3-65" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-66"><a href="#cb3-66" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; OPTIMALER PFAD (Vorwaertssimulation)&quot;</span>)</span>
<span id="cb3-67"><a href="#cb3-67" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb3-68"><a href="#cb3-68" aria-hidden="true" tabindex="-1"></a> bestand <span class="op">=</span> GESAMT</span>
<span id="cb3-69"><a href="#cb3-69" aria-hidden="true" tabindex="-1"></a> gesamtkosten <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb3-70"><a href="#cb3-70" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN):</span>
<span id="cb3-71"><a href="#cb3-71" aria-hidden="true" tabindex="-1"></a> aktion <span class="op">=</span> politik[t, bestand]</span>
<span id="cb3-72"><a href="#cb3-72" aria-hidden="true" tabindex="-1"></a> gesamtkosten <span class="op">+=</span> kosten(aktion)</span>
<span id="cb3-73"><a href="#cb3-73" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Periode </span><span class="sc">{</span>t<span class="sc">}</span><span class="ss">: Bestand </span><span class="sc">{</span>bestand<span class="sc">}</span><span class="ss"> -&gt; verkaufe </span><span class="sc">{</span>aktion<span class="sc">}</span><span class="ss"> &quot;</span></span>
<span id="cb3-74"><a href="#cb3-74" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(Kosten </span><span class="sc">{</span>kosten(aktion)<span class="sc">:.1f}</span><span class="ss">) -&gt; Rest </span><span class="sc">{</span>bestand <span class="op">-</span> aktion<span class="sc">}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-75"><a href="#cb3-75" aria-hidden="true" tabindex="-1"></a> bestand <span class="op">-=</span> aktion</span>
<span id="cb3-76"><a href="#cb3-76" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-77"><a href="#cb3-77" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Gesamtkosten: </span><span class="sc">{</span>gesamtkosten<span class="sc">:.1f}</span><span class="ss"> (V[0, </span><span class="sc">{</span>GESAMT<span class="sc">}</span><span class="ss">] = </span><span class="sc">{</span>V[<span class="dv">0</span>, GESAMT]<span class="sc">:.1f}</span><span class="ss">)&quot;</span>)</span>
<span id="cb3-78"><a href="#cb3-78" aria-hidden="true" tabindex="-1"></a> <span class="cf">assert</span> <span class="bu">abs</span>(gesamtkosten <span class="op">-</span> V[<span class="dv">0</span>, GESAMT]) <span class="op">&lt;</span> <span class="fl">1e-9</span>, <span class="st">&quot;Pfadkosten != Wertfunktion!&quot;</span></span>
<span id="cb3-79"><a href="#cb3-79" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb3-80"><a href="#cb3-80" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vergleich mit naiven Strategien ---------------------------------</span></span>
<span id="cb3-81"><a href="#cb3-81" aria-hidden="true" tabindex="-1"></a> alles_sofort <span class="op">=</span> kosten(GESAMT)</span>
<span id="cb3-82"><a href="#cb3-82" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss"> Zum Vergleich - alles in Periode 0 verkaufen: </span><span class="sc">{</span>alles_sofort<span class="sc">:.1f}</span><span class="ss">&quot;</span>)</span>
<span id="cb3-83"><a href="#cb3-83" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot; Ersparnis durch Stueckelung: </span><span class="sc">{</span>alles_sofort <span class="op">-</span> gesamtkosten<span class="sc">:.1f}</span><span class="ss"> &quot;</span></span>
<span id="cb3-84"><a href="#cb3-84" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(</span><span class="sc">{</span>(<span class="dv">1</span> <span class="op">-</span> gesamtkosten<span class="op">/</span>alles_sofort)<span class="op">*</span><span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> %)&quot;</span>)</span>
<span id="cb3-85"><a href="#cb3-85" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span></code></pre></div>
<hr />
<h2 id="sec:dynamische-programmierung-das-almgren-chriss-problem-optimale-orderausfuehrung">13.4 Das Almgren-Chriss-Problem: optimale Orderausführung</h2>
<p>Ein klassisches OR-Problem an Börsen: Ein institutioneller Händler muss <span class="math inline">X_0 = 100\,000</span> Aktien innerhalb von <span class="math inline">T</span> Handelsperioden verkaufen.</p>
<p><strong>Der Zielkonflikt:</strong></p>
<ol type="1">
<li><strong>Marktauswirkung (<em>market impact</em>, Slippage)</strong>. Verkauft man zu schnell, bricht das Orderbuch ein — der Ausführungspreis verschlechtert sich. Diese Kosten wachsen <strong>überproportional</strong> mit der Ordergröße.</li>
<li><strong>Zeitrisiko (<em>timing risk</em>).</strong> Wartet man zu lange, schwankt der Marktpreis. Je größer der noch offene Restbestand, desto mehr Geld liegt im Risiko.</li>
</ol>
<p>Das Modell nach Almgren und Chriss (2000) minimiert die Summe aus beidem:</p>
<p><span class="math display">
C(n_t, X_t) = \underbrace{\eta\,n_t^2}_{\text{Marktauswirkung}} \;+\; \underbrace{\lambda\,\sigma^2 X_t^2}_{\text{Risiko des Restbestands}}
</span></p>
<blockquote>
<p><strong>📐 Formel-Lesehilfe</strong> * <span class="math inline">n_t</span> — in Periode <span class="math inline">t</span> verkaufte Stückzahl. Quadriert, weil doppelte Menge mehr als doppelten Preisdruck erzeugt. * <span class="math inline">\eta</span> („eta“) — Slippage-Koeffizient. Wie stark reagiert der Markt auf Volumen? * <span class="math inline">X_t</span> — Restbestand <strong>nach</strong> dem Verkauf. Quadriert, weil Varianz quadratisch mit der Positionsgröße wächst. * <span class="math inline">\lambda</span> — Risikoaversion: Wie sehr stört mich Schwankung im Vergleich zu Slippage? * <span class="math inline">\sigma</span> — Volatilität je Periode.</p>
<p><strong>Ohne Formel gesagt:</strong> „Schnell verkaufen kostet Preisabschlag. Langsam verkaufen kostet Nervenkitzel. Finde die Mitte.“</p>
<p><strong>Die zwei Extremfälle:</strong> * <span class="math inline">\lambda \to 0</span> (risikoneutral): gleichmäßiges Aufteilen auf alle Perioden minimiert <span class="math inline">\sum n_t^2</span> bei fester Summe. * <span class="math inline">\lambda \to \infty</span> (extrem risikoscheu): sofort alles verkaufen, um kein Risiko zu tragen.</p>
</blockquote>
<blockquote>
<p><strong>⚠️ Zur Kalibrierung des Risikoparameters</strong></p>
<p>Ein häufiger Fehler ist ein undokumentierter Skalierungsfaktor im Risikoterm, etwa:</p>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a>holding_risk <span class="op">=</span> risk_aversion <span class="op">*</span> (sigma_period <span class="op">**</span> <span class="dv">2</span>) <span class="op">*</span> (remaining <span class="op">**</span> <span class="dv">2</span>) <span class="op">*</span> <span class="fl">1e5</span></span></code></pre></div>
<p>Woher eine solche Zahl kommt, bleibt dann unklar. Faktisch wirkt sie als versteckte Erhöhung der Risikoaversion um fünf Größenordnungen — ein Parameter wie <code>risk_aversion = 1e-6</code> würde dann in Wahrheit <span class="math inline">0{,}1</span> bedeuten. Solche „magischen Zahlen“ machen ein Modell unkalibrierbar: Niemand kann sagen, ob 1e-6 viel oder wenig ist.</p>
<p><strong>Der Parameter lässt sich stattdessen herleiten.</strong> Beide Kostenterme werden in <strong>Euro</strong> gerechnet: * Marktauswirkung: <span class="math inline">\eta \cdot n_t^2</span> mit <span class="math inline">\eta</span> in €/Stück². * Risiko: <span class="math inline">\tfrac{\lambda}{2}\cdot \sigma_{\text{Periode}}^2 \cdot P_0^2 \cdot X_t^2</span> — die Varianz des Werts der offenen Position, multipliziert mit der Risikoaversion.</p>
<p>Durch die Multiplikation mit <span class="math inline">P_0^2</span> (dem Quadrat des Aktienkurses) stimmen die Einheiten, und <span class="math inline">\lambda</span> wird interpretierbar: Es ist der Preis, den man je Einheit Wertvarianz zu zahlen bereit ist.</p>
</blockquote>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="co">#!/usr/bin/env python3</span></span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a><span class="co"># Mehrperiodige_Order_Execution.py</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a><span class="co">Kapitel Dynamische Programmierung: Dynamische Programmierung fuer optimale Orderausfuehrung</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a><span class="co">(Almgren-Chriss-Rahmen, geloest per Rueckwaertsinduktion).</span></span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-8"><a href="#cb5-8" aria-hidden="true" tabindex="-1"></a><span class="co">Eigenschaften:</span></span>
<span id="cb5-9"><a href="#cb5-9" aria-hidden="true" tabindex="-1"></a><span class="co"> * Risikoterm sauber hergeleitet ueber den Aktienkurs P0 (Einheiten: EUR),</span></span>
<span id="cb5-10"><a href="#cb5-10" aria-hidden="true" tabindex="-1"></a><span class="co"> ohne undokumentierte Skalierungsfaktoren</span></span>
<span id="cb5-11"><a href="#cb5-11" aria-hidden="true" tabindex="-1"></a><span class="co"> * Vergleich mit der analytischen Almgren-Chriss-Loesung</span></span>
<span id="cb5-12"><a href="#cb5-12" aria-hidden="true" tabindex="-1"></a><span class="co"> * Vergleich mit naiven Strategien (alles sofort / gleichmaessig)</span></span>
<span id="cb5-13"><a href="#cb5-13" aria-hidden="true" tabindex="-1"></a><span class="co"> * Sensitivitaet gegenueber der Risikoaversion</span></span>
<span id="cb5-14"><a href="#cb5-14" aria-hidden="true" tabindex="-1"></a><span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-15"><a href="#cb5-15" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-16"><a href="#cb5-16" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> os</span>
<span id="cb5-17"><a href="#cb5-17" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-18"><a href="#cb5-18" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> numpy <span class="im">as</span> np</span>
<span id="cb5-19"><a href="#cb5-19" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> pandas <span class="im">as</span> pd</span>
<span id="cb5-20"><a href="#cb5-20" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> matplotlib</span>
<span id="cb5-21"><a href="#cb5-21" aria-hidden="true" tabindex="-1"></a>matplotlib.use(<span class="st">&quot;Agg&quot;</span>)</span>
<span id="cb5-22"><a href="#cb5-22" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> matplotlib.pyplot <span class="im">as</span> plt</span>
<span id="cb5-23"><a href="#cb5-23" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-24"><a href="#cb5-24" aria-hidden="true" tabindex="-1"></a>OUTPUT_DIR <span class="op">=</span> os.path.join(os.path.dirname(os.path.abspath(<span class="va">__file__</span>)), <span class="st">&quot;output&quot;</span>)</span>
<span id="cb5-25"><a href="#cb5-25" aria-hidden="true" tabindex="-1"></a>os.makedirs(OUTPUT_DIR, exist_ok<span class="op">=</span><span class="va">True</span>)</span>
<span id="cb5-26"><a href="#cb5-26" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-27"><a href="#cb5-27" aria-hidden="true" tabindex="-1"></a><span class="co"># --- Parameter -------------------------------------------------------------</span></span>
<span id="cb5-28"><a href="#cb5-28" aria-hidden="true" tabindex="-1"></a>GESAMTBESTAND <span class="op">=</span> <span class="dv">100_000</span> <span class="co"># X_0, zu verkaufende Aktien</span></span>
<span id="cb5-29"><a href="#cb5-29" aria-hidden="true" tabindex="-1"></a>PERIODEN <span class="op">=</span> <span class="dv">5</span> <span class="co"># T Handelsperioden</span></span>
<span id="cb5-30"><a href="#cb5-30" aria-hidden="true" tabindex="-1"></a>KURS <span class="op">=</span> <span class="fl">50.0</span> <span class="co"># P_0 in EUR, zur Skalierung des Risikoterms</span></span>
<span id="cb5-31"><a href="#cb5-31" aria-hidden="true" tabindex="-1"></a>ETA <span class="op">=</span> <span class="fl">2.5e-6</span> <span class="co"># EUR je Stueck^2 (Slippage-Koeffizient)</span></span>
<span id="cb5-32"><a href="#cb5-32" aria-hidden="true" tabindex="-1"></a>VOLA_JAHR <span class="op">=</span> <span class="fl">0.30</span> <span class="co"># 30 % p.a.</span></span>
<span id="cb5-33"><a href="#cb5-33" aria-hidden="true" tabindex="-1"></a>HANDELSSTUNDEN_JAHR <span class="op">=</span> <span class="dv">252</span> <span class="op">*</span> <span class="fl">6.5</span></span>
<span id="cb5-34"><a href="#cb5-34" aria-hidden="true" tabindex="-1"></a>RISIKOAVERSION <span class="op">=</span> <span class="fl">1e-5</span> <span class="co"># 1/EUR; kalibriert, siehe Kommentar unten</span></span>
<span id="cb5-35"><a href="#cb5-35" aria-hidden="true" tabindex="-1"></a>SCHRITTWEITE <span class="op">=</span> <span class="dv">1000</span> <span class="co"># Diskretisierung des Zustandsraums</span></span>
<span id="cb5-36"><a href="#cb5-36" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-37"><a href="#cb5-37" aria-hidden="true" tabindex="-1"></a><span class="co"># Kalibrierungshinweis: Die dimensionslose Kennzahl des Modells ist</span></span>
<span id="cb5-38"><a href="#cb5-38" aria-hidden="true" tabindex="-1"></a><span class="co"># kappa_tilde^2 = lambda * sigma_periode^2 * P0^2 / eta</span></span>
<span id="cb5-39"><a href="#cb5-39" aria-hidden="true" tabindex="-1"></a><span class="co"># Sie entscheidet ueber den Charakter der Loesung:</span></span>
<span id="cb5-40"><a href="#cb5-40" aria-hidden="true" tabindex="-1"></a><span class="co"># &lt;&lt; 1 -&gt; praktisch gleichmaessige Aufteilung (Risiko spielt keine Rolle)</span></span>
<span id="cb5-41"><a href="#cb5-41" aria-hidden="true" tabindex="-1"></a><span class="co"># ~ 1 -&gt; ausgewogener Kompromiss &lt;- hier: 0.55</span></span>
<span id="cb5-42"><a href="#cb5-42" aria-hidden="true" tabindex="-1"></a><span class="co"># &gt;&gt; 1 -&gt; fast alles sofort verkaufen</span></span>
<span id="cb5-43"><a href="#cb5-43" aria-hidden="true" tabindex="-1"></a><span class="co"># Genau diese Interpretierbarkeit geht mit einem undokumentierten</span></span>
<span id="cb5-44"><a href="#cb5-44" aria-hidden="true" tabindex="-1"></a><span class="co"># Skalierungsfaktor verloren.</span></span>
<span id="cb5-45"><a href="#cb5-45" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-46"><a href="#cb5-46" aria-hidden="true" tabindex="-1"></a>VOLA_PERIODE <span class="op">=</span> VOLA_JAHR <span class="op">/</span> np.sqrt(HANDELSSTUNDEN_JAHR)</span>
<span id="cb5-47"><a href="#cb5-47" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-48"><a href="#cb5-48" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-49"><a href="#cb5-49" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> periodenkosten(verkauf: <span class="bu">float</span>, restbestand: <span class="bu">float</span>) <span class="op">-&gt;</span> <span class="bu">float</span>:</span>
<span id="cb5-50"><a href="#cb5-50" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-51"><a href="#cb5-51" aria-hidden="true" tabindex="-1"></a><span class="co"> Sofortkosten einer Periode in EUR:</span></span>
<span id="cb5-52"><a href="#cb5-52" aria-hidden="true" tabindex="-1"></a><span class="co"> (1) Marktauswirkung: eta * n^2</span></span>
<span id="cb5-53"><a href="#cb5-53" aria-hidden="true" tabindex="-1"></a><span class="co"> (2) Risiko des Restbestands: lambda/2 * sigma^2 * P0^2 * X^2</span></span>
<span id="cb5-54"><a href="#cb5-54" aria-hidden="true" tabindex="-1"></a><span class="co"> -&gt; P0^2 macht aus &quot;Stueck^2&quot; einen EUR^2-Wert; lambda hat damit</span></span>
<span id="cb5-55"><a href="#cb5-55" aria-hidden="true" tabindex="-1"></a><span class="co"> die Einheit 1/EUR und ist interpretierbar.</span></span>
<span id="cb5-56"><a href="#cb5-56" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-57"><a href="#cb5-57" aria-hidden="true" tabindex="-1"></a> marktauswirkung <span class="op">=</span> ETA <span class="op">*</span> verkauf <span class="op">**</span> <span class="dv">2</span></span>
<span id="cb5-58"><a href="#cb5-58" aria-hidden="true" tabindex="-1"></a> wertvarianz <span class="op">=</span> (VOLA_PERIODE <span class="op">**</span> <span class="dv">2</span>) <span class="op">*</span> (KURS <span class="op">**</span> <span class="dv">2</span>) <span class="op">*</span> (restbestand <span class="op">**</span> <span class="dv">2</span>)</span>
<span id="cb5-59"><a href="#cb5-59" aria-hidden="true" tabindex="-1"></a> risiko <span class="op">=</span> <span class="fl">0.5</span> <span class="op">*</span> RISIKOAVERSION <span class="op">*</span> wertvarianz</span>
<span id="cb5-60"><a href="#cb5-60" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> marktauswirkung <span class="op">+</span> risiko</span>
<span id="cb5-61"><a href="#cb5-61" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-62"><a href="#cb5-62" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-63"><a href="#cb5-63" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> loese_dp():</span>
<span id="cb5-64"><a href="#cb5-64" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Rueckwaertsinduktion ueber den diskretisierten Zustandsraum.&quot;&quot;&quot;</span></span>
<span id="cb5-65"><a href="#cb5-65" aria-hidden="true" tabindex="-1"></a> zustaende <span class="op">=</span> np.arange(<span class="dv">0</span>, GESAMTBESTAND <span class="op">+</span> SCHRITTWEITE, SCHRITTWEITE)</span>
<span id="cb5-66"><a href="#cb5-66" aria-hidden="true" tabindex="-1"></a> anzahl <span class="op">=</span> <span class="bu">len</span>(zustaende)</span>
<span id="cb5-67"><a href="#cb5-67" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-68"><a href="#cb5-68" aria-hidden="true" tabindex="-1"></a> V <span class="op">=</span> np.full((PERIODEN <span class="op">+</span> <span class="dv">1</span>, anzahl), np.inf)</span>
<span id="cb5-69"><a href="#cb5-69" aria-hidden="true" tabindex="-1"></a> politik <span class="op">=</span> np.zeros((PERIODEN, anzahl), dtype<span class="op">=</span><span class="bu">int</span>)</span>
<span id="cb5-70"><a href="#cb5-70" aria-hidden="true" tabindex="-1"></a> V[PERIODEN, <span class="dv">0</span>] <span class="op">=</span> <span class="fl">0.0</span> <span class="co"># am Ende muss alles verkauft sein</span></span>
<span id="cb5-71"><a href="#cb5-71" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-72"><a href="#cb5-72" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN <span class="op">-</span> <span class="dv">1</span>, <span class="op">-</span><span class="dv">1</span>, <span class="op">-</span><span class="dv">1</span>):</span>
<span id="cb5-73"><a href="#cb5-73" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> idx, bestand <span class="kw">in</span> <span class="bu">enumerate</span>(zustaende):</span>
<span id="cb5-74"><a href="#cb5-74" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> t <span class="op">==</span> PERIODEN <span class="op">-</span> <span class="dv">1</span>:</span>
<span id="cb5-75"><a href="#cb5-75" aria-hidden="true" tabindex="-1"></a> moegliche <span class="op">=</span> [bestand] <span class="co"># letzte Periode: Rest muss weg</span></span>
<span id="cb5-76"><a href="#cb5-76" aria-hidden="true" tabindex="-1"></a> <span class="cf">else</span>:</span>
<span id="cb5-77"><a href="#cb5-77" aria-hidden="true" tabindex="-1"></a> moegliche <span class="op">=</span> zustaende[zustaende <span class="op">&lt;=</span> bestand]</span>
<span id="cb5-78"><a href="#cb5-78" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-79"><a href="#cb5-79" aria-hidden="true" tabindex="-1"></a> bester_wert, beste_aktion <span class="op">=</span> np.inf, <span class="dv">0</span></span>
<span id="cb5-80"><a href="#cb5-80" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> verkauf <span class="kw">in</span> moegliche:</span>
<span id="cb5-81"><a href="#cb5-81" aria-hidden="true" tabindex="-1"></a> rest <span class="op">=</span> bestand <span class="op">-</span> verkauf</span>
<span id="cb5-82"><a href="#cb5-82" aria-hidden="true" tabindex="-1"></a> rest_idx <span class="op">=</span> <span class="bu">int</span>(<span class="bu">round</span>(rest <span class="op">/</span> SCHRITTWEITE))</span>
<span id="cb5-83"><a href="#cb5-83" aria-hidden="true" tabindex="-1"></a> gesamt <span class="op">=</span> periodenkosten(verkauf, rest) <span class="op">+</span> V[t <span class="op">+</span> <span class="dv">1</span>, rest_idx]</span>
<span id="cb5-84"><a href="#cb5-84" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> gesamt <span class="op">&lt;</span> bester_wert:</span>
<span id="cb5-85"><a href="#cb5-85" aria-hidden="true" tabindex="-1"></a> bester_wert, beste_aktion <span class="op">=</span> gesamt, <span class="bu">int</span>(verkauf)</span>
<span id="cb5-86"><a href="#cb5-86" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-87"><a href="#cb5-87" aria-hidden="true" tabindex="-1"></a> V[t, idx] <span class="op">=</span> bester_wert</span>
<span id="cb5-88"><a href="#cb5-88" aria-hidden="true" tabindex="-1"></a> politik[t, idx] <span class="op">=</span> beste_aktion</span>
<span id="cb5-89"><a href="#cb5-89" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-90"><a href="#cb5-90" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> zustaende, V, politik</span>
<span id="cb5-91"><a href="#cb5-91" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-92"><a href="#cb5-92" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-93"><a href="#cb5-93" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> analytische_loesung():</span>
<span id="cb5-94"><a href="#cb5-94" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;</span></span>
<span id="cb5-95"><a href="#cb5-95" aria-hidden="true" tabindex="-1"></a><span class="co"> Geschlossene Almgren-Chriss-Loesung fuer den kontinuierlichen Fall.</span></span>
<span id="cb5-96"><a href="#cb5-96" aria-hidden="true" tabindex="-1"></a><span class="co"> Der optimale Pfad ist X_t = X_0 * sinh(kappa*(T-t)) / sinh(kappa*T)</span></span>
<span id="cb5-97"><a href="#cb5-97" aria-hidden="true" tabindex="-1"></a><span class="co"> mit kappa = arccosh(tilde_kappa^2/2 + 1), tilde_kappa^2 = lambda*sigma^2*P0^2/eta.</span></span>
<span id="cb5-98"><a href="#cb5-98" aria-hidden="true" tabindex="-1"></a><span class="co"> Dient hier als unabhaengige Kontrolle des DP-Ergebnisses.</span></span>
<span id="cb5-99"><a href="#cb5-99" aria-hidden="true" tabindex="-1"></a><span class="co"> &quot;&quot;&quot;</span></span>
<span id="cb5-100"><a href="#cb5-100" aria-hidden="true" tabindex="-1"></a> kappa_tilde_quadrat <span class="op">=</span> (RISIKOAVERSION <span class="op">*</span> (VOLA_PERIODE <span class="op">**</span> <span class="dv">2</span>) <span class="op">*</span> (KURS <span class="op">**</span> <span class="dv">2</span>)) <span class="op">/</span> ETA</span>
<span id="cb5-101"><a href="#cb5-101" aria-hidden="true" tabindex="-1"></a> kappa <span class="op">=</span> np.arccosh(kappa_tilde_quadrat <span class="op">/</span> <span class="fl">2.0</span> <span class="op">+</span> <span class="fl">1.0</span>)</span>
<span id="cb5-102"><a href="#cb5-102" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> kappa <span class="op">&lt;</span> <span class="fl">1e-12</span>: <span class="co"># Grenzfall: risikoneutral -&gt; linear</span></span>
<span id="cb5-103"><a href="#cb5-103" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> np.linspace(GESAMTBESTAND, <span class="dv">0</span>, PERIODEN <span class="op">+</span> <span class="dv">1</span>)</span>
<span id="cb5-104"><a href="#cb5-104" aria-hidden="true" tabindex="-1"></a> t <span class="op">=</span> np.arange(PERIODEN <span class="op">+</span> <span class="dv">1</span>)</span>
<span id="cb5-105"><a href="#cb5-105" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> GESAMTBESTAND <span class="op">*</span> np.sinh(kappa <span class="op">*</span> (PERIODEN <span class="op">-</span> t)) <span class="op">/</span> np.sinh(kappa <span class="op">*</span> PERIODEN)</span>
<span id="cb5-106"><a href="#cb5-106" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-107"><a href="#cb5-107" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-108"><a href="#cb5-108" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> bewerte_pfad(bestaende):</span>
<span id="cb5-109"><a href="#cb5-109" aria-hidden="true" tabindex="-1"></a> <span class="co">&quot;&quot;&quot;Gesamtkosten eines beliebigen Bestandspfades.&quot;&quot;&quot;</span></span>
<span id="cb5-110"><a href="#cb5-110" aria-hidden="true" tabindex="-1"></a> summe <span class="op">=</span> <span class="fl">0.0</span></span>
<span id="cb5-111"><a href="#cb5-111" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(<span class="bu">len</span>(bestaende) <span class="op">-</span> <span class="dv">1</span>):</span>
<span id="cb5-112"><a href="#cb5-112" aria-hidden="true" tabindex="-1"></a> verkauf <span class="op">=</span> bestaende[t] <span class="op">-</span> bestaende[t <span class="op">+</span> <span class="dv">1</span>]</span>
<span id="cb5-113"><a href="#cb5-113" aria-hidden="true" tabindex="-1"></a> summe <span class="op">+=</span> periodenkosten(verkauf, bestaende[t <span class="op">+</span> <span class="dv">1</span>])</span>
<span id="cb5-114"><a href="#cb5-114" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> summe</span>
<span id="cb5-115"><a href="#cb5-115" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-116"><a href="#cb5-116" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-117"><a href="#cb5-117" aria-hidden="true" tabindex="-1"></a><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">&quot;__main__&quot;</span>:</span>
<span id="cb5-118"><a href="#cb5-118" aria-hidden="true" tabindex="-1"></a> zustaende, V, politik <span class="op">=</span> loese_dp()</span>
<span id="cb5-119"><a href="#cb5-119" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-120"><a href="#cb5-120" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vorwaertspfad der optimalen Politik -----------------------------</span></span>
<span id="cb5-121"><a href="#cb5-121" aria-hidden="true" tabindex="-1"></a> bestand <span class="op">=</span> GESAMTBESTAND</span>
<span id="cb5-122"><a href="#cb5-122" aria-hidden="true" tabindex="-1"></a> verlauf <span class="op">=</span> [bestand]</span>
<span id="cb5-123"><a href="#cb5-123" aria-hidden="true" tabindex="-1"></a> verkaeufe <span class="op">=</span> []</span>
<span id="cb5-124"><a href="#cb5-124" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN):</span>
<span id="cb5-125"><a href="#cb5-125" aria-hidden="true" tabindex="-1"></a> idx <span class="op">=</span> <span class="bu">int</span>(<span class="bu">round</span>(bestand <span class="op">/</span> SCHRITTWEITE))</span>
<span id="cb5-126"><a href="#cb5-126" aria-hidden="true" tabindex="-1"></a> verkauf <span class="op">=</span> politik[t, idx]</span>
<span id="cb5-127"><a href="#cb5-127" aria-hidden="true" tabindex="-1"></a> verkaeufe.append(verkauf)</span>
<span id="cb5-128"><a href="#cb5-128" aria-hidden="true" tabindex="-1"></a> bestand <span class="op">-=</span> verkauf</span>
<span id="cb5-129"><a href="#cb5-129" aria-hidden="true" tabindex="-1"></a> verlauf.append(bestand)</span>
<span id="cb5-130"><a href="#cb5-130" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-131"><a href="#cb5-131" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb5-132"><a href="#cb5-132" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot; OPTIMALE MEHRPERIODIGE ORDER-EXECUTION (BELLMAN DP)&quot;</span>)</span>
<span id="cb5-133"><a href="#cb5-133" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb5-134"><a href="#cb5-134" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Gesamtvolumen: </span><span class="sc">{</span>GESAMTBESTAND<span class="sc">:,}</span><span class="ss"> Stueck zu </span><span class="sc">{</span>KURS<span class="sc">:.2f}</span><span class="ss"> EUR &quot;</span></span>
<span id="cb5-135"><a href="#cb5-135" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;= </span><span class="sc">{</span>GESAMTBESTAND<span class="op">*</span>KURS<span class="sc">:,.0f}</span><span class="ss"> EUR Positionswert&quot;</span>)</span>
<span id="cb5-136"><a href="#cb5-136" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Zeithorizont: </span><span class="sc">{</span>PERIODEN<span class="sc">}</span><span class="ss"> Handelsperioden&quot;</span>)</span>
<span id="cb5-137"><a href="#cb5-137" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Volatilitaet: </span><span class="sc">{</span>VOLA_JAHR<span class="op">*</span><span class="dv">100</span><span class="sc">:.0f}</span><span class="ss"> % p.a. &quot;</span></span>
<span id="cb5-138"><a href="#cb5-138" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;= </span><span class="sc">{</span>VOLA_PERIODE<span class="op">*</span><span class="dv">100</span><span class="sc">:.3f}</span><span class="ss"> % je Periode&quot;</span>)</span>
<span id="cb5-139"><a href="#cb5-139" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Slippage eta: </span><span class="sc">{</span>ETA<span class="sc">:.2e}</span><span class="ss"> EUR/Stueck^2&quot;</span>)</span>
<span id="cb5-140"><a href="#cb5-140" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Risikoaversion: </span><span class="sc">{</span>RISIKOAVERSION<span class="sc">:.2e}</span><span class="ss"> 1/EUR&quot;</span>)</span>
<span id="cb5-141"><a href="#cb5-141" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Erwartete Gesamtreibung: </span><span class="sc">{</span>V[<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>]<span class="sc">:,.2f}</span><span class="ss"> EUR &quot;</span></span>
<span id="cb5-142"><a href="#cb5-142" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(</span><span class="sc">{</span>V[<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>]<span class="op">/</span>(GESAMTBESTAND<span class="op">*</span>KURS)<span class="op">*</span><span class="dv">10000</span><span class="sc">:.1f}</span><span class="ss"> Basispunkte)</span><span class="ch">\n</span><span class="ss">&quot;</span>)</span>
<span id="cb5-143"><a href="#cb5-143" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-144"><a href="#cb5-144" aria-hidden="true" tabindex="-1"></a> plan <span class="op">=</span> pd.DataFrame([{</span>
<span id="cb5-145"><a href="#cb5-145" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Periode&quot;</span>: <span class="ss">f&quot;t = </span><span class="sc">{</span>t<span class="sc">}</span><span class="ss"> -&gt; </span><span class="sc">{</span>t<span class="op">+</span><span class="dv">1</span><span class="sc">}</span><span class="ss">&quot;</span>,</span>
<span id="cb5-146"><a href="#cb5-146" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Startbestand&quot;</span>: <span class="ss">f&quot;</span><span class="sc">{</span>verlauf[t]<span class="sc">:,}</span><span class="ss">&quot;</span>,</span>
<span id="cb5-147"><a href="#cb5-147" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Verkauf n_t&quot;</span>: <span class="ss">f&quot;</span><span class="sc">{</span>verkaeufe[t]<span class="sc">:,}</span><span class="ss">&quot;</span>,</span>
<span id="cb5-148"><a href="#cb5-148" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Restbestand&quot;</span>: <span class="ss">f&quot;</span><span class="sc">{</span>verlauf[t<span class="op">+</span><span class="dv">1</span>]<span class="sc">:,}</span><span class="ss">&quot;</span>,</span>
<span id="cb5-149"><a href="#cb5-149" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Anteil&quot;</span>: <span class="ss">f&quot;</span><span class="sc">{</span>verkaeufe[t]<span class="op">/</span>GESAMTBESTAND<span class="op">*</span><span class="dv">100</span><span class="sc">:5.1f}</span><span class="ss"> %&quot;</span>,</span>
<span id="cb5-150"><a href="#cb5-150" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;Kosten (EUR)&quot;</span>: <span class="ss">f&quot;</span><span class="sc">{</span>periodenkosten(verkaeufe[t], verlauf[t<span class="op">+</span><span class="dv">1</span>])<span class="sc">:,.0f}</span><span class="ss">&quot;</span>,</span>
<span id="cb5-151"><a href="#cb5-151" aria-hidden="true" tabindex="-1"></a> } <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN)])</span>
<span id="cb5-152"><a href="#cb5-152" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(plan.to_string(index<span class="op">=</span><span class="va">False</span>))</span>
<span id="cb5-153"><a href="#cb5-153" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-154"><a href="#cb5-154" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Vergleich mit Alternativen und der analytischen Loesung ---------</span></span>
<span id="cb5-155"><a href="#cb5-155" aria-hidden="true" tabindex="-1"></a> sofort <span class="op">=</span> [GESAMTBESTAND] <span class="op">+</span> [<span class="dv">0</span>] <span class="op">*</span> PERIODEN</span>
<span id="cb5-156"><a href="#cb5-156" aria-hidden="true" tabindex="-1"></a> gleichmaessig <span class="op">=</span> [GESAMTBESTAND <span class="op">*</span> (<span class="dv">1</span> <span class="op">-</span> t <span class="op">/</span> PERIODEN) <span class="cf">for</span> t <span class="kw">in</span> <span class="bu">range</span>(PERIODEN <span class="op">+</span> <span class="dv">1</span>)]</span>
<span id="cb5-157"><a href="#cb5-157" aria-hidden="true" tabindex="-1"></a> analytisch <span class="op">=</span> analytische_loesung()</span>
<span id="cb5-158"><a href="#cb5-158" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-159"><a href="#cb5-159" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">&quot;</span> <span class="op">+</span> <span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb5-160"><a href="#cb5-160" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;Strategie&#39;</span><span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Kosten (EUR)&#39;</span><span class="sc">:&gt;15}</span><span class="ss"> </span><span class="sc">{</span><span class="st">&#39;Basispunkte&#39;</span><span class="sc">:&gt;13}</span><span class="ss"> &quot;</span></span>
<span id="cb5-161"><a href="#cb5-161" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;ggue. Optimum&#39;</span><span class="sc">:&gt;16}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-162"><a href="#cb5-162" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb5-163"><a href="#cb5-163" aria-hidden="true" tabindex="-1"></a> optimum <span class="op">=</span> V[<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>]</span>
<span id="cb5-164"><a href="#cb5-164" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> name, pfad <span class="kw">in</span> [(<span class="st">&quot;DP-Optimum&quot;</span>, verlauf),</span>
<span id="cb5-165"><a href="#cb5-165" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Analytisch (Almgren-Chriss)&quot;</span>, <span class="bu">list</span>(analytisch)),</span>
<span id="cb5-166"><a href="#cb5-166" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Gleichmaessig (TWAP)&quot;</span>, gleichmaessig),</span>
<span id="cb5-167"><a href="#cb5-167" aria-hidden="true" tabindex="-1"></a> (<span class="st">&quot;Alles sofort&quot;</span>, sofort)]:</span>
<span id="cb5-168"><a href="#cb5-168" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> bewerte_pfad(pfad)</span>
<span id="cb5-169"><a href="#cb5-169" aria-hidden="true" tabindex="-1"></a> bp <span class="op">=</span> kosten <span class="op">/</span> (GESAMTBESTAND <span class="op">*</span> KURS) <span class="op">*</span> <span class="dv">10000</span></span>
<span id="cb5-170"><a href="#cb5-170" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>name<span class="sc">:&lt;34}</span><span class="ss"> </span><span class="sc">{</span>kosten<span class="sc">:&gt;15,.0f}</span><span class="ss"> </span><span class="sc">{</span>bp<span class="sc">:&gt;12.1f}</span><span class="ss"> &quot;</span></span>
<span id="cb5-171"><a href="#cb5-171" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;</span><span class="sc">{</span>kosten <span class="op">-</span> optimum<span class="sc">:&gt;+15,.0f}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-172"><a href="#cb5-172" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-173"><a href="#cb5-173" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span>
<span id="cb5-174"><a href="#cb5-174" aria-hidden="true" tabindex="-1"></a> abweichung <span class="op">=</span> <span class="bu">abs</span>(bewerte_pfad(<span class="bu">list</span>(analytisch)) <span class="op">-</span> optimum) <span class="op">/</span> optimum</span>
<span id="cb5-175"><a href="#cb5-175" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;Abweichung DP zur analytischen Loesung: </span><span class="sc">{</span>abweichung<span class="op">*</span><span class="dv">100</span><span class="sc">:.3f}</span><span class="ss"> % &quot;</span></span>
<span id="cb5-176"><a href="#cb5-176" aria-hidden="true" tabindex="-1"></a> <span class="ss">f&quot;(Diskretisierung: </span><span class="sc">{</span>SCHRITTWEITE<span class="sc">}</span><span class="ss"> Stueck)&quot;</span>)</span>
<span id="cb5-177"><a href="#cb5-177" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-178"><a href="#cb5-178" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Sensitivitaet gegenueber der Risikoaversion ---------------------</span></span>
<span id="cb5-179"><a href="#cb5-179" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;</span><span class="ch">\n</span><span class="st">--- Wie wirkt die Risikoaversion? ---&quot;</span>)</span>
<span id="cb5-180"><a href="#cb5-180" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span><span class="st">&#39;lambda&#39;</span><span class="sc">:&gt;10}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Verkauf in Periode 0&#39;</span><span class="sc">:&gt;22}</span><span class="ss"> | </span><span class="sc">{</span><span class="st">&#39;Charakter&#39;</span><span class="sc">:&lt;28}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-181"><a href="#cb5-181" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;-&quot;</span> <span class="op">*</span> <span class="dv">70</span>)</span>
<span id="cb5-182"><a href="#cb5-182" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> lam <span class="kw">in</span> [<span class="fl">1e-7</span>, <span class="fl">1e-6</span>, <span class="fl">1e-5</span>, <span class="fl">1e-4</span>, <span class="fl">1e-3</span>]:</span>
<span id="cb5-183"><a href="#cb5-183" aria-hidden="true" tabindex="-1"></a> <span class="bu">globals</span>()[<span class="st">&quot;RISIKOAVERSION&quot;</span>] <span class="op">=</span> lam</span>
<span id="cb5-184"><a href="#cb5-184" aria-hidden="true" tabindex="-1"></a> _, V_l, pol_l <span class="op">=</span> loese_dp()</span>
<span id="cb5-185"><a href="#cb5-185" aria-hidden="true" tabindex="-1"></a> erste <span class="op">=</span> pol_l[<span class="dv">0</span>, <span class="op">-</span><span class="dv">1</span>]</span>
<span id="cb5-186"><a href="#cb5-186" aria-hidden="true" tabindex="-1"></a> anteil <span class="op">=</span> erste <span class="op">/</span> GESAMTBESTAND <span class="op">*</span> <span class="dv">100</span></span>
<span id="cb5-187"><a href="#cb5-187" aria-hidden="true" tabindex="-1"></a> charakter <span class="op">=</span> (<span class="st">&quot;nahezu gleichmaessig&quot;</span> <span class="cf">if</span> anteil <span class="op">&lt;</span> <span class="dv">25</span> <span class="cf">else</span></span>
<span id="cb5-188"><a href="#cb5-188" aria-hidden="true" tabindex="-1"></a> <span class="st">&quot;front-loaded&quot;</span> <span class="cf">if</span> anteil <span class="op">&lt;</span> <span class="dv">60</span> <span class="cf">else</span> <span class="st">&quot;fast alles sofort&quot;</span>)</span>
<span id="cb5-189"><a href="#cb5-189" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="sc">{</span>lam<span class="sc">:&gt;10.0e}</span><span class="ss"> | </span><span class="sc">{</span>erste<span class="sc">:&gt;13,}</span><span class="ss"> (</span><span class="sc">{</span>anteil<span class="sc">:5.1f}</span><span class="ss"> %) | </span><span class="sc">{</span>charakter<span class="sc">:&lt;28}</span><span class="ss">&quot;</span>)</span>
<span id="cb5-190"><a href="#cb5-190" aria-hidden="true" tabindex="-1"></a> <span class="bu">globals</span>()[<span class="st">&quot;RISIKOAVERSION&quot;</span>] <span class="op">=</span> <span class="fl">1e-5</span> <span class="co"># zuruecksetzen</span></span>
<span id="cb5-191"><a href="#cb5-191" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb5-192"><a href="#cb5-192" aria-hidden="true" tabindex="-1"></a> <span class="co"># --- Diagramm ---------------------------------------------------------</span></span>
<span id="cb5-193"><a href="#cb5-193" aria-hidden="true" tabindex="-1"></a> plt.figure(figsize<span class="op">=</span>(<span class="dv">10</span>, <span class="fl">5.5</span>))</span>
<span id="cb5-194"><a href="#cb5-194" aria-hidden="true" tabindex="-1"></a> plt.plot(<span class="bu">range</span>(PERIODEN <span class="op">+</span> <span class="dv">1</span>), verlauf, <span class="st">&quot;o-&quot;</span>, lw<span class="op">=</span><span class="fl">2.5</span>, label<span class="op">=</span><span class="st">&quot;DP-Optimum&quot;</span>)</span>
<span id="cb5-195"><a href="#cb5-195" aria-hidden="true" tabindex="-1"></a> plt.plot(<span class="bu">range</span>(PERIODEN <span class="op">+</span> <span class="dv">1</span>), analytisch, <span class="st">&quot;s--&quot;</span>, lw<span class="op">=</span><span class="fl">1.8</span>, alpha<span class="op">=</span><span class="fl">0.8</span>,</span>
<span id="cb5-196"><a href="#cb5-196" aria-hidden="true" tabindex="-1"></a> label<span class="op">=</span><span class="st">&quot;Analytisch (Almgren-Chriss)&quot;</span>)</span>
<span id="cb5-197"><a href="#cb5-197" aria-hidden="true" tabindex="-1"></a> plt.plot(<span class="bu">range</span>(PERIODEN <span class="op">+</span> <span class="dv">1</span>), gleichmaessig, <span class="st">&quot;^:&quot;</span>, lw<span class="op">=</span><span class="fl">1.8</span>, alpha<span class="op">=</span><span class="fl">0.8</span>,</span>
<span id="cb5-198"><a href="#cb5-198" aria-hidden="true" tabindex="-1"></a> label<span class="op">=</span><span class="st">&quot;Gleichmaessig (TWAP)&quot;</span>)</span>
<span id="cb5-199"><a href="#cb5-199" aria-hidden="true" tabindex="-1"></a> plt.bar(<span class="bu">range</span>(PERIODEN), verkaeufe, alpha<span class="op">=</span><span class="fl">0.25</span>, color<span class="op">=</span><span class="st">&quot;orange&quot;</span>, width<span class="op">=</span><span class="fl">0.45</span>,</span>
<span id="cb5-200"><a href="#cb5-200" aria-hidden="true" tabindex="-1"></a> label<span class="op">=</span><span class="st">&quot;Verkaufstranche $n_t$&quot;</span>)</span>
<span id="cb5-201"><a href="#cb5-201" aria-hidden="true" tabindex="-1"></a> plt.title(<span class="st">&quot;Optimaler Liquidationspfad ueber diskrete Perioden&quot;</span>, fontsize<span class="op">=</span><span class="dv">12</span>)</span>
<span id="cb5-202"><a href="#cb5-202" aria-hidden="true" tabindex="-1"></a> plt.xlabel(<span class="st">&quot;Handelsperiode $t$&quot;</span>)</span>
<span id="cb5-203"><a href="#cb5-203" aria-hidden="true" tabindex="-1"></a> plt.ylabel(<span class="st">&quot;Verbleibender Bestand $X_t$&quot;</span>)</span>
<span id="cb5-204"><a href="#cb5-204" aria-hidden="true" tabindex="-1"></a> plt.xticks(<span class="bu">range</span>(PERIODEN <span class="op">+</span> <span class="dv">1</span>))</span>
<span id="cb5-205"><a href="#cb5-205" aria-hidden="true" tabindex="-1"></a> plt.grid(<span class="va">True</span>, linestyle<span class="op">=</span><span class="st">&quot;:&quot;</span>, alpha<span class="op">=</span><span class="fl">0.6</span>)</span>
<span id="cb5-206"><a href="#cb5-206" aria-hidden="true" tabindex="-1"></a> plt.legend()</span>
<span id="cb5-207"><a href="#cb5-207" aria-hidden="true" tabindex="-1"></a> plt.tight_layout()</span>
<span id="cb5-208"><a href="#cb5-208" aria-hidden="true" tabindex="-1"></a> ziel <span class="op">=</span> os.path.join(OUTPUT_DIR, <span class="st">&quot;optimal_execution_dp.png&quot;</span>)</span>
<span id="cb5-209"><a href="#cb5-209" aria-hidden="true" tabindex="-1"></a> plt.savefig(ziel, dpi<span class="op">=</span><span class="dv">150</span>)</span>
<span id="cb5-210"><a href="#cb5-210" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="ss">f&quot;</span><span class="ch">\n</span><span class="ss">Diagramm gespeichert unter &#39;</span><span class="sc">{</span>ziel<span class="sc">}</span><span class="ss">&#39;&quot;</span>)</span>
<span id="cb5-211"><a href="#cb5-211" aria-hidden="true" tabindex="-1"></a> <span class="bu">print</span>(<span class="st">&quot;=&quot;</span> <span class="op">*</span> <span class="dv">84</span>)</span></code></pre></div>
<p><strong>Erwartete Ausgabe:</strong></p>
<pre><code>====================================================================================
OPTIMALE MEHRPERIODIGE ORDER-EXECUTION (BELLMAN DP)
====================================================================================
Gesamtvolumen: 100,000 Stueck zu 50.00 EUR = 5,000,000 EUR Positionswert
Zeithorizont: 5 Handelsperioden
Volatilitaet: 30 % p.a. = 0.741 % je Periode
Slippage eta: 2.50e-06 EUR/Stueck^2
Risikoaversion: 1.00e-05 1/EUR
Erwartete Gesamtreibung: 10,266.24 EUR (20.5 Basispunkte)
Periode Startbestand Verkauf n_t Restbestand Anteil Kosten (EUR)
t = 0 -&gt; 1 100,000 41,000 59,000 41.0 % 6,593
t = 1 -&gt; 2 59,000 25,000 34,000 25.0 % 2,356
t = 2 -&gt; 3 34,000 16,000 18,000 16.0 % 863
t = 3 -&gt; 4 18,000 10,000 8,000 10.0 % 294
t = 4 -&gt; 5 8,000 8,000 0 8.0 % 160
------------------------------------------------------------------------------------
Strategie Kosten (EUR) Basispunkte ggue. Optimum
------------------------------------------------------------------------------------
DP-Optimum 10,266 20.5 +0
Analytisch (Almgren-Chriss) 10,860 21.7 +594
Gleichmaessig (TWAP) 13,242 26.5 +2,976
Alles sofort 25,000 50.0 +14,734
------------------------------------------------------------------------------------
Abweichung DP zur analytischen Loesung: 5.788 % (Diskretisierung: 1000 Stueck)
--- Wie wirkt die Risikoaversion? ---
lambda | Verkauf in Periode 0 | Charakter
----------------------------------------------------------------------
1e-07 | 20,000 ( 20.0 %) | nahezu gleichmaessig
1e-06 | 23,000 ( 23.0 %) | nahezu gleichmaessig
1e-05 | 41,000 ( 41.0 %) | front-loaded
1e-04 | 78,000 ( 78.0 %) | fast alles sofort
1e-03 | 97,000 ( 97.0 %) | fast alles sofort
====================================================================================</code></pre>
<p><strong>Die Lösung ist <em>front-loaded</em>:</strong> 41 % im ersten Schritt, dann fallend (25 %, 16 %, 10 %, 8 %). Das ist die typische Form — man baut Risiko früh ab, aber nicht abrupt. Die Sensitivitätstabelle zeigt, wie <span class="math inline">\lambda</span> zwischen den beiden Extremen steuert: Bei <span class="math inline">\lambda = 10^{-7}</span> verkauft das Modell gleichmäßig (Risiko ist egal), bei <span class="math inline">\lambda = 10^{-3}</span> praktisch alles sofort (Risiko dominiert).</p>
<p>Der Vergleich beziffert den Nutzen: Gegenüber der naiven TWAP-Strategie (gleiche Tranchen) spart die optimierte Ausführung <strong>2 976 €</strong> oder 6 Basispunkte — bei einer Position von 5 Mio. €. Gegenüber „alles sofort“ sind es 14 734 €.</p>
<blockquote>
<p><strong>💻 Code-Durchgang: die analytische Gegenprobe und was ihre Abweichung bedeutet</strong></p>
<p>Das Almgren-Chriss-Problem hat eine <strong>geschlossene Lösung</strong>: <span class="math display">X_t = X_0\,\frac{\sinh(\kappa(T-t))}{\sinh(\kappa T)}, \qquad \kappa = \operatorname{arcosh}\!\Big(\tfrac{\tilde\kappa^2}{2}+1\Big),\qquad \tilde\kappa^2 = \frac{\lambda\,\sigma^2 P_0^2}{\eta}</span></p>
<p>Sie liefert hier 10 860 € gegenüber 10 266 € beim DP — <strong>eine Abweichung von 5,8 %, und zwar zugunsten des DP.</strong> Das ist kein Fehler, sondern lehrreich, und es hat zwei Ursachen:</p>
<ol type="1">
<li><strong>Unterschiedliche Zeitkonvention.</strong> Die geschlossene Formel gilt für die zeitkontinuierliche Variante des Modells, in der das Risiko über das gesamte Intervall integriert wird. Unser diskretes Modell belastet dagegen den Bestand <strong>nach</strong> dem Verkauf. Beide Varianten sind legitim, sie lösen aber leicht verschiedene Probleme.</li>
<li><strong>Diskretisierung.</strong> Der Zustandsraum ist in 1000er-Schritten gerastert; die analytische Lösung darf beliebige Stückzahlen wählen.</li>
</ol>
<p><strong>Die Lehre daraus ist wichtiger als die Zahl:</strong> Eine Vergleichsrechnung, die <em>ungefähr</em> passt, bestätigt die Größenordnung und die Form der Lösung — beide Pfade sind front-loaded, beide liegen bei rund 21 Basispunkten. Sie beweist aber nicht die Punktgenauigkeit, solange die Modellkonventionen nicht identisch sind. <strong>Wer zwei Zahlen vergleicht, muss zuerst prüfen, ob sie dasselbe messen.</strong></p>
<p><strong>Übernehmen Sie dennoch das Prinzip:</strong> Wo immer eine unabhängige zweite Rechnung möglich ist — Formel, Simulation, Handrechnung —, bauen Sie sie ein. Hätte das DP hier 500 000 € oder 12 € geliefert, wäre der Fehler sofort aufgefallen.</p>
</blockquote>
<hr />
<h2 id="sec:dynamische-programmierung-der-fluch-der-dimensionalitaet">13.5 Der Fluch der Dimensionalität</h2>
<p>DP ist mächtig, aber es hat eine harte Grenze. Der Aufwand der Rückwärtsinduktion ist</p>
<p><span class="math display">
\mathcal{O}(T \cdot |\mathcal{S}| \cdot |\mathcal{A}|)
</span></p>
<p>— Perioden mal Zustände mal Aktionen. Im Beispiel oben: <span class="math inline">5 \times 101 \times 101 \approx 51\,000</span> Auswertungen, in Sekundenbruchteilen erledigt.</p>
<p>Das Problem entsteht, sobald der Zustand <strong>mehrere Dimensionen</strong> hat:</p>
<table>
<colgroup>
<col style="width: 33%" />
<col style="width: 33%" />
<col style="width: 33%" />
</colgroup>
<thead>
<tr class="header">
<th>Zustandsbeschreibung</th>
<th>Zustandsraum</th>
<th>Machbar?</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Restbestand (101 Stufen)</td>
<td>101</td>
<td>✓ trivial</td>
</tr>
<tr class="even">
<td>+ aktueller Kurs (50 Stufen)</td>
<td>5 050</td>
<td>✓ leicht</td>
</tr>
<tr class="odd">
<td>+ Orderbuchtiefe (20 Stufen)</td>
<td>101 000</td>
<td>✓ noch gut</td>
</tr>
<tr class="even">
<td>+ 10 weitere Titel mit je 101 Stufen</td>
<td><span class="math inline">101^{11} \approx 10^{22}</span></td>
<td>✗ hoffnungslos</td>
</tr>
</tbody>
</table>
<p><strong>Das ist der Fluch der Dimensionalität</strong> (Bellmans eigener Begriff): Jede zusätzliche Zustandsvariable <strong>multipliziert</strong> den Aufwand.</p>
<p><strong>Gegenmittel:</strong> * <strong>Zustandsraum</strong> <strong>verkleinern:</strong> gröber diskretisieren, irrelevante Variablen weglassen. * <strong>Approximate Dynamic Programming</strong><strong>:</strong> <span class="math inline">V_t</span> durch eine parametrische Funktion annähern statt tabellarisch zu speichern. * <strong>Reinforcement Learning</strong><strong>:</strong> dieselbe Bellman-Gleichung, aber <span class="math inline">V</span> wird aus Erfahrung gelernt (Q-Learning) statt vollständig berechnet. * <strong>Nach geschlossenen Lösungen suchen</strong> — wie bei Almgren-Chriss. Wo eine Formel existiert, ist sie unschlagbar.</p>
<hr />
<h2 id="sec:dynamische-programmierung-uebungsaufgaben">13.6 Übungsaufgaben</h2>
<blockquote>
<p>Lösungen: <a href="anhang-loesungen.html#sec:loesungen-dynamische-programmierung">Abschnitt A.13</a>.</p>
</blockquote>
<p><strong>Aufgabe 13.1 ⭐ — Bausteine benennen.</strong> Ein Wanderer plant eine 5-Tages-Tour und muss täglich entscheiden, wie weit er läuft. Bestimmen Sie Stufe, Zustand, Aktion und Wertfunktion. Was gehört <strong>nicht</strong> in den Zustand?</p>
<p><strong>Aufgabe 13.2 ⭐ — Optimalitätsprinzip anwenden.</strong> Warum folgt aus dem Optimalitätsprinzip, dass man rückwärts rechnen darf? Was würde schiefgehen, wenn die Kosten einer Periode auch von <strong>früheren</strong> Aktionen abhängen (und nicht nur vom aktuellen Zustand)?</p>
<p><strong>Aufgabe 13.3 ⭐⭐ — Rückwärtsinduktion von Hand.</strong> 5 Einheiten in 3 Perioden, Kosten <span class="math inline">C(n) = n^2 + 2n</span>. Erstellen Sie die vollständige Wertfunktionstabelle und bestimmen Sie den optimalen Pfad. Prüfen Sie mit <code>Bellman_Minimalbeispiel.py</code> (angepasst).</p>
<p><strong>Aufgabe 13.4 ⭐⭐ — Rucksackproblem als DP.</strong> Lösen Sie das Rucksackproblem aus <a href="milp.html#kap-milp">Kapitel 6</a> mit dynamischer Programmierung statt MILP. (Zustand: verbleibende Kapazität; Stufe: betrachteter Gegenstand.) Vergleichen Sie Laufzeit und Ergebnis mit dem MILP-Solver.</p>
<p><strong>Aufgabe 13.5 ⭐⭐⭐ — Risikoaversion kalibrieren.</strong> Untersuchen Sie mit <code>Mehrperiodige_Order_Execution.py</code>: (a) Bei welchem <span class="math inline">\lambda</span> verkauft das Modell in der ersten Periode mehr als 50 %? (b) Stellen Sie den Zusammenhang zwischen <span class="math inline">\lambda</span> und den erwarteten Gesamtkosten dar. (c) Ein Händler sagt: „Ich will höchstens 20 % Marktauswirkungskosten und den Rest an Risiko.“ Welches <span class="math inline">\lambda</span> setzen Sie?</p>
<p><strong>Aufgabe 13.6 ⭐⭐⭐ — Zustandsraum erweitern.</strong> Erweitern Sie das Ausführungsmodell um einen zweiten Zustand: die aktuelle <strong>Orderbuchtiefe</strong> (3 Stufen: dünn/normal/tief), die mit gegebenen Übergangswahrscheinlichkeiten wechselt und <span class="math inline">\eta</span> um Faktor 2 / 1 / 0,5 skaliert. Wie ändert sich die Strategie? Wie stark wächst die Rechenzeit?</p>
<hr />
<h2 id="sec:dynamische-programmierung-denkfehler">13.7 Finde den Denkfehler</h2>
<p>Bei dynamischer Programmierung entscheidet eine einzige Frage über Erfolg oder Misserfolg: <strong>Was gehört in den Zustand?</strong> Ist er zu klein, rechnet das Verfahren völlig korrekt — nur eben an einem anderen Problem.</p>
<div class="card card-denkfehler">
<blockquote>
Sechster und siebter Fund: die letzten handgeschriebenen Nummern Der Loesungsanhang trug 98 Marken der Bauart "**9.2 — Ansatz waehlen.**", saemtlich aus Version 03, wo Unsicherheit Kapitel 9 war. Heute ist es Kapitel 12, und die Aufgabe wird korrekt als "Aufgabe 12.2" gesetzt. Das war keine Schoenheitsfrage: "Aufgabe 9.2" existiert wirklich - sie gehoert zum Kapitel Metaheuristiken ("Zuggroesse und Temperatur"). Wer die Loesung zu 9.2 nachschlug, landete beim falschen Thema. Beide bisherigen Pruefungen liefen vorbei: Vor der Zahl steht kein Schluesselwort, und es ist keine Tabellenzelle. Zwei Messungen machten die Reparatur billig. In allen 15 nummerierten Abschnitten gab es exakt so viele Loesungen wie Aufgaben, lueckenlos 1..n - nur der Kapitelteil war falsch. Und die acht in Phase 3 ergaenzten Kapitel machten es laengst richtig (**Titel.** ohne Nummer); der Anhang wurde also nicht auf etwas Neues umgestellt, sondern auf das, was seine neueren Teile schon taten. nummeriere_marken() vergibt die Marken jetzt selbst. Der Praefix kommt NICHT aus "# Anhang A:" - der Anhang ist eine Ueberschrift, seine Abschnitte gehoeren aber zu 23 verschiedenen Kapiteln. Er kommt aus {#sec:loesungen-X} -> {#kap:X}; diese Zuordnung gilt geprueft fuer alle 23. Im Quelltext steht "**{loesung} — Titel.**". Siebter Fund unterwegs: neun Verweise auf Denkfehler-Nummern. Saetze wie "siehe Denkfehler 8.1" standen in alter Zaehlung - "8" war in Version 03 das QP/NLP-Kapitel, heute ist es Graphen. Eine blosse Umnummerierung haette sie stillschweigend woanders hin zeigen lassen; das Ziel wurde deshalb fuer jeden einzelnen aus dem Zusammenhang bestimmt und auf {ref:sec:<kapitel>-denkfehler} umgestellt. Einer steckte in einem Programm-Docstring und bekam nach Regel 12 den Kapitelnamen statt eines Verweises. Eine bewusste Abweichung vom Plan: Der Denkfehler bekommt GAR KEINE Nummer. Es gibt je Kapitel genau einen, die zweite Stelle waere immer .1, und er steht ohnehin unter einer nummerierten Ueberschrift. Eine Nummer, die nie variiert, holte nur die Fragilitaet zurueck, die hier beseitigt wird. Auch das folgt den acht neuen Kapiteln, die ihren Denkfehler schon vorher nur mit dem Titel des Raetsels ankuendigen. --check bewacht jetzt alle Familien (Denkfehler N.M, Micro-Quiz N, **N.M — im Anhang) und zaehlt zusaetzlich ab, dass jeder Loesungsabschnitt so viele {loesung}-Marken hat wie sein Kapitel Aufgaben - das faengt eine vergessene oder doppelte Loesung, was keine Textsuche leisten kann. Gegengetestet mit vier kuenstlichen Fehlern auf einmal: alle vier gemeldet, nach dem Rueckbau null. Gegenprobe der Umstellung: Gesamtdokument vorher gesichert, nachher verglichen. 168 geaenderte Zeilen, restlos einer Familie zuzuordnen - 118 Loesungsmarken (138 gesamt minus 20, die in den Kapiteln 1 bis 3 zufaellig schon stimmten), 15 Anhang-Ueberschriften, 15 Kapitel-Callouts, 12 Micro-Quiz, 7 Zeilen mit Verweisen, 1 Docstring. Keine verschobene oder inhaltlich veraenderte Zeile. Ergebnis: 138 Aufgaben, 138 Loesungen, keine ohne Gegenstueck. Micro-Quiz tragen die echten Kapitelnummern. 293 Abschnitte, 720 Querverweise, 327 Indexmarken, PDF unveraendert 725 Seiten, 33 pytest-Tests, 67 netzfreie Programme fehlerfrei (die vier yfinance-Programme scheiterten am Rate-Limit des Anbieters, nicht an dieser Aenderung). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:52:41 +02:00
<p><strong>🐛 Finde den Denkfehler: Der Zustand, der zu wenig weiß</strong></p>
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>
2026-09-08 01:20:09 +02:00
<p>Eine Werkstatt plant vier Perioden. Der Bedarf beträgt 3, 1, 4, 2 Stück; produziert werden können höchstens 5 je Periode, gelagert höchstens 6 Stück.</p>
<table>
<colgroup>
<col style="width: 42%" />
<col style="width: 57%" />
</colgroup>
<thead>
<tr class="header">
<th>Kostenart</th>
<th style="text-align: right;">Höhe</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>Stückkosten</td>
<td style="text-align: right;">4 €</td>
</tr>
<tr class="even">
<td>Lagerkosten je Stück und Periode</td>
<td style="text-align: right;">2 €</td>
</tr>
<tr class="odd">
<td><strong>Rüstkosten</strong> — fallen an, wenn produziert wird <strong>und in der Vorperiode nicht</strong></td>
<td style="text-align: right;">30 €</td>
</tr>
</tbody>
</table>
<p>Der Entwickler überlegt: <em>„Für die Zukunft zählt nur, wie viel ich auf Lager habe. Also ist der Lagerbestand mein Zustand.“</em></p>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="im">import</span> functools</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a><span class="at">@functools.lru_cache</span>(<span class="va">None</span>)</span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> V(t, lager):</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> t <span class="op">==</span> <span class="dv">4</span>:</span>
<span id="cb7-6"><a href="#cb7-6" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> (<span class="fl">0.0</span>, ())</span>
<span id="cb7-7"><a href="#cb7-7" aria-hidden="true" tabindex="-1"></a> best <span class="op">=</span> (<span class="bu">float</span>(<span class="st">&quot;inf&quot;</span>), ())</span>
<span id="cb7-8"><a href="#cb7-8" aria-hidden="true" tabindex="-1"></a> <span class="cf">for</span> p <span class="kw">in</span> <span class="bu">range</span>(KAP <span class="op">+</span> <span class="dv">1</span>):</span>
<span id="cb7-9"><a href="#cb7-9" aria-hidden="true" tabindex="-1"></a> neu <span class="op">=</span> lager <span class="op">+</span> p <span class="op">-</span> BEDARF[t]</span>
<span id="cb7-10"><a href="#cb7-10" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> neu <span class="op">&lt;</span> <span class="dv">0</span> <span class="kw">or</span> neu <span class="op">&gt;</span> LAGER_MAX:</span>
<span id="cb7-11"><a href="#cb7-11" aria-hidden="true" tabindex="-1"></a> <span class="cf">continue</span></span>
<span id="cb7-12"><a href="#cb7-12" aria-hidden="true" tabindex="-1"></a> kosten <span class="op">=</span> (RUESTKOSTEN <span class="cf">if</span> p <span class="op">&gt;</span> <span class="dv">0</span> <span class="cf">else</span> <span class="dv">0</span>) <span class="op">+</span> STUECKKOSTEN <span class="op">*</span> p <span class="op">+</span> LAGERKOSTEN <span class="op">*</span> neu</span>
<span id="cb7-13"><a href="#cb7-13" aria-hidden="true" tabindex="-1"></a> rest, plan <span class="op">=</span> V(t <span class="op">+</span> <span class="dv">1</span>, neu)</span>
<span id="cb7-14"><a href="#cb7-14" aria-hidden="true" tabindex="-1"></a> <span class="cf">if</span> kosten <span class="op">+</span> rest <span class="op">&lt;</span> best[<span class="dv">0</span>]:</span>
<span id="cb7-15"><a href="#cb7-15" aria-hidden="true" tabindex="-1"></a> best <span class="op">=</span> (kosten <span class="op">+</span> rest, (p,) <span class="op">+</span> plan)</span>
<span id="cb7-16"><a href="#cb7-16" aria-hidden="true" tabindex="-1"></a> <span class="cf">return</span> best</span></code></pre></div>
<p><strong>Ergebnis:</strong> Plan <span class="math inline">(5, 0, 5, 0)</span> für <strong>110 €</strong>. Nachgerechnet stimmt der Betrag sogar — dieser Plan kostet tatsächlich 110 €. Das wahre Optimum lautet aber <span class="math inline">(3, 1, 4, 2)</span> und kostet <strong>70 €</strong>.</p>
<p><strong>Ihre Aufgabe:</strong> (a) Sehen Sie sich die Zeile mit den Rüstkosten an. Welche Information bräuchte sie, die im Zustand <code>(t, lager)</code> nicht enthalten ist? (b) Welchen Vorteil des Plans <span class="math inline">(3,1,4,2)</span> kann dieses Modell prinzipiell nicht erkennen? (c) Wie lautet der korrigierte Zustand, und um welchen Faktor wächst der Zustandsraum dadurch? (d) Warum ist es besonders tückisch, dass der ausgegebene Kostenbetrag <em>richtig</em> war?</p>
<p><em>Auflösung: <a href="anhang-loesungen.html#sec:loesungen-dynamische-programmierung">Abschnitt A.13</a>.</em></p>
</blockquote>
</div>
<blockquote>
<p><strong>🎯 Merksatz</strong> Der Zustand muss <strong>alles</strong> enthalten, was die Zukunft beeinflusst — und <strong>nichts</strong> darüber hinaus. Zu wenig, und das Modell löst ein anderes Problem, ohne es zu merken. Zu viel, und es wird unnötig groß (<a href="#sec:dynamische-programmierung-der-fluch-der-dimensionalitaet">Abschnitt 13.5</a>). Die Probe dafür ist ein einziger Satz: <em>„Wenn ich nur den Zustand kenne und nicht den Weg dorthin — kann ich dann noch optimal weiterentscheiden?“</em> Lautet die Antwort nein, fehlt etwas.</p>
</blockquote>
<hr />
<h2 id="sec:dynamische-programmierung-quiz">13.8 Micro-Quiz</h2>
<div class="card card-quiz">
<blockquote>
Sechster und siebter Fund: die letzten handgeschriebenen Nummern Der Loesungsanhang trug 98 Marken der Bauart "**9.2 — Ansatz waehlen.**", saemtlich aus Version 03, wo Unsicherheit Kapitel 9 war. Heute ist es Kapitel 12, und die Aufgabe wird korrekt als "Aufgabe 12.2" gesetzt. Das war keine Schoenheitsfrage: "Aufgabe 9.2" existiert wirklich - sie gehoert zum Kapitel Metaheuristiken ("Zuggroesse und Temperatur"). Wer die Loesung zu 9.2 nachschlug, landete beim falschen Thema. Beide bisherigen Pruefungen liefen vorbei: Vor der Zahl steht kein Schluesselwort, und es ist keine Tabellenzelle. Zwei Messungen machten die Reparatur billig. In allen 15 nummerierten Abschnitten gab es exakt so viele Loesungen wie Aufgaben, lueckenlos 1..n - nur der Kapitelteil war falsch. Und die acht in Phase 3 ergaenzten Kapitel machten es laengst richtig (**Titel.** ohne Nummer); der Anhang wurde also nicht auf etwas Neues umgestellt, sondern auf das, was seine neueren Teile schon taten. nummeriere_marken() vergibt die Marken jetzt selbst. Der Praefix kommt NICHT aus "# Anhang A:" - der Anhang ist eine Ueberschrift, seine Abschnitte gehoeren aber zu 23 verschiedenen Kapiteln. Er kommt aus {#sec:loesungen-X} -> {#kap:X}; diese Zuordnung gilt geprueft fuer alle 23. Im Quelltext steht "**{loesung} — Titel.**". Siebter Fund unterwegs: neun Verweise auf Denkfehler-Nummern. Saetze wie "siehe Denkfehler 8.1" standen in alter Zaehlung - "8" war in Version 03 das QP/NLP-Kapitel, heute ist es Graphen. Eine blosse Umnummerierung haette sie stillschweigend woanders hin zeigen lassen; das Ziel wurde deshalb fuer jeden einzelnen aus dem Zusammenhang bestimmt und auf {ref:sec:<kapitel>-denkfehler} umgestellt. Einer steckte in einem Programm-Docstring und bekam nach Regel 12 den Kapitelnamen statt eines Verweises. Eine bewusste Abweichung vom Plan: Der Denkfehler bekommt GAR KEINE Nummer. Es gibt je Kapitel genau einen, die zweite Stelle waere immer .1, und er steht ohnehin unter einer nummerierten Ueberschrift. Eine Nummer, die nie variiert, holte nur die Fragilitaet zurueck, die hier beseitigt wird. Auch das folgt den acht neuen Kapiteln, die ihren Denkfehler schon vorher nur mit dem Titel des Raetsels ankuendigen. --check bewacht jetzt alle Familien (Denkfehler N.M, Micro-Quiz N, **N.M — im Anhang) und zaehlt zusaetzlich ab, dass jeder Loesungsabschnitt so viele {loesung}-Marken hat wie sein Kapitel Aufgaben - das faengt eine vergessene oder doppelte Loesung, was keine Textsuche leisten kann. Gegengetestet mit vier kuenstlichen Fehlern auf einmal: alle vier gemeldet, nach dem Rueckbau null. Gegenprobe der Umstellung: Gesamtdokument vorher gesichert, nachher verglichen. 168 geaenderte Zeilen, restlos einer Familie zuzuordnen - 118 Loesungsmarken (138 gesamt minus 20, die in den Kapiteln 1 bis 3 zufaellig schon stimmten), 15 Anhang-Ueberschriften, 15 Kapitel-Callouts, 12 Micro-Quiz, 7 Zeilen mit Verweisen, 1 Docstring. Keine verschobene oder inhaltlich veraenderte Zeile. Ergebnis: 138 Aufgaben, 138 Loesungen, keine ohne Gegenstueck. Micro-Quiz tragen die echten Kapitelnummern. 293 Abschnitte, 720 Querverweise, 327 Indexmarken, PDF unveraendert 725 Seiten, 33 pytest-Tests, 67 netzfreie Programme fehlerfrei (die vier yfinance-Programme scheiterten am Rate-Limit des Anbieters, nicht an dieser Aenderung). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 09:52:41 +02:00
<p><strong>❓ Micro-Quiz 13: Drei Fragen zum Selbstcheck</strong></p>
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>
2026-09-08 01:20:09 +02:00
<p>Genau eine Antwort ist jeweils richtig. Auflösung in <a href="anhang-loesungen.html#quiz-loesung-dynamische-programmierung">Anhang A</a>.</p>
<p><strong>1. Warum rechnet dynamische Programmierung rückwärts?</strong> (a) Weil Rekursion in Python rückwärts effizienter ist. (b) Weil <span class="math inline">V_t</span> den Wert <span class="math inline">V_{t+1}</span> voraussetzt: Man kann eine Entscheidung erst bewerten, wenn man weiß, was sie für die Zukunft bedeutet. Am Ende ist dieser Wert bekannt — dort beginnt man. (c) Weil die Kosten in späteren Perioden höher sind.</p>
<p><strong>2. Was besagt das Bellmansche Optimalitätsprinzip?</strong> (a) Jede optimale Lösung besteht aus lauter einzeln optimalen Schritten — man kann also gierig vorgehen. (b) Ist ein Weg insgesamt optimal, so ist auch sein <strong>Reststück</strong> ab jedem Zwischenzustand optimal. Deshalb genügt es, je Zustand einen einzigen Wert zu speichern. (c) Bei genügend Rechenzeit findet man das Optimum immer.</p>
<p><strong>3. Ihr DP-Modell für eine Lagerplanung hat den Zustand „Lagerbestand“. Nun kommt eine Mengenrabattstaffel dazu, die sich nach der </strong>bisher im Jahr bestellten Gesamtmenge** richtet. Was folgt?** (a) Nichts — der Rabatt betrifft nur die Kosten, nicht den Zustand. (b) Die kumulierte Jahresmenge muss in den Zustand, sonst kann das Modell den Rabatt nicht korrekt zuordnen. Der Zustandsraum wird dadurch erheblich größer. (c) Man muss auf ein MILP wechseln, DP ist hier grundsätzlich ungeeignet.</p>
</blockquote>
</div>
<hr />
<h2 id="sec:dynamische-programmierung-selbsttest">13.9 Selbsttest</h2>
<blockquote>
<p>Antworten: <a href="anhang-loesungen.html#selbsttest-loesung-dynamische-programmierung">Anhang A</a>.</p>
</blockquote>
<ol type="1">
<li>Formulieren Sie das Optimalitätsprinzip in eigenen Worten.</li>
<li>Warum rechnet man bei DP rückwärts und nicht vorwärts?</li>
<li>Was muss ein Zustand enthalten — und woran erkennt man, dass er unvollständig ist?</li>
<li>Was besagt der Fluch der Dimensionalität, und welche drei Gegenmittel gibt es?</li>
<li>Warum ist eine analytische Vergleichslösung wertvoll, wenn man schon eine numerische hat?</li>
</ol>
<hr />
<h2 id="sec:dynamische-programmierung-zusammenfassung">13.10 Zusammenfassung</h2>
<ul>
<li><strong>Das Optimalitätsprinzip</strong> erlaubt es, ein mehrstufiges Problem in ineinandergreifende Einperiodenprobleme zu zerlegen.</li>
<li><strong>Rückwärtsinduktion</strong> startet am bekannten Ende und arbeitet sich nach vorn — dadurch ist <span class="math inline">V_{t+1}</span> immer schon bekannt, wenn <span class="math inline">V_t</span> berechnet wird.</li>
<li><strong>Die Zustandsdefinition ist die eigentliche Modellierungsleistung</strong>: vollständig, aber so knapp wie möglich. Die Probe dafür ist ein Satz: <em>„Wenn ich nur den Zustand kenne und nicht den Weg dorthin — kann ich dann noch richtig weiterentscheiden?“</em> Fehlt etwas, rechnet das Verfahren korrekt an einem anderen Problem, ohne es zu melden.</li>
<li><strong>Die Wertfunktion ist eine Nachschlagetabelle, kein Plan.</strong> Ihr praktischer Ertrag ist eine Regel für jeden Zustand — die auch dann noch gilt, wenn es anders kommt als gedacht.</li>
<li><strong>Gierige Vorwärtsregeln scheitern systematisch</strong>, weil sie die Restkosten nicht kennen: Im Schnellstart kostet der billigere erste Abschnitt am Ende 50 % mehr.</li>
<li><strong>Bei überproportionalen Kosten lohnt sich Stückelung</strong> — das ist der ökonomische Kern des Ausführungsproblems.</li>
<li><strong>Magische Konstanten sind ein Warnsignal.</strong> Jeder Parameter braucht eine Einheit und eine Interpretation, sonst ist das Modell nicht kalibrierbar.</li>
<li><strong>Der Fluch der Dimensionalität</strong> begrenzt DP auf wenige Zustandsdimensionen.</li>
</ul>
<p><strong>Ausblick.</strong> <a href="bruecke.html#teil-anwendungen">Teil IV</a> führt alles zusammen: Ab <a href="finanzdaten.html#kap-finanzdaten">Kapitel 18</a> arbeiten wir mit echten Marktdaten — und lernen zuerst, warum diese Daten trügerisch sind.</p>
</article>
<button type="button" class="fortschritt-knopf" data-kapitel="dynamische-programmierung.html"><svg class="icon" aria-hidden="true"><use href="#icon-check"></use></svg> <span>Als gelesen markieren</span></button>
<nav class="prev-next"><a class="prev-next-knopf prev-next-prev" href="unsicherheit.html"><svg class="icon" aria-hidden="true"><use href="#icon-chevron-left"></use></svg><span><small>Zurück</small>Kapitel 12: Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit</span></a><a class="prev-next-knopf prev-next-next" href="mehrziel.html"><span><small>Weiter</small>Kapitel 14: Mehrere Ziele — Pareto-Fronten statt Gewichte</span><svg class="icon" aria-hidden="true"><use href="#icon-chevron-right"></use></svg></a></nav>
</main>
</div>
<footer class="site-footer">
<p>© Dieter Schlüter · <a href="gesamtdokument.html">Gesamtdokument</a> ·
<a href="programme.html">Beispielprogramme</a></p>
</footer>
<script defer src="assets/search-index.js"></script>
<script defer src="assets/programme.js"></script>
<script defer src="assets/site.js"></script>
</body>
</html>