MQL4プログラミング 第7回 インジケーターを作成してみよう(勝敗判定の表示)

今回の目標はHighサイン(上向き矢印)、Lowサイン(下向き矢印)が出た次の足の判定結果を表示する処理を追加します。過去のロウソク足も含めた判定が可能となり、いよいよ各通貨ペアチャートの勝率表示の下準備ができあがります。今回は勝負判定用の小さな関数を1つ追加します。今回の内容もしっかり押さえてスキルアップして下さい。

勝負判定用の関数

以下は今回追加する勝負判定用関数の部分のみ切り出したソースコードです。

//+------------------------------------------------------------------+
//| 勝敗のサインを確定する
//| 引数1 シフト数
//+------------------------------------------------------------------+
void winLossCheck(int i) {
//printf("[%d] winLossCheck(%d) in ", __LINE__, i);
   int j, k;
   int counter;

   counter = winCountMaxBar;
   
   if (i > counter) {
      return;
   }
   
   if (Log1Down[i+2] != EMPTY_VALUE) {
      if (iOpen(NULL, 0, i+1) > iClose(NULL, 0, i+1)) {
         OkSign[i+1] = High[i+1] + 5 * Point;
      }
      else  {
         NgSign[i+1] = High[i+1] + 5 * Point;
      }
   }
   if (Log1Up[i+2] != EMPTY_VALUE) {
      if (iOpen(NULL, 0, i+1) < iClose(NULL, 0, i+1)) {
         OkSign[i+1] = Low[i+1] - 5 * Point;
      }
      else {
         NgSign[i+1] = Low[i+1] - 5 * Point;
      }
   }
}

まずは関数の先頭5行目、引数はローソク足のシフト数1つです。今足だと引数iは0でこの関数が呼ばれます。
16行目:Log1Down[i+2]となっています。Log1Down配列は下向き矢印用バッファでしたね。ここに有効な値(つまり矢印を表示している状態)であれば次足が陰線であれば勝ちのバッファに有効な値をセットします。次足が陽線でなければ負けのバッファに有効な値をセットします。
この[i+2]や[i+1]の意味は下記イメージ図を参照のこと。

これらの内容が理解できれば次に示す完全版ソースコードの理解がしやすいと思います。

// prg-0007.mq4
#property copyright "Copyright 2024, smile-invest-blog.com"
#property link      "https://www.smile-invest-blog.com/prg-0007/"
#property strict
#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 Yellow
#property indicator_color2 Red
#property indicator_color3 Red
#property indicator_color4 White
#property indicator_width1 2
#property indicator_width2 2
#property indicator_width3 1
#property indicator_width4 1

double Log1Up[];   // Highサイン用バッファ
double Log1Down[]; // Low サイン用バッファ
double OkSign[];
double NgSign[];
int    realBars = 0;
int winCountMaxBar   = Bars - 3;   // 勝率計算の足本数

