2016年1月6日水曜日

[MT4インジケータ]平均足を漸進計算で上位足を表示する。チャート版




ここ最近、MQLの入門記事ばかりでしたので、たまにはインジケータを。

漸進マルチタイムフレーム平均のチャート表示版です。

1時間足をベースに取引すると仮定します。その際4時間足の平均足を参考にすると、高値安値の発生が時間によって影響時間が異なるという問題が発生します。

例えば、4時間足で、高値が、0時~1時の間に発生したとします。その場合、その高値はその後3時間平均足に大きく影響します。ところが、3時~4時の間に発生すると、その1時間後には次の足へ移動してしまうため、影響時間は短くなります。

このように、上位時間足を参考にした場合、ある時間でまとめられてしまうため、影響時間が大きく異なる問題が発生してしまいます。過去のデータを参考にする場合には、まとめることがノイズ成分を除去することにつながるため利点が大きいのですが、現在値として参考にしようとするとバーの形成時間によって判断が一定しないという問題が発生します。

そこで、漸進マルチタイムフレームの考え方となります。これは、計算対象の足から必ず指定時間前までの値をとることによって、高値安値発生の影響時間を一定に保つ効果があります。

問題点としては、必ず最大時間の影響を与えるため判断が遅くなるということです。
例として、1時間足に16時間足で計算した平均足を表示します。一度高値が発生すると以後16本影響を及ぼす状況がわかるかと思います。平均足のチャートとしても見た目的に微妙ですよね。

少し触った感じでは、表示時間足の2倍~4倍までの平均足計算が実用的な範囲に思われます。

(追記)
時間の経過において高値安値の影響を減衰させる改良版を作成しています。
http://mt4program.blogspot.jp/2016/01/mt4_20.html

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

ソースコードはこちらから。

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
にほんブログ村 為替ブログへ

2016年1月4日月曜日

[MQL超入門] その007 変数の型

さてさて、変数について、もう少し掘り下げてみましょう。

変数というのは値が変化する名前の付いた箱のようなものと説明しました。

MQLでは箱の種類がいくつか存在します。
代表的なものを説明します。

■int型
- 2147483648~2147483647(±約20億)の整数値だけ代入可能な型です。
MQLでは主に配列の添え字やfor文の回数を制御するために使用します。

■double型
小数点を含む値を代入することが可能です。MQLでは主に価格情報や計算結果で使用します。

さて、int型とdouble型ですが、最大の違いは割り算です。
・[MQL超入門] その002 最初の一歩。Hello world!
http://mt4program.blogspot.jp/2015/12/mql-002-hello-world.html
で作成したプログラムを少し改造して、intとdoubleの割り算の違いを見てみましょう。

//+------------------------------------------------------------------+
//|                                                         Test.mq4 |
//|                        Copyright 2015, MetaQuotes Software Corp. |
//|                                             https://www.mql5.com |
//+------------------------------------------------------------------+
#property copyright "Copyright 2015, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
#property strict
//+------------------------------------------------------------------+
//| Script program start function                                    |
//+------------------------------------------------------------------+
void OnStart()
  {

   int iX = 1;
   int iY = 2;
   
   int iZ = iX / iY;

   MessageBox(iZ);
   
   double dX = 1;
   double dY = 2;
   
   double dZ = dX / dY;

   MessageBox(dZ);

  }
//+------------------------------------------------------------------+

画面に0と表示されたメッセージと、0.5と表示されたメッセージが出てくると思います。
このようにint同士の割り算では、小数点以下は切り捨てとなります。

思わぬ切り捨てが発生して予期しない計算式とならないように注意が必要です。

■string型
文字列型です。MQLでは文字列の定数は""で囲んで宣言しますが、宣言した文字列を代入するための変数です。MQLでは主にメッセージボックスの文字列や、インジケータの情報を表示するための文字列として使用します。

string message = "あいうえお";
MessageBox(message);

■bool型
論理型と呼ばれる、trueかfalseの2値のみをが入る値です。これは、まだ説明していないif文と呼ばれる条件によって処理が変わる制御で使用します。

bool isRun = true;

if( isRun )
{
   // 実行時処理
}
else
{
   // 未実行時処理
}

■datetime型
日時が設定可能な型です。現在時刻や、チャート上のバーの時間などを取得する際に使用します。日付関数と呼ばれる日付を操作する関数群とセットで利用します。少々説明が長くなりますので、別の機会を設けて説明します。ここでは日付を扱うための専用の変数があるんだなぁぐらいで覚えておいてください。

datetime now = TimeCurrent();
MessageBox(TimeToStr(now));


■uint型
0~4294967295(0~約40億)までの正の整数値のみが代入可能な特殊な型です。負の値をとりたくない場合に使用します。uint型では、引き算に特徴が表れます。

・MT4プログラムの小ネタ GetTickCount()とuintの引き算とOS再起動
http://mt4program.blogspot.jp/2015/10/mt4-gettickcountuintos_8.html

でも少し書いてありますが、減算しても、-の値になりません。マイナス分が最大値である4294967295から引かれた値に1足した値となります。

例えばこんな感じです。

uint uX = 0;
uint uY = 4294967295;
 
uint uZ = uX - uY;
MessageBox(uZ);

//1と表示されます。

次の回へ


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