Files
AH-Oil-Trader/core/kontrolle.py
T
Axel HocksandClaude Opus 5 792355e8f9 Sieben Verbesserungen umgesetzt: Breaker scharf, B5 entschieden, 5 Werkzeuge
1) CIRCUIT-BREAKER SCHARF -- daily_loss_limit_pct 0 -> 8. Live verifiziert:
   enabled=True, limit_eur 89,44. Steht nicht in runtime_state.json, die ini
   greift direkt. Begruendung: MaxDD 82,8 % auf dem gemessenen IST-Pfad,
   schlechtester Einzeltrade -381 EUR.

2) B5-DEADLOCK RECHNERISCH AUFGELOEST -- die Regel ist ENTSCHIEDEN, nicht
   vertagt. Stand 11/20: Verhaeltnis 0,14 (Latte 1,0), PF 0,39 (Latte 1,0).
   Was die 9 Rest-Trades leisten muessten:
     9/0 Gewinner -> 165,72 EUR je Trade = 12,2x der bisherige Schnitt
     7/2          -> 186,17 EUR
     5/4          -> 222,99 EUR
   Der groesste Squeeze-Gewinn des Zeitraums war 77,34 EUR -- selbst der beste
   Fall verlangt 2,1x den Rekord, neunmal in Folge. Latte nicht mehr erreichbar.
   auto_squeeze bleibt FALSE. Wiederaufnahme waere eine NEUE Entscheidung mit
   NEUER, vorab fixierter Latte.

3) core/kontrolle.py -- Kontroll-Fixture mit den Referenzwerten. Bricht bewusst
   NICHT ab: ein Backtest darf abweichen, er muss es nur wissen und sagen.

4) core/live_config.py -- EINE Quelle der Live-Parameter (ini + runtime_state in
   der Vorrangfolge des Engine-Starts). Dritte Instanz derselben Fehlerklasse:
   ATRMIN_STD 0,12 statt 0,06, fehlendes set_entry_room, nachgebauter Cluster.

5) stichprobe.paarweise()/zeig_paarweise() -- gepaarter Vergleich. Anlass:
   trail_start sah sequentiell besser aus, gepaart kippte das Vorzeichen
   (Selektions- statt Exit-Effekt). Selbsttest in drei Richtungen bestanden.

6) tools/check_konstanten.py -- Waechter fuer veraltete Zahlen.
   Erster Lauf: zwei Befunde, BEIDE Fehler in meiner eigenen Ableitung (Margin
   aus Balance geschaetzt statt vom Broker gelesen; Elliott ohne
   Gueltigkeitsfenster gezaehlt). Beide korrigiert.
   Danach ein ECHTER Fund: meine am 13.08. notierten "~405 EUR/Lot" sind falsch,
   der exakte Broker-Wert ist 712,84 -- die urspruenglich notierten 670 lagen
   naeher als meine Korrektur. Lehre: eine abgeleitete Zahl ist keine gemessene.
   Stand jetzt 0 Befunde.

7) Puls-Waechter bedingungs-bewusst: PULS traegt je Zeile die Vorbedingung,
   bedingte Tabellen erscheinen als Hinweis statt als Ausfall. 0 Zeilen bleibt
   IMMER ein Befund (der rec_outcomes-Fall). Grund: ein Waechter mit
   Fehlalarmen wird ignoriert -- so ist der Divergenz-Waechter verstummt.

Pipeline gruen (85 Tests), Deployment ueber deploy.py --feld circuit_breaker
verifiziert, genau eine Instanz je Port.
Nicht angefasst: Sizing (User-Entscheidung) und die 44 Backtests mit eigener
Exit-Kopie (Migrations-Regel).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-17 00:57:38 +02:00

