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
|
||||
ihm weglief). „Richtung Gap handeln" wäre schädlich; „weg" ist nur
|
||||
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
|
||||
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)
|
||||
|
||||
@@ -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