【MQL4】線形回帰チャネルの価格取得方法|ObjectGetValueByTimeが0になる理由と対処法

線形回帰解説①
EAサンプル・ノウハウ
記事内に商品プロモーションを含む場合があります
スポンサーリンク

「線形回帰チャネルの価格をEAで取得したいのに、ObjectGetValueByTime()が0を返す…」
「コードは合っているはずなのに、なぜ値が取れないの?」

本記事では、MT4(MQL4)で線形回帰チャネルの価格帯を正しく取得する方法を、
実際に0が返る原因 → 解決手順 → サンプルコードの順で詳しく解説します。

トレンドラインやチャネル系オブジェクトの値取得で悩んでいる方は、
「次のOnTickで取得する理由」が必ず理解できる内容です。

📘 この記事で解説する内容
  • 線形回帰(チャネル)オブジェクトの仕組み
  • ObjectGetValueByTime() が 0 を返す理由
  • 次のOnTickで値が取得できる仕組み
  • 線形回帰の上・中・下ラインの取得方法
  • EAで使える実践サンプルコード

MT4で線形回帰(チャネル)の価格をEAで取得したい人向けの前提知識

MT4の線形回帰(チャネル)は、一定期間の価格データをもとに 「価格の平均的な進行方向(トレンド)」を可視化するためのインジケーターです。

チャート上では、中央の回帰ライン(ミドル)と、 その上下に配置された上限ライン・下限ラインによって、 価格がどのレンジで推移しているかを直感的に把握できます。

裁量トレードでは視覚的に判断しやすい一方で、 EA(MQL4)で線形回帰を扱う場合は注意点があります。
それは、線形回帰が「インジケーター」ではなく チャートオブジェクト(OBJ_REGRESSION)として描画される点です。

そのため、iCustom() のようにバッファから値を取得することはできず、 ObjectGetValueByTime() などの オブジェクト取得系関数を使う必要があります。

しかし、この方法をそのまま実装すると、 「価格が0になる」「値が取得できない」といった問題に直面しがちです。

本記事では、線形回帰チャネルの描画タイミングと取得タイミングの違いに注目し、 なぜ ObjectGetValueByTime() が 0 を返すのか、 そしてどうすれば正しく価格を取得できるのかを順を追って解説していきます。

線形回帰ではなく、トレンドラインについて知りたい場合はこちら↓

ObjectGetValueByTime()で線形回帰チャネルの価格が0になる原因

まず、線形回帰の価格帯を取得する場合、ObjectGetValueByTime()で取得する事ができます。

「MT4でEA自作しちゃお~」さんのサイトを見てもObjectGetValueByTime()の解説に線形回帰が出てきているので取れそうです。が、ObjectGetValueByTime()を使って実際にソースコードを組んで動かしてみたんですが、0が返ってきて全く取れる気配がありませんでした。

色々検証している中で、 Nisai FX さんの記事にて、 「2回目のOnTickで価格を取得できた」という情報を見つけました。

そこで実際に自分の環境でも検証してみたところ、 線形回帰を描画した直後のOnTickでは値が取得できず次のOnTickで初めて価格が取得できることを確認できました。

つまり、ObjectGetValueByTime() が 0 を返す原因は、 線形回帰オブジェクトの描画が完了する前に値を取得しようとしていることにあります。

以下にサンプルソースを交えて解説します。

線形回帰チャネルの価格を取得するEAサンプルコード(MQL4)

サンプルのソースコードは単純に線形回帰を引くEAを作り、その価格帯をObjectGetValueByTime()で取得し操作履歴に出力しています。

MT4で線形回帰チャネルを描画しObjectGetValueByTimeで価格取得を試みたが初回OnTickでは0が返っている例

画像下段の赤枠を見ると分かりますが、最初はObjectGetValueByTime()で取得すると価格が0になっている事がわかります。

//+------------------------------------------------------------------+
//|                                                         test.mq4 |
//|                        Copyright 2020, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict

datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   
  }
  
// エントリー関数
void OnTick()
{
   string obj_name = "testobj";
   int    chart_id = 0;
   
   //新しい足ができた時に1度だけ線形回帰を再描画
   if(Time[0] != prevtime){
      prevtime = Time[0];
      Print("描画準備中(このメッセージが出た時は値が取得できない)");
      ObjectDelete(chart_id,obj_name);
      
      ObjectCreate(chart_id,obj_name,OBJ_REGRESSION,0,Time[40],0,Time[10],0);
       
      ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,1);              // ラインの幅設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
      ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);             // オブジェクトのチャートクリックイベント優先順位
      ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)
      
   }

   int windowNumber=ObjectFind(chart_id,obj_name);
   
   if(windowNumber<0){
      
   }else{
   
      //オブジェクトの値を取得する
      long obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,0);
      double Sh_T = ObjectGetValueByTime(0,obj_name,obj_time,0);
      
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_T);

   }
   
}

スポンサーリンク

サンプルコードの解説|線形回帰オブジェクトの描画と取得処理

