Come fare in modo che gli oggetti disegnati da XY cambino in modo fluido (MT4 vs MT5)

Vitaliy Kuznetsov  

Aiuta a risolvere un problema sul terminale MT5.

Ho deciso di trasferire il mio prodotto da mql4 a mql5.

Utilizza rettangoli che sono disegnati su coordinate XY.

In MT4 è molto fluido e senza scatti quando si cambia la scala verticale, ma in MT5 lo stesso approccio porta ad alcuni blocchi e una notevole "non fluidità".

Ho fatto appositamente un prototipo semplificato per dimostrare l'effetto. È lo stesso per MT4 e MT5. Confronta la differenza quando cambi la scala verticale (con il mouse sulla scala dei prezzi).


In MT5 tutto sarà senza freni, ma il ridisegno è a scatti. Più oggetti ci sono, peggio è. Ma in MT4 tutto è liscio.

Allego i file sorgente mq4 e mq5 e incollo il codice mq5.


Per favore, aiutatemi a renderlo fluido.


Voglio capire: "questo è" MT5 o "questo è" il mio codice per 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);}
File:
Nikolai Semko  
Vitaliy Kuznetsov:

Aiuta a risolvere un problema sul terminale MT5.

Ho deciso di trasferire il mio prodotto da mql4 a mql5.

Utilizza rettangoli che sono disegnati su coordinate XY.

In MT4 è molto fluido e senza scatti quando si cambia la scala verticale, ma in MT5 lo stesso approccio porta ad alcuni blocchi e una notevole "non fluidità".

Ho fatto appositamente un prototipo semplificato per dimostrare l'effetto. È lo stesso per MT4 e MT5. Confronta la differenza quando cambi la scala verticale (con il mouse sulla scala dei prezzi).


In MT5 tutto sarà senza freni, ma il ridisegno è a scatti. Più oggetti ci sono, peggio è. Ma in MT4 tutto è liscio.

Allego i file sorgente mq4 e mq5 e incollo il codice mq5.


Per favore, aiutatemi a renderlo fluido.


Voglio capire: è "tale e quale" MT5 o "tale e quale" il mio codice per MT5.


Non ho ancora studiato ed eseguito questo codice, perché non sono al mio computer, ma la prima cosa che salta all'occhio sono le due funzioni asincroneChartXYToTimePrice e ChartTimePriceToXY.
Sono irrealisticamente lenti. Ho lottato a lungo con MQ su questo argomento, ma è un'ignoranza totale.
Ricordo che ogni funzione viene eseguita per ~16 ms, cioè nel ciclo 30 impiega 30*2*16 =~1 sec.
Nessuna scorrevolezza è addirittura fuori questione.
Provate prima a togliere queste funzioni dal ciclo, allora funzionerà 30 volte più velocemente. L'ho implementato in iCanvas.
Nikolai Semko  
ebbene sì, ti sei imbattuto in 4 funzioni asincrone che impiegano il 99,76% del tempo per essere eseguite.
Ho cercato di dimostrare a MQ per anni che queste funzioni non dovrebbero essere asincrone, perché la tabella delle proprietà del grafico esiste già ed è sufficiente prendere quelle proprietà e non ha senso eseguire un processo asincrono.
Ma tutti gli sforzi sono vani.
Questo puzza davvero di idiozia.
So di cosa sto parlando, perché sto programmando in molti linguaggi tra cui Kanvas e utilizzando attivamente il modello event-driven.
E nel preistorico MQL4 questo problema è minimo.

Vitaliy Kuznetsov  
Nikolai Semko:
Sì, hai 4 funzioni asincrone che impiegano il 99,76% del tempo.
...
E nel preistorico MQL4 questo problema è minimo.

Sì, tutto vola in MT4.

Nikolai Semko:
Provate prima a togliere queste funzioni dal ciclo, allora funzionerà 30 volte più velocemente. Ho implementato questo in iCanvas.

Se non ti dispiace, puoi darmi un esempio di come farlo meglio?

Nikolai Semko  
Vitaliy Kuznetsov:

Sì, tutto vola in MT4.

Se non ti dispiace, puoi farmi un esempio di come farlo in modo più intelligente?

