MQL5 v1.50: Knopf-Flackern - der Chart trug den Indikator DREIMAL

User: "beim Wechseln der Zeit blinken die Knoepfe kurz gross auf und sind dann
wieder klein". Nicht die Groesse war das Problem.

BELEG aus dem Terminal-Journal: je Zeitebenen-Wechsel DREI identische Zeilen
"custom indicator SR_Levels loaded succesfully" im selben Zehntelsekunden-
Fenster. Auf dem Chart lagen drei Instanzen. Jede hat ihren EIGENEN
gespeicherten Eingabesatz - die vor v1.49 angehaengten trugen noch 66/17/8,
die neue 132/34/16. Alle drei schrieben im Sekundentakt auf dieselben
Objektnamen, also wechselte die Groesse permanent hin und her.

Doppelte Instanzen sind von aussen unsichtbar, solange nichts schreibend
kollidiert - sie kosteten hier zusaetzlich das Dreifache an CSV-Lesevorgaengen
und Objekt-Auf-/Abbau je Sekunde. Merksatz: bei unerklaerlichem
Chart-Verhalten ZUERST die Ladezeilen im Journal zaehlen.

FIX 1: nur eine Instanz zeichnet die Knoepfe. Anspruch je Chart ueber eine
temporaere GlobalVariable, gesetzt mit GlobalVariableSetOnCondition - also
ATOMAR (ein Check-dann-Set haetten bei gleichzeitigem Start alle drei
gewonnen). Nicht-Inhaber steigen in BtnDraw() sofort aus und melden einmalig
im Experten-Log, dass eine zweite Instanz laeuft.
OnDeinit raeumt die Knoepfe nur noch als Inhaber weg und gibt den Anspruch
frei - sonst haette eine beendete Zweit-Instanz die Knoepfe der noch
laufenden ersten geloescht.

FIX 2: die Groesse heilt sich selbst. BtnDraw() legt einen Knopf neu an, wenn
seine gespeicherte XSIZE/YSIZE/FONTSIZE nicht mehr zum Soll passt; reines
Nachsetzen der Eigenschaften (v1.49) reichte in der Praxis nicht.

Der eigentliche Fix liegt beim Nutzer: doppelte Instanzen entfernen
(Rechtsklick > Indikatorenliste). Der Guard verhindert nur den sichtbaren
Schaden, nicht die dreifache Last.