線形回帰を引くサンプルソースの解説です。

まず線形回帰は以下の箇所で引いています。ローソク足を生成した時(一番最初)だけ描画し、次のローソク足ができるまでは線形回帰を再描画しないようにしています。

   //新しい足ができた時に1度だけ線形回帰を再描画
   if(Time[0] != prevtime){
      prevtime = Time[0];
      Print("描画準備中(このメッセージが出た時は値が取得できない)");
      ObjectDelete(chart_id,obj_name);
      
      ObjectCreate(chart_id,obj_name,OBJ_REGRESSION,0,Time[40],0,Time[10],0);
       
      ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,1);              // ラインの幅設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
      ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);             // オブジェクトのチャートクリックイベント優先順位
      ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)
      
   }

続いて線形回帰の価格帯を取得及び操作履歴に出力する箇所ですが、ObjectFind()を使い線形回帰が見つからない場合は操作履歴に出力しないようにしています。

   int windowNumber=ObjectFind(chart_id,obj_name);
   
   if(windowNumber<0){
      
   }else{
   
      //オブジェクトの値を取得する
      long obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,0);
      double Sh_T = ObjectGetValueByTime(0,obj_name,obj_time,0);
      
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_T);

   }

バックテストで線形回帰チャネルの値が取れない原因と注意点

バックテストで確認する際は、モデルを『始値のみ』にすると線形回帰の値は取れないので、『全ティック』または『コントロールポイント』で確認しましょう。

線形回帰の値をバックテストで取得するには、『全ティック』又は『コントロールポイント』が必須

線形回帰チャネルの価格が取得できるポイントはどこか

線形回帰の価格帯を取得及び操作履歴に表示の部分を以下のように書き換えて、線形回帰の価格帯がとれる部分を洗い出しました。

      //オブジェクトの値を取得する
      long obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,0);
      double Sh_T = ObjectGetValueByTime(0,obj_name,obj_time,0);
      double Sh_M = ObjectGetValueByTime(0,obj_name,obj_time,1);
      double Sh_B = ObjectGetValueByTime(0,obj_name,obj_time,2);
      
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_T);
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_M);
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_B);

      obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,1);
      Sh_T = ObjectGetValueByTime(0,obj_name,obj_time,0);
      Sh_M = ObjectGetValueByTime(0,obj_name,obj_time,1);
      Sh_B = ObjectGetValueByTime(0,obj_name,obj_time,2);
      
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_T);
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_M);
      Print("時間:",TimeToStr(obj_time), " 価格:",Sh_B);  
線形回帰チャネルの始点と終点において上段・ミドル・下段ラインの価格が取得できるポイントを示した例

取れる箇所は、始点(2023.04.25 14:00)と終点(2023.04.25 21:00)の値です。

またミドルラインの値、上段の値、下段の値という感じで取れる事がわかります。

線形回帰の描画だけでは最新の値(現在時間の値)は取得できない
スポンサーリンク

線形回帰の最新の値(現在時間の値)を取りたい場合

線形回帰の最新の値(現在時間の値)を取りたい場合は、線形回帰上にトレンドラインを引けばOKです。

線形回帰の情報を元に取得してきます。こんな感じ。

      //オブジェクトの値を取得する
      long obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,0);
      double Sh_M = ObjectGetValueByTime(0,obj_name,obj_time,0);
      double Sh_T = ObjectGetValueByTime(0,obj_name,obj_time,1);
      double Sh_B = ObjectGetValueByTime(0,obj_name,obj_time,2);

      obj_time = ObjectGetInteger(0,obj_name,OBJPROP_TIME,1);
      double Sh2_M = ObjectGetValueByTime(0,obj_name,obj_time,0);
      double Sh2_T = ObjectGetValueByTime(0,obj_name,obj_time,1);
      double Sh2_B = ObjectGetValueByTime(0,obj_name,obj_time,2);


      //トレンドラインの描画
       obj_name = "Trendline_DUMY_M";
       chart_id = 0;
      
      ObjectDelete(chart_id,obj_name);
      ObjectCreate(chart_id,obj_name,OBJ_TREND,0,Time[200], Sh_M, Time[10], Sh2_M);    

      // トレンドラインのプロパティを設定する
      ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,2);              // ラインの幅設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
      ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位
      ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)

      //トレンドライン(上2)ポイントの描画
       obj_name = "Trendline_DUMY_U";
       chart_id = 0;
      
      ObjectDelete(chart_id,obj_name);
      ObjectCreate(chart_id,obj_name,OBJ_TREND,0,Time[200], Sh_T, Time[10], Sh2_T);    

      // トレンドラインのプロパティを設定する
      ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,2);              // ラインの幅設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
      ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位
      ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)


      //トレンドライン(下2)ポイントの描画
       obj_name = "Trendline_DUMY_B";
       chart_id = 0;
      
      ObjectDelete(chart_id,obj_name);
      ObjectCreate(chart_id,obj_name,OBJ_TREND,0,Time[200], Sh_B, Time[10], Sh2_B);    

      // トレンドラインのプロパティを設定する
      ObjectSetInteger(chart_id,obj_name,OBJPROP_COLOR,clrYellow);    // ラインの色設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_STYLE,STYLE_SOLID);  // ラインのスタイル設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_WIDTH,2);              // ラインの幅設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_BACK,false);           // オブジェクトの背景表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTABLE,true);     // オブジェクトの選択可否設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_SELECTED,true);       // オブジェクトの選択状態
      ObjectSetInteger(chart_id,obj_name,OBJPROP_HIDDEN,true);         // オブジェクトリスト表示設定
      ObjectSetInteger(chart_id,obj_name,OBJPROP_ZORDER,0);     // オブジェクトのチャートクリックイベント優先順位
      ObjectSetInteger(chart_id,obj_name,OBJPROP_RAY_RIGHT,true);      // ラインの延長線(右)

      obj_name = "Trendline_DUMY_U";    //線形回帰トレンドライン(上)
      double senkeikaiki_T = NormalizeDouble(ObjectGetValueByShift(obj_name, 1),5);
      double senkeikaiki_T2 = NormalizeDouble(ObjectGetValueByShift(obj_name, 2),5);
      
      obj_name = "Trendline_DUMY_M";    //線形回帰トレンドライン(真ん中)
      double senkeikaiki_M = NormalizeDouble(ObjectGetValueByShift(obj_name, 1),5);
      
      obj_name = "Trendline_DUMY_B";    //線形回帰トレンドライン(下)
      double senkeikaiki_B = NormalizeDouble(ObjectGetValueByShift(obj_name, 1),5);
      double senkeikaiki_B2 = NormalizeDouble(ObjectGetValueByShift(obj_name, 2),5);

