Bibliotheken geprueft: zwei Ertraege, keine neue Laufzeit-Abhaengigkeit
Nicht nach Katalog beantwortet, sondern gegen die dokumentierten Schwachstellen des Projekts - und beide Kandidaten wurden GEMESSEN, nicht empfohlen. ERTRAG 1 - BLOCK-BOOTSTRAP (core/stichprobe.block_ci95), braucht GAR KEINE Bibliothek. Die Luecke stand seit dem 07.08. woertlich in backtest_cost_gate: "fuer ueberlappende Beobachtungen zu optimistisch ... dafuer braeuchte es einen Block-Bootstrap". Behelf war entkoppeln(). Auf echten Daten (pbreak_predictions): naiv, alle Zeilen n=4702 Breite 0,028 <- 3x zu eng entkoppelt (bisher) n= 552 Breite 0,083 <- 88 % Datenverlust Block L=30 n=4702 Breite 0,081 <- gleiche Breite, ALLE Daten Und das Entkoppeln kostet nicht nur Genauigkeit, es VERSCHIEBT die Schaetzung (0,399 gegen 0,427) - es nimmt systematisch die ERSTE Beruehrung je Level+Stunde. KALIBRIERUNG GEMESSEN (AR(1), bekanntes Mittel, 200 Laeufe, Soll 95 %): rho naiv L=10 L=30 L=60 0,00 94 % 94 % 91 % 92 % 0,50 76 % 92 % 91 % 91 % 0,90 40 % 80 % 89 % 90 % 0,97 18 % 55 % 74 % 83 % Die naive Zeile ist der Befund: bei rho=0,97 behauptet sie 95 % Sicherheit und liegt in 18 % der Faelle richtig. Ehrlich zu den Grenzen: bei unabhaengigen Daten kostet der Block ~3 Punkte (dort bleibt ci95 besser), und bei rho=0,97 deckt auch er nur 83 % - eine Verbesserung, keine Garantie. ERTRAG 2 - hypothesis (dev-only), aufgenommen NACH einer Gegenprobe. Die Suite prueft bisher BEISPIELE, und die waehle ich selbst aus. Die teuersten Fehler sassen auf GRENZEN: room_info musste `frei` auf dem UNGERUNDETEN Abstand entscheiden, weil 0,5951 gerundet durchs 0,6-Gate rutscht. hypothesis findet genau diesen Fall (dist=0.59765625) in Sekunden. Erst danach aufgenommen. Neu tests/test_invarianten.py (4 Eigenschafts-Tests auf REINEN Funktionen), per importorskip - die Suite laeuft auch ohne. Beim ersten Lauf fielen zwei Tests, beide waren MEINE Fehler: cluster rundet auf 3 Stellen (Invariante zu streng) und zwei inhaltsgleiche dicts sind == (Reihenfolge muss ueber die Identitaet geprueft werden). Genau dafuer sind Property-Tests da. Mutationsprobe: Rundung verbogen -> 2 Tests fallen. BEGRUENDET ABGELEHNT: numba (Backtests 11 s/12k Bars = nicht der Engpass, und ein Rewrite verletzt die Bit-Gleichheits-Regel) · statsmodels/arch (jetzt 15 Zeilen stdlib mit eigener Kalibrierung) · polars/pyarrow (kein DataFrame- Workload) · loguru/structlog/rich (das Logformat ist forensisch tragend) · tenacity (Wiederholungen haben eigene Semantik, _mark_dead) · httpx (requests laeuft) · pandas_ta (Indikatoren sind validiert, ein Austausch verschoebe jede dokumentierte Zahl) · mypy (kaum Annotationen, ty deckt die Aritaet ab). requirements.txt (Laufzeit) bleibt UNVERAENDERT - auf einer frischen Maschine soll der Bot mit moeglichst kleiner Oberflaeche wieder laufen. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
0386497a79
commit
3391e04f32
@@ -81,3 +81,58 @@ def test_faengt_den_fehler_vom_07_08():
|
||||
assert roh - entr > 0.3
|
||||
# ...und die rohe Rate muss ausserhalb des Intervalls der entkoppelten liegen
|
||||
assert unterscheidbar([z[2] for z in ent], len(ent), roh) is True
|
||||
|
||||
|
||||
# ── Block-Bootstrap fuer abhaengige Beobachtungen (2026-08-20) ──────────────
|
||||
import random as _rnd
|
||||
|
||||
from core.stichprobe import autokorrelation, block_ci95
|
||||
|
||||
|
||||
def _ar1(n, rho, seed):
|
||||
"""AR(1)-Reihe mit Mittelwert 0 - so sehen ueberlappende Beobachtungen aus."""
|
||||
r = _rnd.Random(seed)
|
||||
out, v = [], 0.0
|
||||
for _ in range(n):
|
||||
v = rho * v + r.gauss(0, 1) * (1 - rho ** 2) ** 0.5
|
||||
out.append(v)
|
||||
return out
|
||||
|
||||
|
||||
def test_autokorrelation_erkennt_unabhaengig_und_geclustert():
|
||||
assert abs(autokorrelation(_ar1(3000, 0.0, 1))) < 0.10
|
||||
assert autokorrelation(_ar1(3000, 0.9, 2)) > 0.7
|
||||
|
||||
|
||||
def test_block_ci_ist_BREITER_als_das_naive_bei_abhaengigen_daten():
|
||||
"""Der ganze Zweck: bei Clusterung muss das Intervall breiter werden.
|
||||
|
||||
⚠ Genau das war die dokumentierte Luecke - `ci95` unterstellt unabhaengige
|
||||
Ziehungen und ist bei ueberlappenden Beobachtungen zu optimistisch.
|
||||
"""
|
||||
x = _ar1(2000, 0.9, 3)
|
||||
lo_b, hi_b, bl = block_ci95(x)
|
||||
assert bl > 2
|
||||
naiv = 2 * 1.96 * (sum((v - sum(x) / len(x)) ** 2 for v in x)
|
||||
/ len(x)) ** 0.5 / len(x) ** 0.5
|
||||
assert (hi_b - lo_b) > 1.5 * naiv, (
|
||||
"Block-KI ist nicht breiter als das naive - die Abhaengigkeit wird "
|
||||
"nicht erfasst")
|
||||
|
||||
|
||||
def test_block_ci_deckt_den_wahren_mittelwert_meistens():
|
||||
"""Kalibrierung: bei AR(1) mit Mittel 0 muss die 0 meistens drin liegen.
|
||||
|
||||
⚠ Bewusst 'meistens' und nicht 'immer': gemessen deckt der Block-Bootstrap
|
||||
bei rho=0,9 rund 90 % (das naive Verfahren nur 40 %). Er ist eine
|
||||
Verbesserung, keine Garantie - und der Test haelt genau das fest.
|
||||
"""
|
||||
treffer = sum(1 for s in range(40)
|
||||
if (lambda r: r[0] <= 0 <= r[1])(block_ci95(_ar1(800, 0.9, s),
|
||||
wiederholungen=400)))
|
||||
assert treffer >= 30, f"nur {treffer}/40 Ueberdeckung"
|
||||
|
||||
|
||||
def test_block_ci_lehnt_zu_kleine_stichproben_ab():
|
||||
lo, hi, bl = block_ci95([1.0, 2.0, 3.0])
|
||||
assert lo is None and hi is None and bl == 0
|
||||
|
||||
Reference in New Issue
Block a user