96 lines
4.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""KONTROLL-FIXTURE: ein Backtest muss die Referenz reproduzieren, BEVOR er berichtet.
⚠⚠ WARUM ES DAS GIBT (2026-08-13). Zwei an einem Tag gebaute Messungen konnten
den dokumentierten Squeeze-Referenzwert (+0,124/+0,292, `backtest_squeeze_
touchfill.py`) NICHT reproduzieren — sie kamen auf 0,151/0,079 bei einem
Drittel der Trades. Beide Ergebnisse mussten deshalb auf den **relativen**
Vergleich beschränkt werden; die absolute Aussage war nicht führbar. Und es
blieb offen, ob der dokumentierte Wert oder die neue Pipeline danebenliegt.
`backtest_verworfene_v2.py` macht es vorbildlich von Hand („✅ Die Kontrolle
reproduziert … Pipeline in Ordnung"). Hier ist dasselbe als geteilte Funktion,
damit es niemand vergisst.
from core.kontrolle import pruefe_squeeze
ok = pruefe_squeeze(h1_oer, h2_oer, n_h1, n_h2) # meldet und gibt bool
⚠ Es wird NICHT abgebrochen. Ein Backtest darf von der Referenz abweichen — er
muss es nur WISSEN und sagen. Ein harter Abbruch würde legitime Varianten
(andere Fill-Annahme, anderer Exit) unmöglich machen; eine stille Abweichung
dagegen erzeugt genau die Fehlschlüsse, die hier vermieden werden sollen.
⚠ Die Referenzen sind MESSWERTE, keine Wunschzahlen. Wer sie ändert, muss die
Quelle nennen — sonst wandert die Latte mit dem Ergebnis.
"""
from __future__ import annotations
from dataclasses import dataclass
@dataclass(frozen=True)
class Referenz:
name: str
quelle: str
h1: float
h2: float
n_h1: int
n_h2: int
toleranz: float = 0.06 # ØR-Abstand, ab dem gewarnt wird
# ── Die dokumentierten Referenzwerte des Projekts ────────────────────────────
SQUEEZE_TOUCHFILL = Referenz(
name="Squeeze · ruhende Order am Level, Fill bei Beruehrung",
quelle="backtest_squeeze_touchfill.py (2026-08-06)",
h1=+0.124, h2=+0.292, n_h1=1092, n_h2=1807)
SQUEEZE_CLOSEBRUCH = Referenz(
name="Squeeze · Fill bei Close-Bruch (die OPTIMISTISCHE Annahme)",
quelle="backtest_squeeze_entry.py (2026-08-05)",
h1=+0.456, h2=+0.611, n_h1=801, n_h2=1280)
SQUEEZE_MARKT = Referenz(
name="Squeeze · Markt-Einstieg am Bar-Close",
quelle="backtest_squeeze_entry.py (2026-08-05)",
h1=-0.158, h2=-0.006, n_h1=801, n_h2=1280)
def pruefe(ref: Referenz, h1: float, h2: float,
n_h1: int | None = None, n_h2: int | None = None,
still: bool = False) -> bool:
"""Reproduziert der Lauf die Referenz? Meldet den Befund, gibt bool zurueck."""
d1, d2 = abs(h1 - ref.h1), abs(h2 - ref.h2)
ok = d1 <= ref.toleranz and d2 <= ref.toleranz
if still:
return ok
print(f"\n ── KONTROLLE: {ref.name}")
print(f" Referenz {ref.quelle}")
print(f" erwartet H1 {ref.h1:+.3f} · H2 {ref.h2:+.3f}"
f" (n {ref.n_h1}/{ref.n_h2})")
n_txt = "" if n_h1 is None else f" (n {n_h1}/{n_h2})"
print(f" gemessen H1 {h1:+.3f} · H2 {h2:+.3f}{n_txt}")
print(f" Abstand H1 {d1:.3f} · H2 {d2:.3f} (Toleranz {ref.toleranz})")
if ok:
print(" ✅ reproduziert — die Pipeline ist in Ordnung, ABSOLUTWERTE "
"sind belastbar.")
else:
print(" ❌ WEICHT AB. Dann gilt:")
print(" · der RELATIVE Vergleich innerhalb dieses Laufs bleibt gueltig,")
print(" solange alle Varianten dieselbe Entry-Liste benutzen;")
print(" · die ABSOLUTWERTE sind es NICHT — sie duerfen nicht als")
print(" Ertragsaussage berichtet werden.")
if n_h1 is not None and (n_h1 < ref.n_h1 * 0.6 or n_h1 > ref.n_h1 * 1.6):
print(f" · ⚠ auch die Trade-ZAHL weicht stark ab ({n_h1} gegen "
f"{ref.n_h1}) — meist ein anderer Filter (ATR-Floor, Cooldown,")
print(" Slot-Behandlung), nicht der Markt.")
return ok
def pruefe_squeeze(h1: float, h2: float, n_h1=None, n_h2=None,
variante: str = "touchfill") -> bool:
"""Bequemer Aufruf fuer die haeufigste Kontrolle."""
ref = {"touchfill": SQUEEZE_TOUCHFILL,
"closebruch": SQUEEZE_CLOSEBRUCH,
"markt": SQUEEZE_MARKT}[variante]
return pruefe(ref, h1, h2, n_h1, n_h2)