XYで描画したオブジェクトをスムーズに変化させる方法(MT4とMT5の比較)

 

MT5端末の問題解決に役立つ。

mql4からmql5へ製品を移行することにした。

XY座標上に描かれた矩形を使用します。

MT4では、垂直スケールを変更する際にカクカクせず非常にスムーズですが、MT5では同じ方法でフリーズすることがあり、「非スムーズ」さが目立ちます。

その効果を実証するために、特別に簡略化した試作品を作りました。MT4、MT5でも同じです。縦軸の目盛りを変えたときの違いを比べてみてください(価格目盛りの上にマウスを乗せた状態)。


MT5では全てノーブレーキになるが、再描画がぎくしゃくする。オブジェクトが多ければ多いほど、悪い。しかし、MT4ではすべてがスムーズです。

mq4とmq5のソースファイルを添付し、mq5のコードを貼り付けています。


スムーズにできるよう、よろしくお願いします。


これはMT5です」または「これは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);}
ファイル:
 
Vitaliy Kuznetsov:

MT5端末の問題解決に役立つ。

mql4からmql5へ製品を移行することにした。

XY座標上に描かれた矩形を使用します。

MT4では、垂直スケールを変更する際にカクカクせず非常にスムーズですが、MT5では同じ方法でフリーズすることがあり、「非スムーズ」さが目立ちます。

その効果を実証するために、特別に簡略化した試作品を作りました。MT4、MT5でも同じです。縦軸の目盛りを変えたときの違いを比べてみてください(価格目盛りの上にマウスを乗せた状態)。


MT5では全てノーブレーキになるが、再描画がぎくしゃくする。オブジェクトが多ければ多いほど、悪い。しかし、MT4ではすべてがスムーズです。

mq4とmq5のソースファイルを添付し、mq5のコードを貼り付けています。


スムーズにできるよう、よろしくお願いします。


私は理解したい:それは "このような" MT5または "このような" MT5用の私のコードです。


私はパソコンの前にいないので、まだこのコードを勉強して実行していませんが、まず目を引くのは2つの非同期関数ChartXYToTimePrice と ChartTimePriceToXY です。
現実離れした遅さです。この件に関しては、ずっとMQとバトってましたが、完全無視です。
各関数の実行時間は16ミリ秒、つまりループ30では30*2*16 =~1秒かかると記憶しています。
滑らかさがないのは論外としても。
まず、これらの関数をループから外してみると、30倍速く動作するようになります。iCanvasに実装しています。
 
プロファイラーで 確認してください。
 
まあそうなんですが、99.76%の実行時間を要する4つの非同期関数に遭遇しました。
私は何年も前から、これらの関数は非同期にすべきではないとMQに証明しようとしてきました。なぜならチャートのプロパティ・テーブルはすでに存在し、それらのプロパティを取得するだけで十分で、非同期処理の実行には意味がないためです。
しかし、すべての努力は無駄である。
これは本当に馬鹿の臭いがする。

そして、前時代的なMQL4ではこの問題は最小限です。

 
バグではなく、機能かも?そこにある何かが、グラフィックに最適化されているのです。そして、すべてのものを本来の目的のために使うことが望ましいので、悪い方向に作用してしまうのです。価格座標 に結びついた特殊なグラフィックオブジェクトがあるので、このタスクにはそれを使うべきでしょう。
 
Nikolai Semko:
ああ、4つの非同期関数が全時間の99.76%を占めているんだな。 ...そして、前時代のMQL4では、この問題は最小です。 。



はい、MT4では何でも飛びます。

ニコライ・セムコ
まずこれらの関数をループから外してみると、30倍速く動作するようになります。iCanvasで実装してみました。

もしよろしければ、より良い方法を例として教えていただけませんか?

 
Vitaliy Kuznetsov:

はい、MT4では何でも飛びます。

差し支えなければ、もっとスマートにできる例を教えてください。

