Eroeffnungsluecken (close_open) gemessen: auch kein Magnet
User-Frage "vielleicht will der Kurs erst die Kursluecke schliessen?" war berechtigt offen: der vorhandene Gap-Befund (backtest_gaps.py) beruht auf der vacuum-Definition, und core/gaps.py sagt ausdruecklich, dass nur die gemessen wurde. Die Klasse, die ein Mensch "Kursluecke" nennt (Schluss -> Eroeffnung, Wochenendluecke), war nie geprueft. 124 Luecken >= 0,50 $ ueber 4.359 D1-Bars, 2 Haelften, Bootstrap-KI. Fuellquote sieht stark aus (75-89 % in 10 Tagen) - die Kontrolle (gleiche Distanz, gleiche Bar, GEGENrichtung) liegt praktisch gleich auf, in H2 bei 3/5/10 Tagen exakt identisch. Alle 24 Differenz-KI enthalten die Null. Kontrolle B: an Nicht-Luecken-Tagen laeuft der Kurs dieselbe Strecke in 10 Tagen zu 73,5 % nach unten und 75,5 % nach oben - die ~75-80 % sind die Basisrate, keine Gap-Eigenschaft. Grosse Luecken >= 1,0 $ kippen zusaetzlich das Vorzeichen zwischen den Haelften (n=22/23). Dritte Bestaetigung derselben Lehre nach Vakuum-Gaps und Fibonacci: die Frage ist nie "faellt/haelt es?", sondern "haeufiger als eine willkuerliche Linie gleicher Distanz?". Keine Aenderung - close_open bleibt reine Anzeige, fliesst weiter NICHT in zone_lines(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
47c596ed70
commit
06ebb014be
@@ -3932,6 +3932,47 @@ dort bereits nachvalidiert, ØR +0,305.) **Eine** Oberfläche:
|
|||||||
**+0,014** — der Magnet zieht NICHT (das Gap blieb offen, weil der Trend von
|
**+0,014** — der Magnet zieht NICHT (das Gap blieb offen, weil der Trend von
|
||||||
ihm weglief). „Richtung Gap handeln" wäre schädlich; „weg" ist nur
|
ihm weglief). „Richtung Gap handeln" wäre schädlich; „weg" ist nur
|
||||||
Trendbestätigung (macht die Welle schon). Gaps bleiben reiner Kontext.
|
Trendbestätigung (macht die Welle schon). Gaps bleiben reiner Kontext.
|
||||||
|
- **⚠⚠ ERÖFFNUNGSLÜCKEN (`close_open`) NACHGEMESSEN = AUCH KEIN MAGNET
|
||||||
|
(`backtest_gaps_closeopen.py`, 2026-08-11, User: „vielleicht will der Kurs
|
||||||
|
erst die Kurslücke schliessen?").** Die Frage war **berechtigt offen**: der
|
||||||
|
Befund darüber beruht auf der **`vacuum`**-Definition, und `core/gaps.py`
|
||||||
|
sagt ausdrücklich, dass nur die gemessen wurde („wer `close_open` einbeziehen
|
||||||
|
will, misst vorher"). Die Klasse, die ein Mensch „Kurslücke" nennt
|
||||||
|
(Schluss → Eröffnung, typisch die Wochenendlücke), war **nie geprüft**.
|
||||||
|
124 Lücken ≥0,50 $ über 4.359 D1-Bars, 2 Hälften, 95-%-Bootstrap.
|
||||||
|
⚠⚠ **Die Füllquote sieht überzeugend aus — und ist nichtssagend:**
|
||||||
|
| Horizont | H1 Füllung / **Kontrolle** | H2 Füllung / **Kontrolle** |
|
||||||
|
|---|---|---|
|
||||||
|
| 3 Tage | 71,0 % / **69,4 %** | 74,2 % / **74,2 %** |
|
||||||
|
| 5 Tage | 80,6 % / **77,4 %** | 75,8 % / **75,8 %** |
|
||||||
|
| 10 Tage | 88,7 % / **83,9 %** | 80,6 % / **80,6 %** |
|
||||||
|
| 20 Tage | 90,3 % / **87,1 %** | 83,9 % / **87,1 %** ⚠ Kontrolle höher |
|
||||||
|
✅ **Die Kontrolle ist der ganze Befund: dieselbe Distanz, dieselbe Bar, aber
|
||||||
|
GEGENrichtung.** Damit sind Volatilität, Regime und Zeitraum automatisch
|
||||||
|
gleich — übrig bleibt nur die Frage, ob die Lücke eine RICHTUNG bevorzugt.
|
||||||
|
In H2 ist sie bei 3/5/10 Tagen **exakt identisch**. **Alle 24 Differenz-KI
|
||||||
|
enthalten die Null**, die vorab fixierte Regel ist in keiner Zelle erfüllt.
|
||||||
|
Kontrolle B bestätigt: an Nicht-Lücken-Tagen läuft der Kurs dieselbe Strecke
|
||||||
|
binnen 10 Tagen zu **73,5 % nach unten und 75,5 % nach oben** — die ~75–80 %
|
||||||
|
sind schlicht die Basisrate eines 1–2-$-Laufs in WTI, keine Eigenschaft der
|
||||||
|
Lücke. ⚠ **GROSSE Lücken ≥1,0 $ kippen zusätzlich das Vorzeichen** zwischen
|
||||||
|
den Hälften (H1 10/20 Tage: Kontrolle **schlägt** die Füllung 81,8/72,7 bzw.
|
||||||
|
86,4/72,7; H2 umgekehrt) — bei n=22/23 das klassische Rauschmuster.
|
||||||
|
⚠ **Fairerweise der einzige konsistente Punkt:** die **Ein-Tages**-Füllung
|
||||||
|
(„Lücke wird am selben Tag zugehandelt") ist in beiden Hälften UND beiden
|
||||||
|
Läufen richtungs-positiv (+4,9/+9,7 bzw. +4,6/+21,7 Pp). Zwei gleichgerichtete
|
||||||
|
Hälften sind mehr als eine — „KI enthält 0" heisst **nicht widerlegt, sondern
|
||||||
|
nicht belegt**. Für einen Eingriff reicht es bei n=22–62 nicht.
|
||||||
|
✅ **Dritte Bestätigung derselben Lehre**, nach Vakuum-Gaps und Fibonacci:
|
||||||
|
die Frage ist nie „füllt/hält es?", sondern **„häufiger als eine willkürliche
|
||||||
|
Linie gleicher Distanz?"**. Ohne diese Kontrolle misst man nur, dass Kurse
|
||||||
|
sich bewegen. **Keine Änderung** — `close_open` bleibt reine Anzeige und
|
||||||
|
fliesst weiterhin NICHT in `zone_lines()`.
|
||||||
|
⚠ **Der Anlassfall entschied sich noch während der Messung dagegen:** die
|
||||||
|
Lücke vom 10.08. (Füllung 77,383) war am 11.08. nicht geschlossen — der Kurs
|
||||||
|
stand bei **82,849**, also **5,47 $ in der Gegenrichtung**. Ein Einzelfall
|
||||||
|
belegt nichts, aber er illustriert den Mechanismus des Ursprungsbefunds: das
|
||||||
|
Gap blieb offen, **weil** der Trend von ihm weglief.
|
||||||
- **Tagesziele — Dashboard-Kachel ENTFERNT (2026-07-13):** Das Frontend zeigt die
|
- **Tagesziele — Dashboard-Kachel ENTFERNT (2026-07-13):** Das Frontend zeigt die
|
||||||
Web-Prognose nicht mehr. Das Modul `daily_levels` läuft **unverändert weiter**: die
|
Web-Prognose nicht mehr. Das Modul `daily_levels` läuft **unverändert weiter**: die
|
||||||
Level fließen als **S/R-Kandidaten** in `_sr_levels` (Anzeige/Close/Verdict-Kontext)
|
Level fließen als **S/R-Kandidaten** in `_sr_levels` (Anzeige/Close/Verdict-Kontext)
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Zieht eine EROEFFNUNGSLUECKE den Kurs an? — die nie gemessene Gap-Klasse.
|
||||||
|
|
||||||
|
ANLASS (2026-08-11, User: „wir haben ja eine Kursluecke. Vielleicht will der Kurs
|
||||||
|
erst diese schliessen?"). Am 10.08. oeffnete WTI bei 78,769 nach einem
|
||||||
|
Freitagsschluss von 77,383 — eine offene `close_open`-Luecke von 1,386 $, deren
|
||||||
|
Fuell-Level UNTER dem Kurs liegt. Die Frage ist also handelsrelevant: sie stuetzt
|
||||||
|
oder widerlegt eine offene SHORT-Position.
|
||||||
|
|
||||||
|
⚠⚠ WARUM DAS NICHT SCHON BEANTWORTET IST. Das Projekt hat den „Gap-Magneten"
|
||||||
|
gemessen und VERWORFEN (`backtest_gaps.py`: Signale WEG vom Gap +0,094 Ø-Edge,
|
||||||
|
ZUM Gap nur +0,014). Dieser Befund beruht aber auf der **`vacuum`**-Definition —
|
||||||
|
dem Vakuum zwischen zwei Tagesspannen. `core/gaps.py` sagt das ausdruecklich:
|
||||||
|
`zone_lines()` speist NUR `vacuum` in die Strategie, „denn nur auf ihnen wurde
|
||||||
|
gemessen … wer `close_open` einbeziehen will, misst vorher."
|
||||||
|
**Genau das passiert hier.** Die Klasse, die ein Mensch „Kursluecke" nennt
|
||||||
|
(Schluss → Eroeffnung, typisch die Wochenendluecke), ist ungemessen.
|
||||||
|
|
||||||
|
METHODE — und der Kontrolltest ist der entscheidende Teil:
|
||||||
|
· Luecken exakt wie `core/gaps.py` (kein Vakuum, |Schluss→Open| >= min).
|
||||||
|
· FUELLQUOTE: erreicht der Kurs binnen N Handelstagen den Vortags-Schluss?
|
||||||
|
· ⚠⚠ KONTROLLE (A) — **dieselbe Distanz, dieselbe Bar, GEGENrichtung.**
|
||||||
|
Fuer eine Aufwaerts-Luecke verlangt die Fuellung einen Ruecklauf um `size`.
|
||||||
|
Die Kontrolle fragt: laeuft der Kurs im selben Fenster `size` nach OBEN?
|
||||||
|
Damit sind Volatilitaet, Regime und Zeitraum automatisch gleich — es bleibt
|
||||||
|
nur die Frage, ob die Luecke eine RICHTUNG bevorzugt.
|
||||||
|
Ohne diesen Test misst man bloss, dass Kurse sich bewegen: „80 % gefuellt"
|
||||||
|
klingt stark und ist nichtssagend, wenn 80 % auch in die Gegenrichtung
|
||||||
|
laufen. Exakt die Lehre aus `backtest_fibonacci.py`, wo eine ZUFALLSLINIE
|
||||||
|
gleicher Distanz genauso gut hielt (55,7 % gegen 54,3 %).
|
||||||
|
· KONTROLLE (B) — Nicht-Luecken-Tage, gleiche Distanz, gleiche Richtung.
|
||||||
|
· 2 Stichproben (chronologische Haelften) + 95-%-Bootstrap je Zelle.
|
||||||
|
|
||||||
|
⚠⚠ ENTSCHEIDUNGSREGEL, VORAB FIXIERT: ein Magnet gilt nur als belegt, wenn die
|
||||||
|
Fuellquote die Kontrolle (A) in BEIDEN Haelften schlaegt UND das Bootstrap-
|
||||||
|
Intervall der Differenz die Null ausschliesst. Ein hoher Absolutwert allein ist
|
||||||
|
KEIN Beleg.
|
||||||
|
|
||||||
|
Aufruf: python backtest_gaps_closeopen.py [min_gap] [tage] (Default 0.5 / 10)
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
|
||||||
|
import MetaTrader5 as mt5
|
||||||
|
|
||||||
|
from core.stichprobe import anteil_ci
|
||||||
|
|
||||||
|
_MIN_CO = 0.50 # wie `[gaps] min_close_open` live
|
||||||
|
_HORIZONTE = (1, 2, 3, 5, 10, 20)
|
||||||
|
|
||||||
|
|
||||||
|
def hole_d1(n=5000):
|
||||||
|
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_D1, 0, n)
|
||||||
|
mt5.shutdown()
|
||||||
|
if bars is None:
|
||||||
|
raise SystemExit("keine D1-Bars")
|
||||||
|
return sym, [{"o": float(b["open"]), "h": float(b["high"]),
|
||||||
|
"l": float(b["low"]), "c": float(b["close"]),
|
||||||
|
"t": int(b["time"])} for b in bars]
|
||||||
|
|
||||||
|
|
||||||
|
def finde_luecken(rows, min_co):
|
||||||
|
"""Exakt die Bedingung aus `core/gaps.py`: KEIN Vakuum, |Schluss→Open| >= min."""
|
||||||
|
out = []
|
||||||
|
for i in range(1, len(rows)):
|
||||||
|
p, cur = rows[i - 1], rows[i]
|
||||||
|
if cur["l"] > p["h"] or cur["h"] < p["l"]:
|
||||||
|
continue # das ist ein Vakuum — andere Klasse
|
||||||
|
co = cur["o"] - p["c"]
|
||||||
|
if abs(co) < min_co:
|
||||||
|
continue
|
||||||
|
out.append({"i": i, "dir": 1 if co > 0 else -1,
|
||||||
|
"size": abs(co), "fill": p["c"], "open": cur["o"],
|
||||||
|
"t": cur["t"]})
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def erreicht(rows, i, ziel, richtung, tage):
|
||||||
|
"""Beruehrt der Kurs `ziel` binnen `tage` Bars? Der Luecken-Bar zaehlt MIT
|
||||||
|
(eine Eroeffnungsluecke wird oft am selben Tag zugehandelt — so macht es auch
|
||||||
|
`core/gaps.py`, waehrend ein Vakuum sich per Konstruktion nicht selbst
|
||||||
|
fuellen kann)."""
|
||||||
|
for r in rows[i:i + tage]:
|
||||||
|
if richtung < 0 and r["l"] <= ziel:
|
||||||
|
return True
|
||||||
|
if richtung > 0 and r["h"] >= ziel:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def zeile(lbl, treffer, n):
|
||||||
|
if n == 0:
|
||||||
|
return f" {lbl:<26} —"
|
||||||
|
a, lo, hi = anteil_ci(treffer, n)
|
||||||
|
ci = f"[{100*lo:.0f} … {100*hi:.0f} %]" if lo is not None else "—"
|
||||||
|
return f" {lbl:<26}{n:>5}{100*a:>8.1f} %{ci:>20}"
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
min_co = float(sys.argv[1]) if len(sys.argv) > 1 else _MIN_CO
|
||||||
|
tage = int(sys.argv[2]) if len(sys.argv) > 2 else 10
|
||||||
|
sym, rows = hole_d1()
|
||||||
|
gl = finde_luecken(rows, min_co)
|
||||||
|
mid = len(gl) // 2
|
||||||
|
|
||||||
|
print("=" * 96)
|
||||||
|
print(f" EROEFFNUNGSLUECKEN (`close_open`) — {sym}, {len(rows)} D1-Bars, "
|
||||||
|
f"{len(gl)} Luecken >= {min_co} $")
|
||||||
|
print(" Frage: fuellt sie sich HAEUFIGER, als der Kurs dieselbe Strecke")
|
||||||
|
print(" ohnehin in die GEGENrichtung laeuft?")
|
||||||
|
print("=" * 96)
|
||||||
|
|
||||||
|
for name, teil in (("H1 (aeltere Haelfte)", gl[:mid]), ("H2 (juengere)", gl[mid:])):
|
||||||
|
print(f"\n ── {name} — {len(teil)} Luecken ──")
|
||||||
|
print(f" {'':<26}{'n':>5}{'Quote':>10}{'95-%-KI':>20}")
|
||||||
|
for h in _HORIZONTE:
|
||||||
|
fu = [erreicht(rows, g["i"], g["fill"], -g["dir"], h) for g in teil]
|
||||||
|
# KONTROLLE A: gleiche Distanz, GEGENrichtung, dieselbe Bar
|
||||||
|
ko = [erreicht(rows, g["i"], g["open"] + g["dir"] * g["size"],
|
||||||
|
g["dir"], h) for g in teil]
|
||||||
|
nf, nk = sum(fu), sum(ko)
|
||||||
|
a1, l1, h1 = anteil_ci(fu, len(fu))
|
||||||
|
a2, _, _ = anteil_ci(ko, len(ko))
|
||||||
|
# Bootstrap der DIFFERENZ (gepaart — beide auf denselben Luecken)
|
||||||
|
r = random.Random(42); m = len(teil)
|
||||||
|
d = sorted((sum(fu[r.randrange(m)] for _ in range(m))
|
||||||
|
- sum(ko[r.randrange(m)] for _ in range(m))) / m
|
||||||
|
for _ in range(3000)) if m >= 10 else []
|
||||||
|
dci = f"[{100*d[75]:+.0f} … {100*d[2924]:+.0f} Pp]" if d else "—"
|
||||||
|
flag = "✅" if d and d[75] > 0 else ("❌" if d and d[2924] < 0 else "⚠")
|
||||||
|
print(f" {f'{h:>2} Tage Fuellung':<26}{len(fu):>5}{100*a1:>9.1f} %"
|
||||||
|
f"{f'[{100*l1:.0f} … {100*h1:.0f} %]':>20}"
|
||||||
|
f" Kontrolle {100*a2:>5.1f} % Diff {dci:>20} {flag}")
|
||||||
|
|
||||||
|
# ── KONTROLLE B: Nicht-Luecken-Tage, gleiche Distanz, gleiche Richtung ──
|
||||||
|
print(f"\n ── Kontrolle B: Nicht-Luecken-Tage ({tage} Tage) ──")
|
||||||
|
idx_gap = {g["i"] for g in gl}
|
||||||
|
r = random.Random(7)
|
||||||
|
proben = [i for i in range(1, len(rows) - tage) if i not in idx_gap]
|
||||||
|
r.shuffle(proben)
|
||||||
|
for lbl, richt in (("Ruecklauf nach UNTEN", -1), ("Lauf nach OBEN", 1)):
|
||||||
|
tr = []
|
||||||
|
for i in proben[:600]:
|
||||||
|
g = r.choice(gl)["size"] # gleiche Distanzverteilung
|
||||||
|
tr.append(erreicht(rows, i, rows[i]["o"] + richt * g, richt, tage))
|
||||||
|
print(zeile(lbl, tr, len(tr)))
|
||||||
|
|
||||||
|
print("\n " + "-" * 92)
|
||||||
|
print(" ⚠ REGEL (vorab): Magnet nur belegt, wenn die Fuellquote die Kontrolle A")
|
||||||
|
print(" in BEIDEN Haelften schlaegt UND das Differenz-KI die Null ausschliesst.")
|
||||||
|
print(" ⚠ Ein hoher Absolutwert allein ist KEIN Beleg — Kurse bewegen sich.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user