0 errors 0 warnings, .ex5 frisch, Spiegel bitgleich.
This commit is contained in:
Axel Hocks
2026-08-22 13:24:25 +02:00
parent 4a6be29c8f
commit 7800b2cf05
2 changed files with 100 additions and 2 deletions
+43
View File
@@ -9661,3 +9661,46 @@ erreichbar schlägt schön, und der Normalfall ist eine Spalte.
`0 errors, 0 warnings`, `.ex5` frisch, Spiegel bitgleich. `0 errors, 0 warnings`, `.ex5` frisch, Spiegel bitgleich.
⚠ Wer noch größer will, dreht `InpBtnBreite`/`InpBtnHoehe`/`InpBtnFont` weiter — ⚠ Wer noch größer will, dreht `InpBtnBreite`/`InpBtnHoehe`/`InpBtnFont` weiter —
der Spalten-Rückfall fängt zu hohe Stapel ab. der Spalten-Rückfall fängt zu hohe Stapel ab.
### ⚠⚠ „Knöpfe blinken kurz groß auf und sind dann wieder klein" — der Chart trug den Indikator DREIMAL (2026-08-22, v1.50)
User-Meldung beim Zeitebenen-Wechsel. **Nicht die Größe war das Problem.**
Beleg aus dem Terminal-Journal (`<terminal>/logs/20260822.log`) — je Wechsel
**drei** identische Zeilen im selben Zehntelsekunden-Fenster:
```
13:19:16.071 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
13:19:16.072 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
13:19:16.072 custom indicator SR_Levels (SpotCrude,M5) loaded succesfully
```
**Auf dem Chart lagen DREI Instanzen.** Jede hat ihren **eigenen gespeicherten
Eingabesatz**: die vor v1.49 angehängten trugen noch `66/17/8`, die neue
`132/34/16`. Alle drei schrieben im Sekundentakt auf **dieselben** Objektnamen —
also wechselte die Größe permanent hin und her.
⚠⚠ **Das ist die Falle hinter dem Symptom: doppelte Instanzen sind von außen
unsichtbar**, solange nichts schreibend kollidiert. Sie kosteten hier ausserdem
das Dreifache an CSV-Lesevorgängen und Objekt-Auf-/Abbau je Sekunde.
**➜ Beim nächsten unerklärlichen Chart-Verhalten ZUERST die Ladezeilen im
Journal zählen.** Das kostet eine Minute und hätte hier drei Hypothesen erspart.
**Fix im Code: nur EINE Instanz zeichnet die Knöpfe.** Anspruch je Chart über
eine temporäre `GlobalVariable` (`SRB_BTNOWN_<ChartID>`), gesetzt mit
**`GlobalVariableSetOnCondition`** — also **atomar**. Ein Check-dann-Set hätten
bei praktisch gleichzeitigem Start alle drei gewonnen. Nicht-Inhaber steigen in
`BtnDraw()` sofort aus und melden **einmalig** im Experten-Log, dass eine zweite
Instanz läuft (sichtbar machen statt still schlucken).
**`OnDeinit` räumt die Knöpfe nur noch als Inhaber weg** und gibt den Anspruch
frei — sonst hätte eine beendete Zweit-Instanz die Knöpfe der noch laufenden
ersten gelöscht. Fällt der Inhaber weg, holt sich der nächste Timer-Durchlauf
einer anderen Instanz den Anspruch.
**Zweite Absicherung: die Größe heilt sich selbst.** `BtnDraw()` legt einen
Knopf **neu an**, wenn seine gespeicherte `XSIZE`/`YSIZE`/`FONTSIZE` nicht mehr
zum Soll passt. Reines Nachsetzen der Eigenschaften (v1.49) reichte in der
Praxis nicht.
**Der eigentliche Fix liegt beim Nutzer:** die doppelten Instanzen entfernen
(Rechtsklick auf den Chart → *Indikatorenliste* → alle `SR_Levels` bis auf eines
löschen). Der Code-Guard verhindert nur den sichtbaren Schaden, nicht die
dreifache Last.
`0 errors, 0 warnings`, `.ex5` frisch, Spiegel bitgleich.
+57 -2
View File
@@ -12,7 +12,7 @@
//| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). | //| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). |
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+
#property copyright "Oil Trading Bot" #property copyright "Oil Trading Bot"
#property version "1.49" #property version "1.50"
// v1.42 (2026-08-19): Y-Stapel oben rechts GEMESSEN statt geschaetzt. Der // v1.42 (2026-08-19): Y-Stapel oben rechts GEMESSEN statt geschaetzt. Der
// Faktor 1,8 aus v1.41 war nur die naechste Schaetzung; jetzt liefert // Faktor 1,8 aus v1.41 war nur die naechste Schaetzung; jetzt liefert
// TextGetSize die TATSAECHLICHE Pixelhoehe (LabelH). Ausserdem wird Y bei // TextGetSize die TATSAECHLICHE Pixelhoehe (LabelH). Ausserdem wird Y bei
@@ -334,6 +334,43 @@ bool g_bvis[BTN_N];
//| Sichtbar? Einzige Quelle fuer alle Zeichen-Zweige. //| Sichtbar? Einzige Quelle fuer alle Zeichen-Zweige.
bool Vis(int i) { return (i >= 0 && i < BTN_N) ? g_bvis[i] : true; } bool Vis(int i) { return (i >= 0 && i < BTN_N) ? g_bvis[i] : true; }
//+------------------------------------------------------------------+
//| ⚠⚠ NUR EINE INSTANZ DARF DIE KNOEPFE ZEICHNEN (v1.50). |
//| Real am 2026-08-22: der Chart trug SR_Levels DREIMAL (im Journal |
//| belegt: drei "loaded succesfully" je Zeitebenen-Wechsel). Jede |
//| Instanz hat ihren EIGENEN gespeicherten Eingabesatz — die vor |
//| v1.49 angehaengten trugen noch 66/17/8, die neue 132/34/16. Alle |
//| drei schrieben im Sekundentakt auf DIESELBEN Objektnamen, also |
//| wechselte die Groesse staendig hin und her: "blinken kurz gross |
//| auf und sind dann wieder klein". |
//| ⚠ Der Anspruch wird ATOMAR gesetzt (GlobalVariableSetOnCondition) |
//| — drei Instanzen starten praktisch gleichzeitig, ein |
//| Check-dann-Set haetten alle drei gewonnen. |
//| ⚠ Faellt der Inhaber weg (OnDeinit gibt frei), holt sich der |
//| naechste Timer-Durchlauf einer anderen Instanz den Anspruch. |
//+------------------------------------------------------------------+
double g_stamp = 0.0; // eigene Kennung (nie 0)
bool g_isowner = false;
bool g_warned = false;
string BtnOwnGV() { return "SRB_BTNOWN_" + IntegerToString(ChartID()); }
bool BtnClaim()
{
if(g_isowner) return true;
string gv = BtnOwnGV();
if(!GlobalVariableCheck(gv)) GlobalVariableTemp(gv); // legt mit 0 an
g_isowner = GlobalVariableSetOnCondition(gv, g_stamp, 0.0);
if(!g_isowner && !g_warned)
{
g_warned = true;
Print("SR_Levels: weitere Instanz auf diesem Chart — die Knoepfe "
"zeichnet nur die erste. Doppelte Instanzen bitte entfernen "
"(Rechtsklick > Indikatorenliste).");
}
return g_isowner;
}
string BtnGV(int i) { return "SRB_" + _Symbol + "_" string BtnGV(int i) { return "SRB_" + _Symbol + "_"
+ IntegerToString((int)_Period) + "_" + g_bkey[i]; } + IntegerToString((int)_Period) + "_" + g_bkey[i]; }
@@ -376,6 +413,7 @@ void BtnPaint(int i)
void BtnDraw() void BtnDraw()
{ {
if(!BtnClaim()) return; // eine andere Instanz zeichnet
if(!InpShowButtons) { ObjectsDeleteAll(0, BPFX); return; } if(!InpShowButtons) { ObjectsDeleteAll(0, BPFX); return; }
int h = MathMax(10, InpBtnHoehe); int h = MathMax(10, InpBtnHoehe);
// ⚠⚠ SPALTEN-RUECKFALL. Mit der 200-%-Groesse ist der Stapel 490 px hoch; // ⚠⚠ SPALTEN-RUECKFALL. Mit der 200-%-Groesse ist der Stapel 490 px hoch;
@@ -390,6 +428,14 @@ void BtnDraw()
for(int i = 0; i < BTN_N; i++) for(int i = 0; i < BTN_N; i++)
{ {
string nm = BPFX + g_bkey[i]; string nm = BPFX + g_bkey[i];
// ⚠ Auch NEU ANLEGEN, wenn die Groesse nicht mehr passt: ein von einer
// frueheren Fassung erzeugter Knopf behaelt sonst seine alten Masse.
// Reines Nachsetzen der Eigenschaften reichte in der Praxis nicht.
bool falsch = (ObjectFind(0, nm) >= 0)
&& ((int)ObjectGetInteger(0, nm, OBJPROP_XSIZE) != InpBtnBreite
|| (int)ObjectGetInteger(0, nm, OBJPROP_YSIZE) != h
|| (int)ObjectGetInteger(0, nm, OBJPROP_FONTSIZE) != InpBtnFont);
if(falsch) ObjectDelete(0, nm);
if(ObjectFind(0, nm) < 0) if(ObjectFind(0, nm) < 0)
{ {
if(!ObjectCreate(0, nm, OBJ_BUTTON, 0, 0, 0)) continue; if(!ObjectCreate(0, nm, OBJ_BUTTON, 0, 0, 0)) continue;
@@ -469,6 +515,9 @@ int LabelH(const string font, const int size)
int OnInit() int OnInit()
{ {
if(InpApplyTheme) ApplyTheme(); if(InpApplyTheme) ApplyTheme();
// Kennung: nie 0 (0 heisst "frei"), pro Instanz verschieden.
g_stamp = (double)(GetTickCount() % 1000000) + 1.0 + (double)(MathRand() % 997) / 1000.0;
BtnClaim();
BtnInit(); BtnInit();
EventSetTimer(MathMax(1, InpRefreshS)); EventSetTimer(MathMax(1, InpRefreshS));
Redraw(); Redraw();
@@ -590,7 +639,13 @@ void OnDeinit(const int reason)
EventKillTimer(); EventKillTimer();
ObjectsDeleteAll(0, PFX); ObjectsDeleteAll(0, PFX);
ObjectsDeleteAll(0, RPFX); ObjectsDeleteAll(0, RPFX);
ObjectsDeleteAll(0, BPFX); // ⚠ NUR der Inhaber raeumt die Knoepfe weg — sonst loescht eine
// beendete Zweit-Instanz die Knoepfe der noch laufenden ersten.
if(g_isowner)
{
ObjectsDeleteAll(0, BPFX);
GlobalVariableSet(BtnOwnGV(), 0.0); // Anspruch freigeben
}
ChartRedraw(); ChartRedraw();
} }
//+------------------------------------------------------------------+ //+------------------------------------------------------------------+