Gesamtempfehlung: Selbst-Kalibrierung, voller Ausrichtungs-Split, Block-Gruende,

Elliott entmachtet (v=144)

Vier Verbesserungen, alle aus dem Gemessenen abgeleitet — kein neues Signal.
Ausgangspunkt: die Karte hat genau eine belegte Funktion, das Veto; als Prognose
ist sie ein Muenzwurf (48-51 % ueber 1.071 Episoden).

(1) SELBST-KALIBRIERUNG. Neue Tabelle rec_outcomes: eine Zeile je EPISODE
    (Richtungswechsel, nicht je Minute), Auswertung ~alle 5 min gegen candles_m1
    nach 30/60 min, Snapshot rec_track, Zeile #vd-selfcal. Gegen 50 % zu lesen.
    Warum der groesste Hebel: die Qualitaet war bis heute unsichtbar und musste
    auf Nachfrage rueckwirkend gemessen werden. Dasselbe Muster
    (pbreak_predictions) hat den Ausfall des P(break)-Modells LIVE aufgedeckt.
    Mit 4 Szenarien getestet (tests_rec_outcomes.py). Zwei Fehlschlaege dabei
    waren Testfehler, keine Code-Fehler — dokumentiert.

(2) alignment_stats auf die VOLLE Historie (1.216 statt 40 Trades) und
    zusaetzlich je Lot. Absolute Euro-Vergleiche sind bei wechselnder
    Positionsgroesse ungueltig (Lehre 04.08.). Damit steht die einzige
    beidhaelftig robuste Aussage der Karte auf ihrer vollen Stichprobe.