最短の方法は、私のiCanvasのbiblioを添付することで、ループ内で非同期関数を使用しないようにすることです。
キャンバスそのものを使用する必要はありません。ずっとチャートに飾っておくのですが、空っぽで透明なものになります。
iCanvasを使わずにもっと長い方法なら、このバイブルに実装されているので、そこでまた調べる必要がありますが、気の弱い人には無理でしょう。:))
iCanvas が何をするものなのか、簡単に説明したいと思います。
OnChartEventという 内部ハンドラがあり、CHARTEVENT_CHART_CHANGE イベントをキャッチして、同じ非同期ChartGet関数を使って内部構造体をW充填しますが、このイベントの発生時に一度だけ行います。
これによって多少なりとも速度を最適化できます。
このライブラリには、画面全体を広げ、画面が変わるとそれに合わせるCanvasオブジェクトもぶらさがっています。


ここでは、例えば3行のコードが追加され、多くの行が破棄されています。


#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);
}
//+------------------------------------------------------------------+
いずれにせよ、このアルゴリズムでオブジェクトの数を増やしても、性能にはあまり影響しないでしょう。
ファイル:
iCanvas.mqh  52 kb
 
Dmitry Fedoseev:
それとも、バグではなく、機能なのでしょうか?何かというと、グラフィックが最適化されているのです。そして、すべてのものを本来の目的のために使うことが望ましいので、悪い方向に作用してしまうのです。価格 座標に関連する特殊なグラフィックオブジェクトがあるので、このタスクに使用する必要があります。


理由は明白で、「考え不足」です。
どこかの「権威」が最初(10年くらい前)にミスをしたのですが、今でも誰も「ミスをしたかもしれない」と思っていません。
https://www.mql5.com/ru/forum/1111/page2780#comment_16886162
同意して改善を約束したようにさえ見えますが、いや~終わってますね。まだあるんだ。
https://www.mql5.com/ru/forum/1111/page2781#comment_16904132


 
Nikolai Semko:

最短の方法は、私のiCanvas bibbletを添付することで、ループ内で非同期関数を使わなくて済むようにすることです。
キャンバスそのものを使う必要は全くありません。ずっとチャートに飾っておくのですが、空っぽで透明なものになります。
iCanvasを使わずにもっと長い方法なら、このバイブルに実装されているので、そこでまた調べる必要がありますが、気の弱い人には無理でしょう。:))
iCanvas が何をするものなのか、簡単に説明したいと思います。
OnChartEventという 内部ハンドラがあり、CHARTEVENT_CHART_CHANGE イベントをキャッチして、同じ非同期ChartGet関数を使って内部構造体をW充填しますが、このイベントの発生時に一度だけ行います。
これによって多少なりとも速度を最適化できます。
このライブラリには、画面全体を広げ、画面が変わるとそれに合わせるCanvasオブジェクトもぶらさがっています。


例えば、ここでは3行のコードが追加され、多くの行が破棄されています。

いずれにせよ、このアルゴリズムでオブジェクトの数を増やしても、性能にはあまり影響しないでしょう。

同じような問題を解決してくれてありがとうございました。確かに、描画速度は向上しています。図書館を極めるしかないようだ。

また、次のようなニュアンスで明記したいと思います。この処方でコンパイルすると、警告が出ます。

y = Canvas.Y(price);

で、これだと警告も出ずにコンパイルされますが、速度は少し落ちますね。

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

どちらが正しいのでしょうか)

 
Vitaliy Kuznetsov:

このような形で問題を解決していただき、ありがとうございました。確かに、描画速度は向上しています。図書館の勉強をしなければならないようだ。

また、次のようなニュアンスも明確にしたいと思います。この処方でコンパイルすると、警告が出ます。

で、これだと警告も出ずにコンパイルされますが、速度は少し落ちますね。

何がより正しいのか)

Y "の種類は?

なぜなら、直感的・客観的に「速度が少し落ちる」なら、これはBAGだからです。

 
Canvas.Y(price)の型がよくわからないのですが。

どのようなタイプを返すか
理由: