Wie man Objekte, die von XY gezeichnet werden, reibungslos ändern kann (MT4 vs MT5)

 

Hilfe bei der Lösung eines Problems auf dem MT5-Terminal.

Ich habe beschlossen, mein Produkt von mql4 auf mql5 zu übertragen.

Sie verwendet Rechtecke, die auf XY-Koordinaten gezeichnet werden.

In MT4 ist es sehr glatt und ohne Ruckeln, wenn man die vertikale Skala ändert, aber in MT5 führt der gleiche Ansatz zu einigen Einfrierungen und spürbarer "Ungeschmeidigkeit".

Ich habe extra einen vereinfachten Prototyp angefertigt, um die Wirkung zu demonstrieren. Das gilt auch für MT4 und MT5. Vergleichen Sie den Unterschied, wenn Sie die vertikale Skala ändern (mit der Maus auf der Preisskala).


In MT5 wird alles ungebremst sein, aber die Neuberechnung ist ruckelig. Je mehr Objekte, desto schlimmer. Aber in MT4 läuft alles glatt.

Ich füge die mq4- und mq5-Quelldateien bei und füge den mq5-Code ein.


Bitte helfen Sie mir, es reibungslos zu gestalten.


Ich möchte verstehen: "das ist" MT5 oder "das ist" mein Code für MT5.


//+------------------------------------------------------------------+
//|                                                  PrototypeXY.mq5 |
//|                                  Copyright 2021, MetaQuotes Ltd. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2021, MetaQuotes Ltd."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property indicator_chart_window

#property indicator_buffers 0
#property indicator_plots   0


string obj_name = "Asd_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(){

return(INIT_SUCCEEDED);}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[]){
   if(NewBar()){
      DrawObj();
   }
return(rates_total);}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam){
   if(id == CHARTEVENT_CHART_CHANGE){
      DrawObj();
   }
}

//+------------------------------------------------------------------+
//| Выводим на график                                                |
//+------------------------------------------------------------------+

void DrawObj(){
   string GenName = obj_name;
   double startPricePos = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   int step_Pips = 50;
   for(int i=1; i<=20;i++){
      double stp = (step_Pips*i)*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
      RectLabelCreate(GenName+"UP_"+IntegerToString(i),startPricePos + stp);
      RectLabelCreate(GenName+"DN_"+IntegerToString(i),startPricePos - stp);
   }
   ChartRedraw(0);
}

//+------------------------------------------------------------------+ 
//| Создает прямоугольную метку                                      | 
//+------------------------------------------------------------------+ 

void RectLabelCreate(string name,   // имя метки 
                     double price   // цена
                     ){
   const long             chart_ID=0;               // ID графика
         int              sub_window=0;             // номер подокна
         int              x=0;                      // координата по оси X 
         int              y=0;                      // координата по оси Y
         
   datetime time_pos_X_centr = 0; // Время по центру графика
   double price_pos_Y_tmp = 0;
   x=(int)(ChartGetInteger(chart_ID,CHART_WIDTH_IN_PIXELS,sub_window)/2);
   ChartXYToTimePrice(chart_ID,x,y,sub_window,time_pos_X_centr,price_pos_Y_tmp);         
         
   ChartTimePriceToXY(chart_ID,sub_window,time_pos_X_centr,price,x,y);

   const int              width=50;                 // ширина 
   const int              height=10;                // высота 
   const color            back_clr=C'236,233,216';  // цвет фона 
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // тип границы 
   const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER; // угол графика для привязки 
   const color            clr=clrRed;               // цвет плоской границы (Flat) 
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // стиль плоской границы 
   const int              line_width=1;             // толщина плоской границы 
   const bool             back=false;               // на заднем плане 
   const bool             selection=false;          // выделить для перемещений 
   const bool             hidden=true;              // скрыт в списке объектов 
   const long             z_order=0;                // приоритет на нажатие мышью 

   if(!ObjectIsExist(name)){
      if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)){
         ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
         ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
         ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
         ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border); 
         ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner); 
         ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr); 
         ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style); 
         ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width); 
         ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back); 
         ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection); 
         ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection); 
         ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden); 
         ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
      }
   }else{
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y); 
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width); 
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);   
   }
} 


//+------------------------------------------------------------------+
//| Есть-ли обьект на графике                                        |
//+------------------------------------------------------------------+

bool ObjectIsExist(string name){
   if(ObjectGetString(0,name,OBJPROP_NAME,0)==name)return(true);
return(false);}

//+------------------------------------------------------------------+
//| Появился новый бар                                               |
//+------------------------------------------------------------------+

