Szenario-Vergleich in der Monte-Carlo-Simulation + Sensitivitaetsanalyse (Tornado)
Deploy App / deploy (push) Successful in 53s

Monte Carlo ueber mehrere Szenarien eines Plans in einem Lauf:
- Annahmen nur EINMAL je logischem Element (Zuordnung ueber sourceElementId,
  dieselbe Kette wie beim Diff) -- sonst vergleicht man die Eingaben statt
  der Szenarien
- gemeinsamer Seed fuer alle Szenarien (Common Random Numbers), damit
  Unterschiede strukturell und nicht zufaellig sind
- Zielbetrag bleibt szenario-eigen: die Erfolgswahrscheinlichkeit misst,
  wie oft ein Szenario sein EIGENES Versprechen haelt
- Vergleichstabelle + Median-Linien; bei einem Szenario unveraenderter Faecher

Sensitivitaetsanalyse (Roadmap Nr. 20), eigener Dialog "Einflussfaktoren":
- neues reines Modul sensitivity.ts, One-at-a-time ueber 7 Treiber
- Bandbreiten je Treiber pflichtig und ohne Default (die Balkenlaenge haengt
  direkt davon ab)
- Pensionsalter bewusst ausgeschlossen: nicht variierbar ohne Mitverschieben
  der Phasengrenzen (Begruendung in 9.18)

SPEZIFIKATION auf 1.0: neue Kapitel 3.6.6, 4.12.6, 4.13, 9.18, 9.19 sowie
vier korrigierte Dokumentationsfehler (Kap. 1.2, 4.6.5, 5.2/5.3/5.5.2/8.1,
Glossar). 20 Tests ergaenzt (60 -> 80). Keine DB-Aenderung.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@
This commit is contained in:
2026-07-18 14:33:27 +02:00
parent d203e504c0
commit 3fe7a3978d
9 changed files with 1762 additions and 211 deletions
+17
View File
@@ -12,12 +12,14 @@ import {
Menu,
PiggyBank,
Plus,
Tornado,
Trash2,
X,
} from "lucide-react";
import { PlanView } from "@/components/PlanView";
import { Dashboard } from "@/components/Dashboard";
import { MonteCarloDialog } from "@/components/MonteCarloDialog";
import { SensitivityDialog } from "@/components/SensitivityDialog";
import { SpecView } from "@/components/SpecView";
import { ProfileMenu } from "@/components/ProfileMenu";
import { PlanProfileFields, emptyProfileDraft, type ProfileDraft } from "@/components/PlanProfileFields";
@@ -44,6 +46,7 @@ export function AppShell({ username }: { username: string }) {
const [showSpec, setShowSpec] = useState(false);
const [showCharts, setShowCharts] = useState(false);
const [showMonteCarlo, setShowMonteCarlo] = useState(false);
const [showSensitivity, setShowSensitivity] = useState(false);
const loadPlans = useCallback(async () => {
const data = await api.get<{ plans: PlanListItem[] }>("/api/plans");
@@ -290,6 +293,14 @@ export function AppShell({ username }: { username: string }) {
<Dices className="h-4 w-4" />
Monte-Carlo-Simulation
</button>
<button
type="button"
onClick={() => setShowSensitivity(true)}
className="flex items-center gap-1.5 rounded-lg border border-border px-3 py-1.5 text-sm font-medium text-muted hover:bg-surface-2"
>
<Tornado className="h-4 w-4" />
Einflussfaktoren berechnen
</button>
</>
)}
{diff && detail.base && (
@@ -337,10 +348,16 @@ export function AppShell({ username }: { username: string }) {
<MonteCarloDialog
plan={detail.plan}
computed={detail.computed}
meta={detail.meta}
scenarios={activePlan?.scenarios ?? [detail.meta]}
onClose={() => setShowMonteCarlo(false)}
/>
)}
{showSensitivity && detail && (
<SensitivityDialog plan={detail.plan} onClose={() => setShowSensitivity(false)} />
)}
{copyFrom && (
<CopyScenarioDialog
source={copyFrom}