EAでオブジェクトを作成して値が取れない問題

EA内でオブジェクトを作成した後に値を取得したいという場面。

MQL4で以下のコードを書く。

1#property copyright "nisai"
2#property link      "https://nisaifx.com"
3#property version   "1.00"
4#property strict
5 
6string objName="obj";
7int count=0;
8 
9int OnInit(){
10   return(INIT_SUCCEEDED);
11}
12 
13void OnTick(){
14   Print("count:",count);
15   ObjectCreate(0,objName,OBJ_TREND,0,iTime(NULL,PERIOD_CURRENT,60),1.06,iTime(NULL,PERIOD_CURRENT,10),1.068);
16   ObjectSetInteger(0,objName,OBJPROP_COLOR,clrYellow);
17   ObjectSetInteger(0,objName,OBJPROP_RAY_RIGHT,true);
18   ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);
19   int windowNumber=ObjectFind(0,objName);
20   if(windowNumber>=0){
21      Print("object found:", windowNumber);
22      double value=ObjectGetValueByTime(0,objName,iTime(NULL,PERIOD_CURRENT,0),0);
23      Print("value:", value);
24      ExpertRemove();
25   }
26   count++;
27}

メインチャート(チャート番号:0)に対して、トレンドライン(OBJ_TREND)を作成する。
オブジェクトが作成されたことを確認できたら、現在バーの位置の値を取得し、これを表示してEAを削除する。
何回OnTickが動いたかも表示させる。

走らせてみる。オブジェクトがどのように作成されるかを見るためにビジュアルモードをONにする。

チャートにはトレンドラインが表示された。

OnTickは1度だけ実行された(オブジェクト作成後、すぐに参照が可能)。
オブジェクトは問題なく作成され、値「1.0696」が表示された。

オブジェクトを線形回帰チャネル(OBJ_REGRESSION)に変更してみる。

1#property copyright "nisai"
2#property link      "https://nisaifx.com"
3#property version   "1.00"
4#property strict
5 
6string objName="obj";
7int count=0;
8 
9int OnInit(){
10   return(INIT_SUCCEEDED);
11}
12 
13void OnTick(){
14   Print("count:",count);
15   ObjectCreate(0,objName,OBJ_REGRESSION,0,iTime(NULL,PERIOD_CURRENT,60),0,iTime(NULL,PERIOD_CURRENT,10),0);
16   ObjectSetInteger(0,objName,OBJPROP_COLOR,clrYellow);
17   ObjectSetInteger(0,objName,OBJPROP_RAY_RIGHT,true);
18   ObjectSetInteger(0,objName,OBJPROP_SELECTED,true);
19   int windowNumber=ObjectFind(0,objName);
20   if(windowNumber>=0){
21      Print("object found:", windowNumber);
22      double value=ObjectGetValueByTime(0,objName,iTime(NULL,PERIOD_CURRENT,0),0);
23      Print("value:", value);
24      ExpertRemove();
25   }
26   count++;
27}

チャネルが描画された。

トレンドラインの時同様にオブジェクトは作成され、すぐに参照可能になったが値が取れていない。

オブジェクトが存在しない場合は作成、存在する場合は値を取得して、値が0以外であればEAを削除するように書き換える。
(オブジェクトを作成したティックでは参照を行わない)

1#property copyright "nisai"
2#property link      "https://nisaifx.com"
3#property version   "1.00"
4#property strict
5 
6string objName="obj";
7int count=0;
8 
9int OnInit(){
10   return(INIT_SUCCEEDED);
11}
12 
13void OnTick(){
14   Print("count:",count);
15   int windowNumber=ObjectFind(0,objName);
16   if(windowNumber<0){
17      ObjectCreate(0,objName,OBJ_REGRESSION,0,iTime(NULL,PERIOD_CURRENT,60),0,iTime(NULL,PERIOD_CURRENT,10),0);
18      ObjectSetInteger(0,objName,OBJPROP_COLOR,clrYellow);
19      ObjectSetInteger(0,objName,OBJPROP_RAY_RIGHT,true);
20      ObjectSetInteger(0,objName,OBJPROP_SELECTED,true);
21   }else{
22      Print("object found:", windowNumber);
23      double value=ObjectGetValueByTime(0,objName,iTime(NULL,PERIOD_CURRENT,0),0);
24      Print("value:", value);
25      if(value>0){
26         ExpertRemove();
27      }
28   }
29   count++;
30}

今度は値が取得できた。
ただ、今回は2度目のOnTickで取得できたが、必ず2度目に取得できるわけではない。
リファレンス(https://docs.mql4.com/objects/objectcreate)には、ObjectCreateが非同期であることが書いてある(MQL5も同様)。
リファレンスによると、ObjectCreateの戻り値ではなくObjectFindなどを用いるように書いてあるが、前述の通り値が取れるとは限らない。

テスター上、EAの処理と非同期のオブジェクト作成処理が同期していないため、ビジュアルモードの速度が影響することになる。
テスターがゆっくり動いていれば、オブジェクト作成後、次のOnTickで値が取得できるだろう。

また、謎の落とし穴がもう一つあって、ビジュアルモードのON/OFF自体がオブジェクト作成処理に影響している場合がある。
MT4で前述のテストをする場合、トレンドラインを作成・表示する処理はビジュアルモードがOFFであっても値を取得できるが、線形回帰チャネルはいつまで待っても取得できない。
できるオブジェクトとできないオブジェクトがあるという事だが、どこに線引きがあるのかは知らない。

ただし、MT5で同様のテストをする場合、ビジュアルモードのON/OFFに関わらずどちらも取得できる(なんなら線形回帰チャネルであっても作成後即取得でき、非同期であるという意識をしないかもしれない)。

まとめ

とりあえずMT4ユーザーは早くMT5に乗り換えよう。
ただ、EAでオブジェクトを扱うのは色々と面倒くさい。作ったり消したり、それらの処理が非同期だったり。
オブジェクトに頼らずに計算した方が、間違いなく安全なプログラムにはなるだろうと思う。
オブジェクトは、あくまで人の目に見せるためだけに使おう。

2件のフィードバック

  1. 線形回帰の値を取得する際に、ObjectGetValueByTime()を使って値が取れない状況でした。記事の内容のようにライン描画した後のOnTickで値を取得するようにすると取れました。有難うございます!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

コメントする