bool NewBar(){
   static int countLastBar=0;
   int curBars = iBars(Symbol(),PERIOD_CURRENT);
   bool flg = false;
   if(countLastBar!=curBars){
      countLastBar=curBars;
      flg=true;
   }
return(flg);}
Dateien:
 
Vitaliy Kuznetsov:

Hilfe bei der Lösung eines Problems auf dem MT5-Terminal.

Ich habe beschlossen, mein Produkt von mql4 auf mql5 zu übertragen.

Sie verwendet Rechtecke, die auf XY-Koordinaten gezeichnet werden.

In MT4 ist es sehr glatt und ohne Ruckeln, wenn man die vertikale Skala ändert, aber in MT5 führt der gleiche Ansatz zu einigen Einfrierungen und spürbarer "Ungeschmeidigkeit".

Ich habe extra einen vereinfachten Prototyp angefertigt, um die Wirkung zu demonstrieren. Das gilt auch für MT4 und MT5. Vergleichen Sie den Unterschied, wenn Sie die vertikale Skala ändern (mit der Maus auf der Preisskala).


In MT5 wird alles ungebremst sein, aber die Neuberechnung ist ruckartig. Je mehr Objekte, desto schlimmer. Aber in MT4 läuft alles glatt.

Ich füge die mq4- und mq5-Quelldateien bei und füge den mq5-Code ein.


Bitte helfen Sie mir, es reibungslos zu gestalten.


Ich möchte verstehen: ist es "so und so" MT5 oder "so und so" mein Code für MT5.


Ich habe diesen Code noch nicht studiert oder ausgeführt, da ich nicht an meinem Computer sitze, aber das erste, was mir ins Auge sticht, sind die beiden asynchronen FunktionenChartXYToTimePrice und ChartTimePriceToXY.
Sie sind unrealistisch langsam. Ich habe mich lange mit MQ über dieses Thema gestritten, aber es ist mir völlig egal.
Ich erinnere mich, dass jede Funktion ~16 ms lang ausgeführt wird, d. h. in Schleife 30 dauert es 30*2*16 = ~1 Sekunde.
Glätte kommt gar nicht in Frage.
Versuchen Sie zuerst, diese Funktionen aus der Schleife herauszunehmen, dann geht es 30 Mal schneller. Ich habe es in iCanvas implementiert.
 
Überprüfen Sie dies im Profiler.
 
Nun ja, Sie sind auf 4 asynchrone Funktionen gestoßen, die 99,76 % der Zeit für die Ausführung benötigen.
Ich versuche seit Jahren, MQ zu beweisen, dass diese Funktionen nicht asynchron sein sollten, weil die Tabelle mit den Diagrammeigenschaften bereits existiert und es ausreicht, diese Eigenschaften zu übernehmen, und es keinen Sinn hat, einen asynchronen Prozess auszuführen.
Aber alle Bemühungen sind vergeblich.
Das stinkt wirklich nach Idiotie.
Ich weiß, wovon ich spreche, denn ich programmiere in vielen Sprachen, einschließlich Kanvas, und verwende aktiv das ereignisgesteuerte Modell.
Und im prähistorischen MQL4 ist dieses Problem minimal.

 
Vielleicht ist es kein Fehler, sondern eine Funktion? Da ist etwas mit der Grafik optimiert. Und es funktioniert schlecht, weil es wünschenswert ist, alle Dinge für ihren Zweck zu nutzen. Es gibt spezialisierte grafische Objekte, die an Preiskoordinaten gebunden sind - sie sollten also für diese Aufgabe verwendet werden.
 
Nikolai Semko:
Ja, Sie haben 4 asynchrone Funktionen, die 99,76% der Zeit benötigen.
...
Und im prähistorischen MQL4 ist dieses Problem minimal.

Ja, im MT4 fliegt alles.

Nikolai Semko:
Versuchen Sie zuerst, diese Funktionen aus der Schleife herauszunehmen, dann geht es 30 Mal schneller. Ich habe dies in iCanvas implementiert.

Wenn Sie nichts dagegen haben, können Sie mir ein Beispiel dafür geben, wie man es besser machen kann?

 
Vitaliy Kuznetsov:

Ja, im MT4 fliegt alles.

Wenn Sie nichts dagegen haben, können Sie mir ein Beispiel dafür geben, wie man es intelligenter machen kann?

