Files
AH-Oil-Trader/backtest_signal_touchfill.py
T
Axel HocksandClaude Opus 5 874ee16e00 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>
2026-08-06 23:21:52 +02:00

118 lines
5.1 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.
#!/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())