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
+57 -2
View File
@@ -12,7 +12,7 @@
//| 4) Der Bot muss laufen (schreibt die CSV alle ~5 s). |
//+------------------------------------------------------------------+
#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
// Faktor 1,8 aus v1.41 war nur die naechste Schaetzung; jetzt liefert
// 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.
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 + "_"
+ IntegerToString((int)_Period) + "_" + g_bkey[i]; }
@@ -376,6 +413,7 @@ void BtnPaint(int i)
void BtnDraw()
{
if(!BtnClaim()) return; // eine andere Instanz zeichnet
if(!InpShowButtons) { ObjectsDeleteAll(0, BPFX); return; }
int h = MathMax(10, InpBtnHoehe);
// ⚠⚠ 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++)
{
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(!ObjectCreate(0, nm, OBJ_BUTTON, 0, 0, 0)) continue;
@@ -469,6 +515,9 @@ int LabelH(const string font, const int size)
int OnInit()
{
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();
EventSetTimer(MathMax(1, InpRefreshS));
Redraw();
@@ -590,7 +639,13 @@ void OnDeinit(const int reason)
EventKillTimer();
ObjectsDeleteAll(0, PFX);
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();
}
//+------------------------------------------------------------------+