Der kürzeste Weg ist es, meine iCanvas biblio, die nicht auf asynchrone Funktionen in der Schleife verwenden können, zu befestigen.
Sie müssen nicht die Leinwand selbst verwenden. Es wird die ganze Zeit über in Ihrem Horoskop hängen, aber es wird leer und transparent sein.
Wenn eine längere Methode ohne iCanvas, dann müssen Sie es in dieser Bibel wieder nachschlagen, wie es dort implementiert ist, aber es ist nichts für schwache Nerven. :))
Ich werde versuchen, kurz zu erklären, was iCanvas tut.
Es hat einen internen HandlerOnChartEvent, der dasCHARTEVENT_CHART_CHANGE-Ereignis abfängt und seine interne Struktur W füllt, indem er die gleichen asynchronen ChartGet-Funktionen verwendet, dies aber nur einmal beim Auftreten dieses Ereignisses tut.
Dies erlaubt es, die Geschwindigkeit mehr oder weniger zu optimieren.
Auch in dieser Bibliothek hängt ein Canvas-Objekt, das den gesamten Bildschirm ausdehnt und sich anpasst, wenn sich der Bildschirm ändert.


Hier wurden beispielsweise drei Codezeilen hinzugefügt und viele Zeilen gestrichen:


#property indicator_chart_window
#include <Canvas\iCanvas.mqh> //https://www.mql5.com/ru/code/22164

#property indicator_buffers 0
#property indicator_plots   0


string obj_name = "Asd_";

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit() {
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[]) {
   if(NewBar()) {
      DrawObj();
   }
   return(rates_total);
}
//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam) {
   if(id == CHARTEVENT_CHART_CHANGE) {
      DrawObj();
   }
}
//+------------------------------------------------------------------+
//| Выводим на график                                                |
//+------------------------------------------------------------------+
void DrawObj() {
   string GenName = obj_name;
   double startPricePos = SymbolInfoDouble(Symbol(),SYMBOL_BID);
   int step_Pips = 50;
   for(int i=1; i<=20; i++) {
      double stp = (step_Pips*i)*SymbolInfoDouble(Symbol(),SYMBOL_POINT);
      RectLabelCreate(GenName+"UP_"+IntegerToString(i),startPricePos + stp);
      RectLabelCreate(GenName+"DN_"+IntegerToString(i),startPricePos - stp);
   }
   ChartRedraw(0);
}
//+------------------------------------------------------------------+
//| Создает прямоугольную метку                                      |
//+------------------------------------------------------------------+
void RectLabelCreate(string name,   // имя метки
                     double price   // цена
                    ) {
   const long  chart_ID=0;               // ID графика
   int         sub_window=0;             // номер подокна
   int         x=0;                      // координата по оси X
   int         y=0;                      // координата по оси Y
   x=W.Width/2;
   y = Round(Canvas.Y(price));
   //x=(int)(ChartGetInteger(chart_ID,CHART_WIDTH_IN_PIXELS,sub_window)/2);
   //ChartXYToTimePrice(chart_ID,x,y,sub_window,time_pos_X_centr,price_pos_Y_tmp);
   //ChartTimePriceToXY(chart_ID,sub_window,time_pos_X_centr,price,x,y);
   const int              width=50;                 // ширина
   const int              height=10;                // высота
   const color            back_clr=C'236,233,216';  // цвет фона
   const ENUM_BORDER_TYPE border=BORDER_SUNKEN;     // тип границы
   const ENUM_BASE_CORNER corner=CORNER_LEFT_UPPER; // угол графика для привязки
   const color            clr=clrRed;               // цвет плоской границы (Flat)
   const ENUM_LINE_STYLE  style=STYLE_SOLID;        // стиль плоской границы
   const int              line_width=1;             // толщина плоской границы
   const bool             back=false;               // на заднем плане
   const bool             selection=false;          // выделить для перемещений
   const bool             hidden=true;              // скрыт в списке объектов
   const long             z_order=0;                // приоритет на нажатие мышью
   if(ObjectCreate(chart_ID,name,OBJ_RECTANGLE_LABEL,sub_window,0,0)) {
      ObjectSetInteger(chart_ID,name,OBJPROP_XDISTANCE,x);
      ObjectSetInteger(chart_ID,name,OBJPROP_YDISTANCE,y);
      ObjectSetInteger(chart_ID,name,OBJPROP_XSIZE,width);
      ObjectSetInteger(chart_ID,name,OBJPROP_YSIZE,height);
      ObjectSetInteger(chart_ID,name,OBJPROP_BGCOLOR,back_clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_BORDER_TYPE,border);
      ObjectSetInteger(chart_ID,name,OBJPROP_CORNER,corner);
      ObjectSetInteger(chart_ID,name,OBJPROP_COLOR,clr);
      ObjectSetInteger(chart_ID,name,OBJPROP_STYLE,style);
      ObjectSetInteger(chart_ID,name,OBJPROP_WIDTH,line_width);
      ObjectSetInteger(chart_ID,name,OBJPROP_BACK,back);
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTABLE,selection);
      ObjectSetInteger(chart_ID,name,OBJPROP_SELECTED,selection);
      ObjectSetInteger(chart_ID,name,OBJPROP_HIDDEN,hidden);
      ObjectSetInteger(chart_ID,name,OBJPROP_ZORDER,z_order);
   } else Print(_LastError);
}
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
bool NewBar() {
   static int countLastBar=0;
   int curBars = iBars(Symbol(),PERIOD_CURRENT);
   bool flg = false;
   if(countLastBar!=curBars) {
      countLastBar=curBars;
      flg=true;
   }
   return(flg);
}
//+------------------------------------------------------------------+
Auf jeden Fall können Sie die Anzahl der Objekte mit diesem Algorithmus erhöhen, ohne dass dies die Leistung allzu sehr beeinträchtigt.
Dateien:
iCanvas.mqh  52 kb
 