(3) Block-Gruende sichtbar (block_mix -> #vd-blocks): "heute X % ohne Block ·
    entry_room … · min_conf …". Bisher nur per DB-Abfrage zu beantworten.

(4) ELLIOTT: Verdict-Gewicht 1,0 -> 0. Gemessen ueber 15.544 Zeilen hatte es mit
    1.384 Episoden die groesste Stichprobe der Tabelle und ist darin flach
    (-0,075/+0,032, 50 % Treffer) — bei 25,9 % Einfluss auf die Nadel, weil es
    in nur 1 % der Zeilen schweigt. Chip bleibt, Stimme entfaellt.
    ⚠ Folge: die Nadel schlaegt staerker aus (real +0,33 -> +1,00).

⚠ Namenskollision beim Bau gefunden und behoben: die neue Klasse hiess zuerst
.vd-track — so heisst bereits die Schiene der Bias-Nadel; sie waere
ueberschrieben worden. Jetzt .vd-selfcal.

Live verifiziert: alignment je Lot, block_mix (93 von 1381 ohne Block),
rec_outcomes angelegt, Elliott weight=0 bei erhaltenem Chip, v=144 ausgeliefert,
eine Instanz auf Port 8000.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Axel Hocks
2026-08-06 23:21:52 +02:00
co-authored by Claude Opus 5
parent 4f85d2ce75
commit 874ee16e00
9 changed files with 705 additions and 48 deletions
+117
View File
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""Haelt der SIG-Pending-Pfad auch mit realistischem Fill? — die offene Zahl.
ANLASS (2026-08-06): `signal_pending_entry` laeuft seit dem 05.08. mit ECHTEM
Geld. Die Erwartung dahinter (ØR +0,39/+0,36) stammt aus
`backtest_auto_signal_v3.py` und unterstellt: Einstieg zum Level-Preis, aber
**nur dort, wo der Bar-CLOSE das Level gebrochen hat**. Eine liegende Stop-Order
weiss das nicht — sie fuellt bei der ERSTEN BERUEHRUNG, auch bei jeder, die
wieder zurueckfaellt.
Wie teuer dieser Unterschied ist, wurde heute zweimal gemessen:
Momentum +0,40 → 0,06 (0,45 R Aufschlag) — Level WANDERT
Squeeze +0,46 → +0,13 (0,32 R Aufschlag) — Level STEHT
Der Squeeze haelt, das Momentum nicht. **Und `_pend` gehoert zur wandernden
Sorte:** der Anker wird bei jeder k×ATR-Gegenbewegung, nach Timeout und bei
Richtungswechsel neu gesetzt. Es ist also mit dem schlechteren Fall zu rechnen.
Benutzt dieselbe `lauf()` wie v3, nur mit `fill="touch"` — nichts nachgebaut,
damit die Zahlen vergleichbar bleiben. Der Default `fill="close"` reproduziert
v3 bitgenau (verifiziert).
VORAB FIXIERTE ENTSCHEIDUNGSREGEL:
`signal_pending_entry` bleibt nur gerechtfertigt, wenn der Pfad mit
`fill="touch"` in BEIDEN Haelften ØR > 0 UND PF > 1 haelt UND besser ist als
der Markt-Einstieg. Sonst gehoert er abgeschaltet.
Aufruf: python backtest_signal_touchfill.py [bars]
"""
import sys
import MetaTrader5 as mt5
import backtest_auto_signal_v3 as V
from core.wave_rec import WaveRecommender, calc_trend_angle
def main():
n_bars = int(sys.argv[1]) if len(sys.argv) > 1 else 80000
mt5.initialize()
sym = next((c for c in ("SpotCrude", "USOIL", "WTI", "XTIUSD")
if mt5.symbol_info(c)), None)
bars = mt5.copy_rates_from_pos(sym, mt5.TIMEFRAME_M5, 0, n_bars)
point = mt5.symbol_info(sym).point
mt5.shutdown()
# ⚠ Aufbau 1:1 wie in v3.main() — aber ueber die IMPORTIERTEN Helfer, nicht
# ueber Kopien. Waere hier ein eigener EMA/ATR drin, waere der Vergleich
# zwischen den beiden Fill-Annahmen nicht mehr sauber.
H = [float(b["high"]) for b in bars]; L = [float(b["low"]) for b in bars]
C = [float(b["close"]) for b in bars]; T = [int(b["time"]) for b in bars]
SP = [float(b["spread"]) * point for b in bars]
EF = V._ema_series(C, V._EMA_FAST); ES = V._ema_series(C, V._EMA_SLOW)
A = V._atr_series(H, L, C)
AD = [0.0] * len(C)
for i in range(V._N_BARS, len(C)):
AD[i] = calc_trend_angle(C[i - V._N_BARS:i + 1], V._ANGLE_LR) - 90.0
if abs(AD[i]) < V._ANGLE_DEAD:
AD[i] = 0.0
HTF = [V._htf_sign(C, i, 6, A[i] or 0.1) for i in range(len(C))]
H1S = [V._htf_sign(C, i, 12, A[i] or 0.1) for i in range(len(C))]
PH = [j for j in range(V._PIV_K, len(C) - V._PIV_K)
if H[j] == max(H[j - V._PIV_K:j + V._PIV_K + 1])]
PL = [j for j in range(V._PIV_K, len(C) - V._PIV_K)
if L[j] == min(L[j - V._PIV_K:j + V._PIV_K + 1])]
w = WaveRecommender(lambda: None)
w.set_reversal_enabled(False)
w.set_entry_room(0.6) # ⚠ ohne das ist das Raum-Gate ein No-op
w.set_dead_hours("")
mid = len(C) // 2
print("=" * 104)
print(f" SIG-PENDING — Fill bei BAR-CLOSE gegen Fill bei BERUEHRUNG "
f"({sym}, {len(C)} M5-Bars)")
print("=" * 104)
print(f" {V._berlin(T[0]):%d.%m.%Y} bis {V._berlin(T[-1]):%d.%m.%Y} · "
f"voller Live-Gate-Stack · kanonischer Exit · Echtkosten · EIN Slot")
print()
def paar(mc, am_level, fill):
r1, _ = V.lauf(w, H, L, C, T, A, SP, EF, ES, AD, HTF, H1S, PH, PL,
0, mid, mc, V._NACHT_LIVE, am_level, 0.0, fill)
r2, _ = V.lauf(w, H, L, C, T, A, SP, EF, ES, AD, HTF, H1S, PH, PL,
mid, len(C), mc, V._NACHT_LIVE, am_level, 0.0, fill)
return V.kenn(r1), V.kenn(r2)
print(f" {'Variante':<38}{'H1 n WR OeR PF SigmaR':>33}"
f" |{'H2 n WR OeR PF SigmaR':>33}")
print(" " + "-" * 102)
print(" A) MARKT am Bar-Close (der Ist-Zustand vor dem 05.08.)")
for mc in (55, 65, 75):
V.zeile(f" Konf>={mc}", *paar(mc, False, "close"))
print(" " + "-" * 102)
print(" B) am Level, Fill nur bei CLOSE-Bruch (die Zahl, auf der live steht)")
for mc in (55, 65, 75):
V.zeile(f" Konf>={mc}", *paar(mc, True, "close"))
print(" " + "-" * 102)
print(" C) am Level, Fill bei BERUEHRUNG (was live wirklich passiert)")
haelt = []
for mc in (55, 65, 75):
k1, k2 = paar(mc, True, "touch")
if V.zeile(f" Konf>={mc}", k1, k2):
haelt.append(mc)
print()
print("=" * 104)
if haelt:
print(f" → Variante C haelt bei Konf {haelt} — `signal_pending_entry` ist gedeckt.")
else:
print(" → Variante C haelt in KEINER Konfidenz-Stufe.")
print(" Damit steht `signal_pending_entry=true` auf einer Zahl, die die")
print(" realistische Fill-Annahme nicht ueberlebt → abschalten.")
print(" ⚠ B gegen C gelesen = der Look-ahead-Aufschlag (Squeeze 0,32 R,")
print(" Momentum 0,45 R).")
return 0
if __name__ == "__main__":
sys.exit(main())