extern int shortMaPeriod = 10;  // 短期移動平均線の期間
extern int longMaPeriod  = 30;  // 長期移動平均線の期間
extern int adxPeriod     = 14;  // ADXの期間
//+------------------------------------------------------------------+
//| initialization function
//+------------------------------------------------------------------+
int init() {
//---- indicators
   SetIndexStyle(0, DRAW_ARROW, EMPTY);
   SetIndexArrow(0, 233);
   SetIndexBuffer(0, Log1Up);
   SetIndexStyle(1, DRAW_ARROW, EMPTY);
   SetIndexArrow(1, 234);
   SetIndexBuffer(1, Log1Down);
   SetIndexStyle(2, DRAW_ARROW, EMPTY);
   SetIndexArrow(2, 161);
   SetIndexBuffer(2, OkSign);
   SetIndexStyle(3, DRAW_ARROW, EMPTY);
   SetIndexArrow(3, 120);
   SetIndexBuffer(3, NgSign);

//----
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| deinitialization function
//+------------------------------------------------------------------+
int deinit() {
   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| start function
//+------------------------------------------------------------------+
int start() {
   bool ret_hantei;  // hantei関数の結果格納用
   int counted_bars=IndicatorCounted(); 
   int limit = Bars - counted_bars;

   for(int i = limit - 1; i >= 0; i--) {
      if(i == 0) {
         Log1Down[i] = EMPTY_VALUE;
         Log1Up[i]   = EMPTY_VALUE;
         OkSign[i]   = EMPTY_VALUE;
         NgSign[i]   = EMPTY_VALUE;

         ret_hantei = hantei(i);    // 上下サイン表示の判定を関数化した
         if (ret_hantei == true) {  // hantei関数の結果がtrueの時
            if (realBars < Bars) {  // 1本のローソク足で1回だけ実行するための条件
               realBars = Bars;     // realBarsをBarsにする事で同じローソク足では1回だけ実行する
               if (Log1Up[i] != EMPTY_VALUE) {           // hantei関数内で上サイン表示と判定された
                  Alert(Symbol(), " *** High ***");
               }
               else if (Log1Down[i] != EMPTY_VALUE) {    // hantei関数内で下サイン表示と判定された
                  Alert(Symbol(), " *** Low ***");
               }
            }
         }

         if (TaskPeriod()){
            // 勝敗判定
            winLossCheck(i);
         }

      }

      if (i > 1) {
         // 過去足のサインを表示する
         hantei(i);
         winLossCheck(i);
      }

   }

   return(0);
}

//+------------------------------------------------------------------+
//| エントリーの条件判定関数
//| int i メイン関数からのiの値
//+------------------------------------------------------------------+
bool hantei(int i) {
   double shortMaPrevious, longMaPrevious;
   double shortMaCurrent, longMaCurrent;
   double adx;

   // 前回の移動平均値を取得
   shortMaPrevious = iMA(NULL, 0, shortMaPeriod, 0, MODE_SMA, PRICE_CLOSE, i+1);
   longMaPrevious  = iMA(NULL, 0, longMaPeriod, 0, MODE_SMA, PRICE_CLOSE, i+1);

   // 現在の移動平均値を取得
   shortMaCurrent = iMA(NULL, 0, shortMaPeriod, 0, MODE_SMA, PRICE_CLOSE, i);
   longMaCurrent  = iMA(NULL, 0, longMaPeriod, 0, MODE_SMA, PRICE_CLOSE, i);

   // 現在のADXベースライン値を取得する
   adx = getAdx(MODE_MAIN, i);

   if (shortMaPrevious < longMaPrevious && shortMaCurrent > longMaCurrent) {
      // // 移動平均線のクロスを確認 → ゴールデンクロス
      if (adx > 25) {
         // adx値が25より大きい場合 → Highサイン
         Log1Up[i] = Low[i] - 10 * Point;
         return true;
         }
   }
   else if (shortMaPrevious > longMaPrevious && shortMaCurrent < longMaCurrent) {
       // 移動平均線のクロスを確認 → デッドクロス
      if (adx > 25) {
         // adx値が25より大きい場合 → Lowサイン
         Log1Down[i] = High[i] + 10 * Point;
         return true;
      }
   }

   return false;

}

//+----------------------------------------------------------------------+
//| ADXの値を取得する
//| 引数1 MODE_MAIN(0):ベースライン,MODE_PLUSDI(1):+DIライン,MODE_MINUSDI(2):-DIライン
//| 引数2 シフト数
//+----------------------------------------------------------------------+
double getAdx(int mode, int i) {
   double ret;
   ret         = iADX(
                           NULL,        // 通貨ペア
                           0,           // 時間軸
                           adxPeriod,   // 平均期間
                           PRICE_CLOSE, // 適用価格
                           mode,        // ラインインデックス
                           i            // シフト
                          );
   return ret;
}

//+------------------------------------------------------------------+
//| ローソク足確定時の処理
//+------------------------------------------------------------------+
bool TaskPeriod() {
    static    datetime s_lasttime;                      // 最後に記録した時間軸時間
                                                        // staticはこの関数が終了してもデータは保持される

    datetime temptime    = iTime( Symbol(), Period() ,0 );  // 現在の時間軸の時間取得

    if ( temptime == s_lasttime ) {                     // 時間に変化が無い場合
        return false;                                         // 処理終了
    }
    s_lasttime = temptime;                              // 最後に記録した時間軸時間を保存

    // ----- 処理はこれ以降に追加 -----------
    return true;                                   // 移動平均線のクロス判定
}

//+------------------------------------------------------------------+
//| 勝敗のサインを確定する
//| 引数1 シフト数
//+------------------------------------------------------------------+
void winLossCheck(int i) {
//printf("[%d] winLossCheck(%d) in ", __LINE__, i);
   int j, k;
   int counter;

   counter = winCountMaxBar;
   
   if (i > counter) {
      return;
   }
   
   if (Log1Down[i+2] != EMPTY_VALUE) {
      if (iOpen(NULL, 0, i+1) > iClose(NULL, 0, i+1)) {
         OkSign[i+1] = High[i+1] + 5 * Point;
      }
      else  {
         NgSign[i+1] = High[i+1] + 5 * Point;
      }
   }
   if (Log1Up[i+2] != EMPTY_VALUE) {
      if (iOpen(NULL, 0, i+1) < iClose(NULL, 0, i+1)) {
         OkSign[i+1] = Low[i+1] - 5 * Point;
      }
      else {
         NgSign[i+1] = Low[i+1] - 5 * Point;
      }
   }
}

162行目:この関数は足が替わった(次の足に処理が移った)タイミングで1回実行されます。start関数からは82行目で次足に替わったかを判定し、替わったなら次の行のwinLossCheck関数を1回呼びます。
92行目:過去のロウソク足に対してはこちらで勝負判定をおこないます。

いかがでしたか?今回は少し難しかったかも知れませんが上記のイメージ図で描いたイメージをたよりに理解を深めていただければと思います。
さて次回は勝負判定の仕上げ、いよいよ勝率を算出するプログラムを公開していきたいと思います。

コメントを残す

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

CAPTCHA