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>
This commit is contained in:
Axel Hocks
2026-07-31 08:25:29 +02:00
co-authored by Claude Opus 5
parent 03be3b8f5f
commit d9cb54a52c
9 changed files with 508 additions and 21 deletions
+75 -2
View File
@@ -12,7 +12,13 @@
//| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). |
//+------------------------------------------------------------------+
#property copyright "Oil Trading Bot"
#property version "1.30"
#property version "1.31"
// v1.31 (2026-07-31): WAHRSCHEINLICHKEITS-KEGEL (CN;<minuten>;<lo>;<hi>) — zwei sich
// oeffnende, gepunktete Pfade vom aktuellen Bar ueber 30/60/120 min.
// ⚠ KEINE Richtungsprognose, sondern STREUUNG: wie weit der Kurs plausibel
// gelaufen sein kann. Out-of-sample kalibriert (analyze_cone.py): das
// 80%-Band trifft real ~76%. Bewusst NEUTRAL grau (keine Richtungsfarbe).
// InpShowCone / InpConeColor.
// v1.30 (2026-07-30): Konsolidierungs-Box BESCHRIFTET (User-Frage „was bedeutet die
// graue Box") — sichtbares Label an der Oberkante, rechtsbuendig wie die
// uebrigen: „Konsolidierung (10-Bar-Spanne)" bzw. bei Kompression
@@ -149,6 +155,8 @@ input bool InpShowConsol = true; // Konsolidierungs-Box (10 M5-Bar
input color InpConsolColor = C'88,166,255'; // Box-Farbe (blau)
input int InpConsolAlpha = 8; // Deckkraft in % (komprimiert: x2)
input int InpConsolBars = 10; // Box-Laenge in Bars (= _SQ_N im Bot)
input bool InpShowCone = true; // Wahrscheinlichkeits-Kegel (Streuung, NICHT Richtung)
input color InpConeColor = C'139,148,158'; // Kegel-Farbe (neutral grau - bewusst KEINE Richtungsfarbe)
input bool InpShowPnL = true; // G/V der offenen Position unter dem Kurs
input color InpPnLUpColor = C'0,255,127'; // G/V im Plus (neongruen)
input color InpPnLDownColor = C'255,69,0'; // G/V im Minus (rot)
@@ -172,6 +180,12 @@ input bool InpShowTradeLevels = true; // MT5-Positionslinien+Labels (st
const string PFX = "SRB_"; // Präfix S/R-Linien (aus Datei)
const string RPFX = "SRR_"; // Präfix dynamische Range (aus Chart)
string g_last = "\x01"; // letzter Datei-Inhalt (gegen Flackern)
// Kegel: Verkettung der Horizonte (30->60->120). Die CSV liefert sie aufsteigend;
// jeder Abschnitt startet dort, wo der vorherige endete -> ein sich oeffnender
// Faecher statt drei unverbundener Geraden. Wird je Redraw zurueckgesetzt.
datetime g_coneT = 0;
double g_coneLo = 0.0, g_coneHi = 0.0;
double g_conePx = 0.0; // aktueller Kurs (aus der PX-Zeile)
bool g_range_drawn = false; // hat DIESE Instanz die Range gezeichnet?
//+------------------------------------------------------------------+
@@ -394,6 +408,9 @@ void Redraw()
string rows[];
int nr = StringSplit(content, '\n', rows);
int idx = 0;
// Kegel-Verkettung je Redraw zuruecksetzen (sonst haengt der Faecher am Stand
// des vorherigen Durchlaufs)
g_coneT = 0; g_coneLo = 0.0; g_coneHi = 0.0; g_conePx = 0.0;
for(int r = 0; r < nr; r++)
{
@@ -650,13 +667,69 @@ void Redraw()
ObjectSetString(0, ctn, OBJPROP_TOOLTIP, ctip);
continue;
}
// WAHRSCHEINLICHKEITS-KEGEL: CN;<minuten>;<lo>;<hi>
// Zwei sich oeffnende Pfade vom aktuellen Bar aus. ⚠ Das ist KEINE Prognose
// der Richtung, sondern der STREUUNG: wie weit der Kurs in 30/60/120 min
// plausibel gelaufen sein kann. Out-of-sample kalibriert (analyze_cone.py):
// das 80%-Band trifft real ~76%. Bewusst NEUTRAL grau, kein gruen/rot.
if(typ == "CN")
{
if(k < 4) continue;
if(!InpShowCone) continue;
int mins = (int)StringToInteger(p[1]);
double clo = StringToDouble(p[2]);
double chi = StringToDouble(p[3]);
if(clo <= 0 || chi <= 0 || mins <= 0) continue;
// Anker: vom aktuellen Bar bis +mins Minuten (in Broker-Zeit)
datetime t0 = iTime(_Symbol, PERIOD_CURRENT, 0);
datetime t1 = t0 + (datetime)(mins * 60);
// Vorgaenger-Punkt: der naechstkleinere Horizont, sonst der aktuelle Kurs.
// g_coneT/g_coneLo/g_coneHi halten ihn zwischen den CSV-Zeilen fest; die
// Zeilen kommen aufsteigend sortiert vom Bot.
datetime pt = (g_coneT > 0 ? g_coneT : t0);
double plo = (g_coneT > 0 ? g_coneLo : g_conePx);
double phi = (g_coneT > 0 ? g_coneHi : g_conePx);
if(plo <= 0 || phi <= 0) { plo = clo; phi = chi; pt = t0; }
string ln = PFX + "CONE_L" + IntegerToString(mins);
string un = PFX + "CONE_U" + IntegerToString(mins);
string tip = "Streuung (keine Richtung): in " + IntegerToString(mins)
+ " min liegt der Kurs mit ~76% zwischen "
+ DoubleToString(clo, (int)_Digits) + " und "
+ DoubleToString(chi, (int)_Digits);
for(int s = 0; s < 2; s++)
{
string nm2 = (s == 0 ? ln : un);
double a2 = (s == 0 ? plo : phi);
double b2 = (s == 0 ? clo : chi);
if(ObjectCreate(0, nm2, OBJ_TREND, 0, pt, a2, t1, b2))
{
ObjectSetInteger(0, nm2, OBJPROP_COLOR, InpConeColor);
ObjectSetInteger(0, nm2, OBJPROP_STYLE, STYLE_DOT);
ObjectSetInteger(0, nm2, OBJPROP_WIDTH, 1);
ObjectSetInteger(0, nm2, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, nm2, OBJPROP_BACK, true);
ObjectSetInteger(0, nm2, OBJPROP_SELECTABLE, false);
}
else
{
ObjectMove(0, nm2, 0, pt, a2);
ObjectMove(0, nm2, 1, t1, b2);
}
ObjectSetString(0, nm2, OBJPROP_TOOLTIP, tip);
}
g_coneT = t1; g_coneLo = clo; g_coneHi = chi;
continue;
}
// AKTUELLER KURS oben rechts, fett (User-Wunsch): PX;bid
if(typ == "PX")
{
if(k < 2) continue;
if(!InpShowPrice) continue;
double pxv = StringToDouble(p[1]);
if(pxv <= 0) continue;
// Kegel-Startpunkt IMMER merken - auch wenn das Kurs-Label aus ist
// (sonst verliert der Faecher seinen Anker).
g_conePx = pxv;
if(!InpShowPrice) continue;
string pn = PFX + "PRICE";
if(ObjectFind(0, pn) < 0)
{