Dmitry Fedoseev:
Oder handelt es sich vielleicht nicht um einen Fehler, sondern um eine Funktion? Dort wird etwas mit Grafiken optimiert. Und es funktioniert schlecht, weil es wünschenswert ist, alle Dinge für ihren Zweck zu nutzen. Es gibt spezialisierte grafische Objekte, die an Preiskoordinaten gebunden sind - sie sollten also für diese Aufgabe verwendet werden.

Das dachte ich auch, bis ich mehr Wissen über dieses Thema erlangte.
Der Grund ist eindeutig: zu wenig Denken.
Irgendeine "Behörde" hat am Anfang (vor etwa 10 Jahren) einen Fehler gemacht, und niemand denkt mehr daran, dass er einen Fehler gemacht haben könnte.
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
Sie scheinen sogar zugestimmt und versprochen zu haben, es zu verbessern, aber nein - es ist vorbei. Sie ist noch da.
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


 
Nikolai Semko:

Der kürzeste Weg ist, mein iCanvas-Bibblet anzuhängen, wodurch Sie die Verwendung asynchroner Funktionen in der Schleife vermeiden können.
Sie müssen die Leinwand selbst gar nicht verwenden. Es wird die ganze Zeit über in Ihrem Horoskop hängen, aber es wird leer und transparent sein.
Wenn eine längere Methode ohne iCanvas, dann müssen Sie es in dieser Bibel wieder nachschlagen, wie es dort implementiert ist, aber es ist nichts für schwache Nerven. :))
Ich werde versuchen, kurz zu erklären, was iCanvas tut.
Es hat einen internen HandlerOnChartEvent, der dasCHARTEVENT_CHART_CHANGE-Ereignis abfängt und seine interne Struktur W füllt, indem er die gleichen asynchronen ChartGet-Funktionen verwendet, dies aber nur einmal beim Auftreten dieses Ereignisses tut.
Dies erlaubt es, die Geschwindigkeit mehr oder weniger zu optimieren.
Auch in dieser Bibliothek hängt ein Canvas-Objekt, das den gesamten Bildschirm ausdehnt und sich anpasst, wenn sich der Bildschirm ändert.


Hier wurden beispielsweise drei Codezeilen hinzugefügt und viele Zeilen gestrichen:

Auf jeden Fall können Sie die Anzahl der Objekte mit diesem Algorithmus erhöhen, ohne dass dies die Leistung allzu sehr beeinträchtigt.

Vielen Dank für eine ähnliche Lösung des Problems. Die Rendering-Geschwindigkeit hat sich in der Tat erhöht. Sieht aus, als müsste ich die Bibliothek meistern.

Ich möchte auch auf folgende Nuance hinweisen. Es kompiliert mit dieser Formulierung mit einer Warnung:

y = Canvas.Y(price);

Und es kompiliert mit diesem ohne eine Warnung, aber die Geschwindigkeit ist ein bisschen langsamer.

y = (int)Canvas.Y(price);

Was ist richtiger?)

 
Vitaliy Kuznetsov:

Ich danke Ihnen für diese Art der Problemlösung. Die Rendering-Geschwindigkeit hat sich in der Tat verbessert. Sieht so aus, als müsste ich die Bibliothek lernen.

Ich möchte auch die folgende Nuance klarstellen. Es kompiliert mit dieser Formulierung mit einer Warnung:

Und es kompiliert mit diesem ohne eine Warnung, aber die Geschwindigkeit ist ein bisschen langsamer.

Was ist richtiger?)

Welchen Typ von "y" haben Sie?

denn wenn int und objektiv"die Geschwindigkeit ein wenig sinkt", ist das ein BAG

 
Ich bin mir nicht ganz sicher, welchen Typ Canvas.Y(Preis);

welchen Typ er zurückgibt