Files
AH-Oil-Trader/core/cone.py
T
Axel HocksandClaude Opus 5 d9cb54a52c C: Wahrscheinlichkeits-Kegel (kalibriert) + Config-Waechter im Reminder
C - core/cone.py + Kachel #card-cone + MT5 v1.31 (CN;<min>;<lo>;<hi>):
"Erwartete Spanne" statt "Kursvorhersage". Der User wollte eingezeichnete
Verlaufspfade; bewusst NICHT gebaut - sie suggerieren eine Praezision, die
es nicht gibt (Lehre aus analyze_verdict_calibration.py). Der Kegel sagt
wie WEIT der Kurs in 30/60/120 min plausibel laeuft, nicht wohin.

analyze_cone.py: r(h)=(C[i+h]-C[i])/ATR[i], Fit auf H1, Abdeckung gemessen
auf H2 (echtes out-of-sample):
  80%-Band -> 76,7 / 76,6 / 75,3 %
  90%-Band -> 87,5 / 87,2 / 86,5 %
Konsistent ~3-5 Pp ZU ENG. Das wird NICHT weggefittet - Kachel und Tooltip
nennen die REAL gemessene Abdeckung, nicht den Nennwert. Damit nach dem
P(break)-Modell die zweite kalibriert geprueft Komponente im Projekt.

Zentriert auf den aktuellen Kurs, KEINE Drift addiert (Median +-0,0..0,16
xATR = klein gegen die Bandbreite; eine Drift-Korrektur waere eine
Richtungsaussage und die ist nicht belegt). Farbe neutral grau, kein
gruen/rot. Kein Signal, kein Verdict-Gewicht.

MQL5 v1.31: zwei sich oeffnende gepunktete Pfade, verkettet 30->60->120.
Startpunkt aus der PX-Zeile - die setzt g_conePx jetzt auch, wenn
InpShowPrice aus ist (sonst verliert der Faecher seinen Anker).

Config-Waechter in measurement_reminder.py (Antwort auf "sollten wir alle
verworfenen Backtests woechentlich neu rechnen?"): NEIN - eine Woche sind
~2000 Bars, und 18 Ideen x 52 Wochen = 936 Tests/Jahr erzeugen bei 5 %
Fehlalarmquote ~47 falsche "funktioniert jetzt!" pro Jahr. Stattdessen
EREIGNIS-gesteuert: CONFIG_DEPS verankert 8 config-abhaengige Messungen an
den Wert, gegen den sie validiert wurden (entry_room_atr 0,6 /
sr_close_pbreak 0,55 / breakout_k 0,3 / risk_pct 0 / margin_buffer_pct 95 /
adverse_15min_atr 0,5 / auto_signal_min_conf 75 / dead_hours leer). Weicht
einer ab, meldet der Timer welche Messung veraltet ist. Numerischer
Vergleich, damit 0.60 == 0.6. Mit 5 synthetischen Szenarien getestet.

Erledigt ausgetragen: Chartmuster-Kontrolltest (30.07., Muster schlagen die
Kontrolle in beiden Haelften +0,022/+0,108).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 08:25:29 +02:00

69 lines
3.0 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.
"""core/cone.py — Wahrscheinlichkeits-Kegel (Streuung, NICHT Richtung).
User-Wunsch 2026-07-31: „ein Kursvorhersagemodul, das mögliche Verläufe in MT5
einzeichnet". Eingezeichnete PFADE würden eine Präzision suggerieren, die es nicht
gibt — das Projekt hat mit `analyze_verdict_calibration.py` teuer gelernt, dass eine
Zahl ohne Kalibrierungsprüfung in die Irre führt (conf_pct war in H2 invertiert).
Deshalb bewusst WENIGER, dafür belegt: der Kegel sagt nicht, WOHIN der Kurs geht,
sondern WIE WEIT er in 30/60/120 min plausibel gelaufen sein kann.
GEMESSEN & OUT-OF-SAMPLE VALIDIERT (`analyze_cone.py`, 80k M5-Bars, Fit auf H1,
Abdeckung gemessen auf H2):
Band 30 min 60 min 120 min
80 % nom. 76,7 % 76,6 % 75,3 %
90 % nom. 87,5 % 87,2 % 86,5 %
⚠ Die Bänder sind konsistent ~35 Prozentpunkte ZU ENG (H2 war relativ zum ATR
etwas volatiler als H1). Das wird NICHT weggefittet — die Anzeige nennt die real
gemessene Abdeckung, nicht den Nennwert. Damit ist der Kegel nach dem P(break)-
Modell die zweite kalibriert geprüfte Komponente im Projekt.
Quantile final auf allen 80k Bars gefittet (dasselbe Vorgehen wie beim P(break)-
Modell: erst out-of-sample validieren, dann für den Live-Einsatz auf der vollen
History fitten). ATR-normiert → über Vola-Regime hinweg gültig.
⚠ KEIN Signal, kein Verdict-Gewicht, kein Trade-Trigger. Der Kegel ist um den
AKTUELLEN Kurs zentriert (keine Drift addiert — der Median lag bei ±0,0…0,16×ATR,
also klein gegen die Bandbreite; eine Drift-Korrektur wäre eine Richtungsaussage
und genau die ist hier nicht belegt).
"""
from __future__ import annotations
# Horizont (M5-Bars) → {Nennband: (unteres, oberes Quantil in ×ATR)}
_CONE = {
6: {50: (-0.915, +0.940), 80: (-2.000, +1.962), 90: (-2.800, +2.713)},
12: {50: (-1.333, +1.346), 80: (-2.854, +2.833), 90: (-4.064, +4.000)},
24: {50: (-1.949, +2.000), 80: (-4.183, +4.177), 90: (-5.955, +5.938)},
}
# real gemessene Abdeckung (H2, out-of-sample) — das steht in der UI, nicht der Nennwert
_COVERAGE = {6: {50: 44.4, 80: 76.7, 90: 87.5},
12: {50: 45.1, 80: 76.6, 90: 87.2},
24: {50: 45.5, 80: 75.3, 90: 86.5}}
_ATRMIN = 0.06
MINUTES = {6: 30, 12: 60, 24: 120}
def build(price: float, atr: float, bands=(50, 80)) -> dict | None:
"""→ {'atr':…, 'levels':[{minutes, bars, band, lo, hi, coverage}, …]}
`price` = aktueller Kurs (Bid), `atr` = ATR der M5-Basis.
"""
if not price or not atr or atr <= 0:
return None
a = max(float(atr), _ATRMIN)
out = []
for bars in sorted(_CONE):
for band in bands:
q = _CONE[bars].get(band)
if not q:
continue
out.append({
"minutes": MINUTES[bars], "bars": bars, "band": band,
"lo": round(price + q[0] * a, 3),
"hi": round(price + q[1] * a, 3),
"coverage": _COVERAGE.get(bars, {}).get(band),
})
return {"atr": round(a, 4), "price": round(float(price), 3), "levels": out}