//オブジェクトの値を取得する の箇所で線形回帰の値を取得し、それ以降でトレンドラインを3本描画しています。

❓ 線形回帰チャネルをEAで扱う際のよくある質問【MT4・MQL4対応】

💬 線形回帰チャネルの価格取得やEA実装について、よくある疑問をまとめました。

ObjectGetValueByTime()で線形回帰チャネルの価格が0になるのはなぜですか?

線形回帰チャネルはチャートオブジェクトとして描画されるため、描画直後のOnTickではオブジェクト情報が確定しておらず、ObjectGetValueByTime()を実行すると0が返る場合があります。次のOnTick以降で取得することで、正しく価格を取得できます。

線形回帰チャネルの最新の価格(現在時間の値)は取得できますか?

線形回帰チャネル単体では現在時間の価格を直接取得することはできません。取得した線形回帰の値を元にトレンドラインを描画し、そのトレンドラインからObjectGetValueByShift()などを使って現在価格を取得する方法が有効です。

バックテストでは線形回帰チャネルの価格が取得できないことがありますか?

はい。バックテストを「始値のみ」で実行した場合、線形回帰チャネルの価格は正しく取得できません。「全ティック」または「コントロールポイント」を選択することで、正常に取得できるようになります。

まとめ|MT4で線形回帰チャネルの価格をEAで扱う際のポイント

本記事では、MT4(MQL4)で線形回帰チャネルの価格をEAで扱う際の注意点と、正しい取得方法について解説しました。

  • 線形回帰チャネルはインジケーターではなく、チャートオブジェクト(OBJ_REGRESSION)として描画される
  • そのため、iCustom()では取得できず、ObjectGetValueByTime()などのオブジェクト取得系関数を使う必要がある
  • 線形回帰を描画した同一OnTickではオブジェクト情報が確定しておらず、価格を取得しようとすると 0 が返る
  • 次のOnTick以降で取得することで、線形回帰チャネルの上・中・下ラインの価格を正しく取得できる
  • バックテストでは「始値のみ」では取得できないため、全ティックまたはコントロールポイントを使用する
  • 線形回帰チャネル単体では現在時間の値は取得できないため、必要に応じてトレンドラインを併用する

MT4でライン系オブジェクトの値が取得できない場合は、
「描画タイミングと取得タイミングが一致しているか」を一度疑ってみることが重要です。

線形回帰チャネルに限らず、トレンドラインやチャネル系オブジェクトをEAで扱う際も、
この考え方を応用すれば安定した価格取得が可能になります。


✅ 今回のロジックをベースにしたEAサンプルも多数公開中

今回紹介したようなEAの売買ロジック・考え方をベースに、
当サイトではさまざまなFX自動売買EAのサンプルコードを公開しています。

ロジックの違いや設計の考え方を比較しながら、
自分に合ったEA構成を探したい方はぜひチェックしてみてください。


📊 EA運用・検証フェーズに進みたい方へ

今回のような仕組みを理解したうえで、
「実際にどのEAが安定しているのか」、「検証データではどんな差が出ているのか」
を確認したい方は、以下の記事も参考になります。


EA開発初心者向けに、今後も実践的に使えるMQL4関数や実装例を紹介していきます。
気になる機能やロジックがあれば、用途別に整理した関連記事もぜひあわせてご覧ください。

コメント

タイトルとURLをコピーしました