Echtes CME-Volumen (CL=F) gemessen: traegt nicht mehr als die CFD-Tick-Zaehlung
User-Idee eines Volumen-Spike-Alerts. Berechtigte Frage dahinter: das Projekt kennt nur tick_volume vom CFD - eine Zaehlung von Preisaenderungen bei einem Market-Maker-Broker, kein gehandeltes Volumen. Echtes NYMEX-Volumen waere die erste Datenquelle, die NICHT aus dem Preis abgeleitet ist. Beide Quellen auf DEMSELBEN Fenster gemessen (28.05.-07.08., 13.855 CME- gegen 14.178 CFD-Bars), gleiche Methode, Ueberschuss ueber die Drift, zwei Haelften. Bewusst nicht gegen den dokumentierten Wert gestellt - genau dieser Vergleich (kurzes Fenster gegen Langfrist-Mittel) war am 07.08. der Messfehler. Volumen CME H1/H2 CFD H1/H2 1,5-2,0x +0,116 / -0,203 -0,011 / +0,152 2,0-2,5x -0,221 / -0,282 -0,101 / +0,589 >=2,5x +0,102 / +0,096 -0,267 / -0,881 Trefferquote in ALLEN Zellen 44-53 % - Muenzwurf, in beiden Quellen. Kein monotoner Zusammenhang, Vorzeichen kippen. Die einzige beidhaelftig konsistente Zelle (CME >=2,5x) ist mit [-0,234 ... +0,428] bzw. [-0,265 ... +0,461] klar Rauschen. EHRLICHE GRENZE: meine CFD-Kontrolle reproduziert den alten Befund (-0,117/-0,204) NICHT - Populations-Unterschied, kein Widerspruch. backtest_candles.py mass bedingt auf ein Kerzen-MUSTER, hier laeuft es unbedingt ueber alle Kerzen. Der alte Befund gilt weiter fuer seine Population. 60 Tage = ein Regime (yfinance-Deckel), also nur ein Ausschluss-Urteil moeglich. Konsequenz: kein Volumen-Spike-Signal. Die Datenquelle funktioniert, sie traegt nur keine Richtungsinformation. Telegram-Kanaele als Eingabe (Bookmap/ATAS) ohne Messung verworfen: sie transportieren Screenshots. Ein Orderbuch-Zustand ist vorbei, sobald ihn jemand abfotografiert. Nebenbefund zum eingereichten Skript: das .iloc[0]-Muster ist KORREKT (MultiIndex-Spalten), nicht wie von mir zunaechst vermutet ein Fehler. Echte Maengel: kein Dedup, kein Handelszeit-Gate, voller Tagesabruf im 60-s-Takt. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
56a3fe31d8
commit
abd5dc0cb8
@@ -4942,6 +4942,53 @@ Modell schliesst keine Trades.
|
|||||||
„die Rangfolge ist kaputt" ist **nicht** belegt; belegt ist nur die
|
„die Rangfolge ist kaputt" ist **nicht** belegt; belegt ist nur die
|
||||||
Basisraten-/Kalibrierungslücke.
|
Basisraten-/Kalibrierungslücke.
|
||||||
|
|
||||||
|
## ⚠⚠ ECHTES CME-VOLUMEN (CL=F) TRÄGT NICHT MEHR ALS DIE CFD-TICK-ZÄHLUNG (2026-08-08)
|
||||||
|
|
||||||
|
Anlass: User-Idee eines Telegram-Alerts auf WTI-Volumen-Spikes (`CL=F` via
|
||||||
|
yfinance). Die berechtigte Frage dahinter: das Projekt kennt bisher nur
|
||||||
|
`tick_volume` vom CFD — eine Zählung von Preisänderungen bei einem
|
||||||
|
**Market-Maker**-Broker, kein gehandeltes Volumen. Echtes NYMEX-Kontraktvolumen
|
||||||
|
wäre die erste Datenquelle, die **nicht aus dem Preis abgeleitet** ist.
|
||||||
|
|
||||||
|
**Gemessen (`backtest_cme_volumen.py`): beide Quellen auf DEMSELBEN Fenster**
|
||||||
|
(28.05.–07.08.26, 13.855 CME- gegen 14.178 CFD-Bars), gleiche Methode,
|
||||||
|
Überschuss über die Drift, zwei Hälften.
|
||||||
|
⚠ Bewusst NICHT gegen den dokumentierten Wert −0,117/−0,204 gestellt — der
|
||||||
|
stammt aus einem anderen Zeitraum. Genau dieser Vergleich („kurzes Fenster gegen
|
||||||
|
Langfrist-Mittel") war am 07.08. der Messfehler, der elf Hypothesen ausgelöst hat.
|
||||||
|
| Volumen | CME H1 / H2 | CFD H1 / H2 |
|
||||||
|
|---|---|---|
|
||||||
|
| 1,5–2,0× | +0,116 / **−0,203** | −0,011 / +0,152 |
|
||||||
|
| 2,0–2,5× | −0,221 / −0,282 | −0,101 / **+0,589** |
|
||||||
|
| **≥2,5×** | **+0,102 / +0,096** | −0,267 / **−0,881** |
|
||||||
|
**Trefferquote in ALLEN Zellen 44–53 %** — Münzwurf, in beiden Quellen.
|
||||||
|
Kein monotoner Zusammenhang, und die Vorzeichen kippen zwischen den Hälften.
|
||||||
|
Die einzige beidhälftig konsistente Zelle (CME ≥2,5×) ist mit
|
||||||
|
**[−0,234 … +0,428] bzw. [−0,265 … +0,461]** klar Rauschen.
|
||||||
|
|
||||||
|
⚠⚠ **EHRLICHE GRENZE: meine CFD-Kontrolle REPRODUZIERT den alten Befund NICHT** —
|
||||||
|
und der Grund ist ein Populations-Unterschied, kein Widerspruch.
|
||||||
|
`backtest_candles.py` maß „2× Volumen" **bedingt auf ein Kerzen-MUSTER** (langer
|
||||||
|
Körper / Marubozu), hier läuft es unbedingt über alle Kerzen. Der alte Befund
|
||||||
|
ist damit weder bestätigt noch widerlegt; er gilt weiter für seine Population.
|
||||||
|
⚠ 60 Tage = EIN Regime (yfinance deckelt 5-min-Bars). Ein ✅ hieße hier „nicht
|
||||||
|
ausgeschlossen"; das ❌ ist belastbar.
|
||||||
|
|
||||||
|
✅ **Konsequenz: kein Volumen-Spike-SIGNAL.** Die Datenquelle funktioniert
|
||||||
|
(CL=F liefert echte Kontrakte, ~4 s je Abruf), sie trägt nur keine
|
||||||
|
Richtungsinformation. Ein Alert ist allenfalls als **Aufmerksamkeits-Hinweis**
|
||||||
|
vertretbar („hier passiert etwas"), ausdrücklich NICHT als Richtungsaussage —
|
||||||
|
und dann mit derselben Ehrlichkeit beschriftet wie die Kerzen-Anatomie-Karte.
|
||||||
|
⚠ **Telegram-KANÄLE als Eingabe (Bookmap/ATAS) = verworfen, ohne Messung:** sie
|
||||||
|
transportieren **Screenshots**. Ein Orderbuch-Zustand ist vorbei, sobald ihn
|
||||||
|
jemand abfotografiert. Dasselbe Argument wie bei der X-API — und der vorhandene
|
||||||
|
Textstrom (News-Sentiment) ist bereits gemessen nicht robust prädiktiv.
|
||||||
|
⚠ **Nebenbefund zum eingereichten Skript:** das `.iloc[0]`-Muster ist **korrekt**
|
||||||
|
(yfinance liefert MultiIndex-Spalten), nicht wie zunächst von mir vermutet ein
|
||||||
|
Fehler. Echte Mängel wären: kein Dedup (die „letzte geschlossene Kerze" bleibt
|
||||||
|
5 min stehen → 5 Alerts je Spike), kein Handelszeit-Gate, voller Tagesabruf im
|
||||||
|
60-s-Takt.
|
||||||
|
|
||||||
## ⚠⚠ VWAP-BÄNDER AUF M15 = VERWORFEN (2026-08-08) — die letzte ungetestete Zutat
|
## ⚠⚠ VWAP-BÄNDER AUF M15 = VERWORFEN (2026-08-08) — die letzte ungetestete Zutat
|
||||||
|
|
||||||
Anlass: User-Wunsch nach einem **M15-Daytrading-Modul** auf Profi-Methodik
|
Anlass: User-Wunsch nach einem **M15-Daytrading-Modul** auf Profi-Methodik
|
||||||
|
|||||||
@@ -0,0 +1,174 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Verhält sich ECHTES CME-Volumen anders als die CFD-Tick-Zählung?
|
||||||
|
|
||||||
|
ANLASS (2026-08-08, User-Idee „Volume-Spike-Alert auf CL=F"). Das Projekt hat
|
||||||
|
„2× Volumen = echt" schon gemessen (`backtest_candles.py`): Überschuss
|
||||||
|
**−0,117 / −0,204** — *monoton in die FALSCHE Richtung*. Mehr Volumen bedeutet
|
||||||
|
eine STÄRKERE Gegenbewegung; der Spike ist die Klimax-Kerze, und der Kurs läuft
|
||||||
|
danach GEGEN sie. Ein naiver „viel Volumen → mitgehen"-Alert stupst also
|
||||||
|
messbar falsch.
|
||||||
|
|
||||||
|
⚠ ABER: jene Messung lief auf `tick_volume` des **CFD** — das ist eine Zählung
|
||||||
|
von Preisänderungen, KEIN gehandeltes Volumen. Der Broker ist Market Maker; was
|
||||||
|
dort als „Volumen" erscheint, hat mit den Kontrakten an der NYMEX nur mittelbar
|
||||||
|
zu tun. Genau das ist die offene Frage: trägt ECHTES Börsenvolumen (CL=F, CME)
|
||||||
|
eine andere Information?
|
||||||
|
|
||||||
|
⚠⚠ DESHALB LAUFEN BEIDE QUELLEN AUF DEMSELBEN FENSTER UND MIT DERSELBEN METHODE.
|
||||||
|
Der dokumentierte Wert −0,117/−0,204 stammt aus einem anderen Zeitraum und von
|
||||||
|
anderen Bars — ihn direkt danebenzustellen wäre derselbe Fehler wie am 07.08.,
|
||||||
|
als eine 6-Tage-Live-Stichprobe gegen ein 208-Tage-Mittel gehalten wurde.
|
||||||
|
Verglichen wird nur CME gegen CFD, Zeitpunkt für Zeitpunkt.
|
||||||
|
|
||||||
|
METHODE (wie `backtest_candles.py`):
|
||||||
|
· Volumen-Verhältnis = V[i] / Ø(V[i−20 … i−1])
|
||||||
|
· Forward-Return über 12 Bars (60 min), in ×ATR, VORZEICHEN = Kerzenrichtung
|
||||||
|
· gelesen als ÜBERSCHUSS über die Drift des Zeitraums — sonst liest man in
|
||||||
|
einem Trendfenster die Drift als Edge (die Lehre von `analyze_tageszeit.py`)
|
||||||
|
· zwei Hälften des Fensters
|
||||||
|
|
||||||
|
⚠⚠ GRENZE, DIE VORAB ZU NENNEN IST: yfinance liefert für 5-min-Bars maximal
|
||||||
|
~60 Tage. Beide Hälften liegen damit im SELBEN Regime — genau die Konstellation,
|
||||||
|
an der ORB und die Dead-Hour-Stunde 13 gescheitert sind. **Ein ✅ heißt hier
|
||||||
|
„nicht ausgeschlossen", NICHT „bestätigt".** Ein ❌ ist dagegen belastbar.
|
||||||
|
|
||||||
|
Aufruf: python backtest_cme_volumen.py
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import datetime as dt
|
||||||
|
|
||||||
|
import MetaTrader5 as mt5
|
||||||
|
|
||||||
|
_FWD = 12 # 12 M5-Bars = 60 min
|
||||||
|
_LOOK = 20 # Fenster fuer das Durchschnittsvolumen
|
||||||
|
_BUCKETS = ((0, 1.5), (1.5, 2.0), (2.0, 2.5), (2.5, 99))
|
||||||
|
_BROKER_OFF = 10800 # Brokerzeit = UTC+3
|
||||||
|
|
||||||
|
|
||||||
|
def atr_series(H, L, C, p=14):
|
||||||
|
tr = [0.0]
|
||||||
|
for i in range(1, len(C)):
|
||||||
|
tr.append(max(H[i] - L[i], abs(H[i] - C[i - 1]), abs(L[i] - C[i - 1])))
|
||||||
|
out = [None]
|
||||||
|
for i in range(1, len(C)):
|
||||||
|
seg = tr[max(1, i - p + 1):i + 1]
|
||||||
|
out.append(sum(seg) / len(seg))
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def hole_cme():
|
||||||
|
"""CL=F 5-min von yfinance. ⚠ MultiIndex-Spalten sind hier NORMAL — das
|
||||||
|
`.iloc[0]`-Muster im User-Skript ist deshalb korrekt, kein Fehler."""
|
||||||
|
import yfinance as yf
|
||||||
|
df = yf.download("CL=F", interval="5m", period="60d",
|
||||||
|
progress=False, auto_adjust=False)
|
||||||
|
if df is None or df.empty:
|
||||||
|
return None
|
||||||
|
reihen = []
|
||||||
|
for ts, r in df.iterrows():
|
||||||
|
def f(sp):
|
||||||
|
v = r[sp]
|
||||||
|
return float(v.iloc[0] if hasattr(v, "iloc") else v)
|
||||||
|
try:
|
||||||
|
reihen.append((int(ts.timestamp()), f("High"), f("Low"),
|
||||||
|
f("Close"), f("Volume")))
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
return reihen
|
||||||
|
|
||||||
|
|
||||||
|
def hole_cfd(von_ts, bis_ts):
|
||||||
|
"""SpotCrude M5 aus MT5, auf dasselbe Zeitfenster beschnitten.
|
||||||
|
⚠ `time` ist Broker-Wallclock (UTC+3) — fuer den Abgleich in echte
|
||||||
|
UTC-Epoch umrechnen (die im Projekt mehrfach dokumentierte Falle)."""
|
||||||
|
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, 30000)
|
||||||
|
mt5.shutdown()
|
||||||
|
if bars is None:
|
||||||
|
return None, sym
|
||||||
|
out = []
|
||||||
|
for b in bars:
|
||||||
|
t = int(b["time"]) - _BROKER_OFF
|
||||||
|
if von_ts - 300 <= t <= bis_ts + 300:
|
||||||
|
out.append((t, float(b["high"]), float(b["low"]),
|
||||||
|
float(b["close"]), float(b["tick_volume"])))
|
||||||
|
return out, sym
|
||||||
|
|
||||||
|
|
||||||
|
def auswerten(reihen, name):
|
||||||
|
"""Ueberschuss ueber die Drift je Volumen-Bucket, in zwei Haelften."""
|
||||||
|
T = [r[0] for r in reihen]; H = [r[1] for r in reihen]
|
||||||
|
L = [r[2] for r in reihen]; C = [r[3] for r in reihen]
|
||||||
|
V = [r[4] for r in reihen]
|
||||||
|
AT = atr_series(H, L, C)
|
||||||
|
mid = len(C) // 2
|
||||||
|
print(f"\n ── {name} ── {len(C)} Bars, "
|
||||||
|
f"{dt.datetime.utcfromtimestamp(T[0]):%d.%m.} bis "
|
||||||
|
f"{dt.datetime.utcfromtimestamp(T[-1]):%d.%m.}")
|
||||||
|
|
||||||
|
for hn, lo, hi in (("H1", _LOOK + 15, mid), ("H2", mid, len(C) - _FWD - 1)):
|
||||||
|
# Drift des Fensters: mittlerer Forward-Return OHNE Richtungsvorzeichen
|
||||||
|
drift = []
|
||||||
|
for i in range(lo, hi):
|
||||||
|
a = AT[i]
|
||||||
|
if a and a > 0:
|
||||||
|
drift.append((C[i + _FWD] - C[i]) / a)
|
||||||
|
d_mean = sum(drift) / len(drift) if drift else 0.0
|
||||||
|
print(f" {hn} Drift des Fensters: {d_mean:+.3f}×ATR "
|
||||||
|
f"(darueber wird gelesen)")
|
||||||
|
print(f" {'Volumen':<14}{'n':>7}{'Ueberschuss':>14}{'Treffer':>10}")
|
||||||
|
for b_lo, b_hi in _BUCKETS:
|
||||||
|
werte = []
|
||||||
|
for i in range(lo, hi):
|
||||||
|
a = AT[i]
|
||||||
|
if not a or a <= 0:
|
||||||
|
continue
|
||||||
|
vs = V[max(0, i - _LOOK):i]
|
||||||
|
if len(vs) < _LOOK:
|
||||||
|
continue
|
||||||
|
m = sum(vs) / len(vs)
|
||||||
|
if m <= 0:
|
||||||
|
continue
|
||||||
|
q = V[i] / m
|
||||||
|
if not (b_lo <= q < b_hi):
|
||||||
|
continue
|
||||||
|
d = 1 if C[i] > (H[i] + L[i]) / 2 else -1 # Kerzenrichtung
|
||||||
|
r = (C[i + _FWD] - C[i]) * d / a
|
||||||
|
werte.append(r - d * d_mean) # Ueberschuss
|
||||||
|
if len(werte) < 25:
|
||||||
|
print(f" {f'{b_lo}–{b_hi}x':<14}{len(werte):>7}{'zu wenig':>14}")
|
||||||
|
continue
|
||||||
|
m = sum(werte) / len(werte)
|
||||||
|
tr = 100 * sum(1 for x in werte if x > 0) / len(werte)
|
||||||
|
print(f" {f'{b_lo}–{b_hi}x':<14}{len(werte):>7}{m:>+14.3f}{tr:>9.0f}%")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("=" * 84)
|
||||||
|
print(" ECHTES CME-VOLUMEN gegen CFD-TICK-ZAEHLUNG — gleiches Fenster, gleiche Methode")
|
||||||
|
print("=" * 84)
|
||||||
|
cme = hole_cme()
|
||||||
|
if not cme or len(cme) < 500:
|
||||||
|
print(f" CL=F lieferte zu wenig ({0 if not cme else len(cme)} Bars)"); return
|
||||||
|
cfd, sym = hole_cfd(cme[0][0], cme[-1][0])
|
||||||
|
if not cfd or len(cfd) < 500:
|
||||||
|
print(f" {sym} lieferte zu wenig ({0 if not cfd else len(cfd)} Bars)"); return
|
||||||
|
|
||||||
|
print(f"\n Fenster: {dt.datetime.utcfromtimestamp(cme[0][0]):%d.%m.%Y} bis "
|
||||||
|
f"{dt.datetime.utcfromtimestamp(cme[-1][0]):%d.%m.%Y} (UTC)")
|
||||||
|
print(f" CL=F (CME, echte Kontrakte): {len(cme)} Bars")
|
||||||
|
print(f" {sym} (CFD, tick_volume): {len(cfd)} Bars")
|
||||||
|
print("\n ⚠ ~60 Tage = EIN Regime. Ein ✅ heisst 'nicht ausgeschlossen',")
|
||||||
|
print(" NICHT 'bestaetigt'. Ein ❌ ist dagegen belastbar.")
|
||||||
|
print("\n Erwartung aus der Vormessung (CFD, anderer Zeitraum): der Ueberschuss")
|
||||||
|
print(" wird mit steigendem Volumen NEGATIVER (Klimax, Kurs laeuft dagegen).")
|
||||||
|
|
||||||
|
auswerten(cme, "CL=F — echtes CME-Kontraktvolumen")
|
||||||
|
auswerten(cfd, f"{sym} — CFD-tick_volume (Kontrolle)")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user