La via più breve è quella di allegare il mio iCanvas biblio, che permetterà di non usare funzioni asincrone in loop.
Non c'è bisogno di usare la tela stessa. Sarà appeso nel tuo grafico per tutto il tempo, ma sarà vuoto e trasparente.
Se un metodo più lungo senza usare iCanvas, allora dovrete cercarlo di nuovo in questa bibbia, poiché è implementato lì, ma non è per i deboli di cuore. :))
Cercherò di spiegare brevemente cosa fa iCanvas.
Ha un gestore internoOnChartEvent, che cattura l'eventoCHARTEVENT_CHART_CHANGE e riempie la sua struttura interna W, usando tutte le stesse funzioni asincrone ChartGet, ma lo fa solo una volta al verificarsi di questo evento.
Questo permette più o meno di ottimizzare la velocità.
Anche in questa libreria è appeso l'oggetto Canvas, che estende l'intero schermo e si adatta ad esso quando lo schermo cambia.


Qui, per esempio, sono state aggiunte tre linee di codice e molte linee sono state scartate:


#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);
}
//+------------------------------------------------------------------+
In ogni caso, è possibile aumentare il numero di oggetti con questo algoritmo e non influenzerà troppo le prestazioni.
File:
iCanvas.mqh  52 kb
Nikolai Semko  
Dmitry Fedoseev:
O forse non è un bug ma una caratteristica? C'è qualcosa di ottimizzato con la grafica. E funziona male perché è auspicabile utilizzare tutte le cose per il loro scopo. Ci sono oggetti grafici specializzati che sono legati alle coordinate dei prezzi - quindi dovrebbero essere usati per questo compito.

Lo pensavo anch'io, finché non ho acquisito maggiori conoscenze sull'argomento.
La ragione è inequivocabile - il sottopensiero.
Qualche "autorità" ha commesso un errore all'inizio (circa 10 anni fa) e nessuno pensa ancora che possa aver commesso un errore.
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
Sembra persino che siano stati d'accordo e abbiano promesso di migliorarlo, ma no - è finita. È ancora lì.
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


Vitaliy Kuznetsov  
Nikolai Semko:

Il modo più breve è quello di allegare il mio iCanvas bibblet, che vi permetterà di evitare l'uso di funzioni asincrone nel ciclo.
Non c'è bisogno di usare la tela stessa. Sarà appeso nel tuo grafico per tutto il tempo, ma sarà vuoto e trasparente.
Se un modo più lungo senza usare iCanvas, allora dovrete cercarlo di nuovo in questa bibbia, poiché è implementato lì, ma non è per i deboli di cuore. :))
Cercherò di spiegare brevemente cosa fa iCanvas.
Ha un gestore internoOnChartEvent, che cattura l'eventoCHARTEVENT_CHART_CHANGE e riempie la sua struttura interna W, usando tutte le stesse funzioni asincrone ChartGet, ma lo fa solo una volta al verificarsi di questo evento.
Questo permette più o meno di ottimizzare la velocità.
Anche in questa libreria è appeso l'oggetto Canvas, che estende l'intero schermo e si adatta ad esso quando lo schermo cambia.


Qui, per esempio, sono state aggiunte tre linee di codice e molte linee sono state scartate:

In ogni caso, è possibile aumentare il numero di oggetti con questo algoritmo e non influenzerà troppo le prestazioni.

Grazie per una soluzione simile al problema. In effetti, la velocità di rendering è aumentata. Sembra che dovrò padroneggiare la biblioteca.

Vorrei anche specificare la seguente sfumatura. Si compila con questa formulazione con un avvertimento:

y = Canvas.Y(price);

E si compila con questo senza un avvertimento, ma la velocità è un po' più lenta.

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

Cosa è più corretto?).

Maxim Kuznetsov  
Vitaliy Kuznetsov:

Grazie per questo tipo di soluzione al problema. In effetti, la velocità di rendering è migliorata. Sembra che dovrò imparare la biblioteca.

Vorrei anche chiarire la seguente sfumatura. Si compila con questa formulazione con un avvertimento:

E si compila con questo senza un avvertimento, ma la velocità è un po' più lenta.

Cos'è più corretto?)

Che tipo di "y" avete?

perché se int e oggettivamente"la velocità scende un po'" questa è una BORSA

Vitaliy Kuznetsov  
Non sono sicuro di quale tipo Canvas.Y(price);

che tipo restituisce
Motivazione: