2016年1月5日火曜日

[MQL超入門] その008 if文で分岐しよう。

年明けから世界の株式が大暴れで大変ですね。

さてさて、超入門ということで、プログラムを作るうえで、この程度は必要と思われる内容を挙げています。今回は条件分岐について記述したいと思います。

if文というやつです。
判定式と呼ばれる数式とセットで使います。

記述方法は次の通りです。

if( 判定式 )
{
   判定式が真の時
}
else
{
   判定式が偽の時
}

判定式というのは、結果が真か偽のどちらかになる式のことをいいます。

例えばx < yといった比較式が代表例です。
比較式の中で、よく使う下の式を覚えましょう。

a < b  
aよりbが大きい

a > b
aよりbが小さい

a <= b
bがa以上

a >= b
bがa以下

a == b
aとbが等しい

また、条件式同士を結ぶ論理式というのも使用します。
主に使うのは次の二つです。

a < b && c < d
aよりbが大きく[かつ]cよりdが大きい
二つの条件が共に真の時、真になります。

a < b || c < d
aよりbが大きく[もしくは]cよりdが大きい
二つの条件のどちらかが真の時、真になります。

これらと()を組み合わせて判定式を組み立てます。

とりあえず、なれるということで、とりあえずやってみましょう。
[MQL超入門] その006 とりあえずやってみよう!簡単インジケータ作成2
http://mt4program.blogspot.jp/2015/12/mql-006-2.html

で作成したインジケータをさらに修正してみます。
OnCalculateの部分を修正します。前回同様に赤字の部分を修正してみましょう。

//+------------------------------------------------------------------+
//| 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[])
  {
//---

   for( int i = rates_total - prev_calculated - 1; i >= 0; i = i - 1 )
   {
      double volumeTotal = 0 ;
      int count = 0 ;
      for( int j = i; j < i + 21 && j < rates_total - 1; j = j + 1)
      {
         count = count + 1;
         volumeTotal = volumeTotal + tick_volume[j];
      }
      if( count > 0 )
      {
         volumeTotal = volumeTotal / count / 2;
      }
   
      if( tick_volume[i] >= volumeTotal )
      {
         Label1Buffer[i] = (high[i] + low[i]) / 2;
      }
      else
      {
         Label1Buffer[i] = Label1Buffer[i + 1];
      }
   }
   
//--- return value of prev_calculated for next call
   return(rates_total - 1);
  }
//+------------------------------------------------------------------+


さて、早速実行して、前回と比べてみてください。

・図008.01 一定ボリュームがあるときだけ、中央値を更新する。



一部分、中央値が更新されない区間が出てきたと思います。
過去21本の取引量平均の半分を超えていない場合、ラインを更新しないという処理を追加してみました。一挙に本格的なプログラムになってきましたね。

余談ですが、コメントが無いため、プログラムの中で何をしているのかわかりにくいですね。
ちょっとコメントも入れてみましょう。

//+------------------------------------------------------------------+
//| 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[])
  {
//---

   for( int i = rates_total - prev_calculated - 1; i >= 0; i = i - 1 )
   {
      // 過去21本の取引量平均を求める。
      // ただし、チャートの最大本数を超える場合は、そこまでの計算とする。
      double volumeTotal = 0 ;
      int count = 0 ;
      for( int j = i; j < i + 21 && j < rates_total - 1; j = j + 1)
      {
         count = count + 1;
         volumeTotal = volumeTotal + tick_volume[j];
      }
      // 0除算対策で、count値が0より大きい場合のみ計算する。
      if( count > 0 )
      {
         volumeTotal = volumeTotal / count / 2;
      }
   
      // 一定以上のボリュームを伴っている場合のみ、チャート更新する。
      if( tick_volume[i] >= volumeTotal )
      {
         Label1Buffer[i] = (high[i] + low[i]) / 2;
      }
      else
      {
         Label1Buffer[i] = Label1Buffer[i + 1];
      }
   }
   
//--- return value of prev_calculated for next call
   return(rates_total - 1);
  }
//+------------------------------------------------------------------+

このようにコメントを入れておくと、あとから見たときに何してたっけ?ってわかりやすいかなと思うのですが、皆さまどうですか?ちなみに会社では、まずコメントから書きなさいと新人には指導しています。

変数と、繰り返し制御文、if制御文を勉強したことで、自分の考えをプログラムとして作る基礎が出来上がりました。ここまでできると後は実際にあれこれやってみる段階になります。追加で必要な知識は、具体的に作りながら覚えていきましょう。

ということで、関数とかプログラムを作るうえで追加で必要な知識はやりながら覚えていくとして、次からはMQLならではの特徴を説明しながら掘り下げていきます。

まずインジケータからです。今までは説明を省いていましたが、OnCalculateについて詳しく説明したいと思います。

次の回へ

※追記2018.10.31 どうも2重ループで皆様戸惑うようです。
まず、MT4の仕様としてOnCalculateの引数として渡ってくるclose[]などの配列は0番目の要素が最新の値(一番右端バーの値)です。
close[0];  //最新の値

そして、この配列にはrates_total数要素があります。
C言語の文法に従って、最初の要素は0で指定しますから最後の要素はrates_total-1となります。
close[rates_total - 1]; //最も古い値

最初のループ
for( int i = rates_total - prev_calculated - 1; i >= 0; i = i - 1 )
{
....
}
は、iの値を、未計算分の一番大きな値から0までiを1ずつ減らしながらループする。

という形になります。

では、その中にもう一つループを書くとどうなるでしょうか?

for( int i = rates_total - prev_calculated - 1; i >= 0; i = i - 1 )
{
      for( int j = i; j < i + 21 && j < rates_total - 1; j = j + 1)
      {
      }
}

少し難しいですね。まず、中のループはiの値毎に動きます。iが0の時を考えてみるとわかりやすいです。
iが0の時、jは0からj < 0 + 21の条件で、jが1ずつ上がっていくループになります。
j=0,1,2,....20って感じです。その場合、次のように処理を書いてあると結果は。。

      double volumeTotal = 0 ;
      int count = 0 ;
      for( int j = i; j < i + 21 && j < rates_total - 1; j = j + 1)
      {
         count = count + 1;
         volumeTotal = volumeTotal + tick_volume[j];
      }

結果は、countは21、volumeTotalはtick_volume[0]~tick_volume[20]を足した値です。
i=1の時には、jは1からj < 1 + 21の条件となります。結果はcountは21、volumeTotalはtick_volume[1]~tick_volume[21]を足した値です。

ループ終了条件がj < i + 21 && j < rates_total - 1となっている理由ですが、配列の最大数がrates_total - 1です。単純に j < i + 21だけだと、iの値が rates_total-21を超えてくると、配列に存在しない値を見ようとしてエラーになってしまいます。

例 rates_total=100で、i=80の時、
j は80開始 j < 80 + 21で条件終了なので、jは80~100までの値をとります。
配列最後の値はtick_volume[tick_volume - 1]=tick_volume[99]です。
処理の最後にtick_volume[100]を読もうとしてエラーになってしまいます。

これを防止するのに条件を追加しています。

※蛇足
j < rates_totalが条件的には正しいですが、いつも隣の要素まで見ることが多いので安全マージンとして癖で-1って入れてしまっています。(汗




ブログランキングにご協力よろしくお願いします。m(._.)m
にほんブログ村 為替ブログへ