関数化しておくと便利
前回の記事で、移動平均線(MA)の上昇/下降の判定ロジックを検討しました。
今回は、それをMQL4でプログラミングしてみます。
おさらいですが、MAの上昇/下降の判定ロジックは以下の3つでしたね。
- ある一定期間内で、あるMAがその1つ前のMAより大きい間隔の割合が多い
- ある一定期間内で、最新のMAが最古のMAより大きい
- 最新のMAがその1つ前のMAより大きい(任意)
これらをプログラミングしていくわけですが、その前に、MAの上昇/下降というのは汎用的な条件ですので、今後も使用することが考えられます。ですので、関数として作成しておくのが良さそうです。
MQL4\Includeフォルダの下に私のオリジナル関数を格納するOriginalフォルダを作成済という前提で話を進めますが、MQL4\Include\Originalの下にApplication.mqhというテキストファイルを作成します(Originalフォルダ内に存在する既存のmqhファイルをコピーするのが手っ取り早いです)。
そこに以下を記載します(いきなり全部書いちゃいます!)。
//+------------------------------------------------------------------+ //| Application.mqh | //| Copyright (c) 2016, りゅーき | //| https://autofx100.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright (c) 2015, りゅーき" #property link "https://autofx100.com/" #property version "1.00" //+------------------------------------------------------------------+ //|【関数】移動平均線の上昇/下降を判断する | //| | //|【引数】 IN OUT 引数名 説明 | //| --------------------------------------------------------- | //| ○ aSymbol 通貨ペア | //| ○ aTimeframe 時間枠 | //| ○ aPeriod MAの算出期間 | //| ○ aShowShift MA表示シフト | //| ○ aMethod MAの種類 | //| ┣ MODE_SMA(0) 単純移動平均 | //| ┣ MODE_EMA(1) 指数移動平均 | //| ┣ MODE_SMMA(2) 平滑移動平均 | //| ┗ MODE_LWMA(3) 線形加重移動平均 | //| ○ aAppliedPrice MA算出に使う価格 | //| ┣ PRICE_CLOSE(0) 終値 | //| ┣ PRICE_OPEN(1) 始値 | //| ┣ PRICE_HIGH(2) 高値 | //| ┣ PRICE_LOW(3) 安値 | //| ┣ PRICE_MEDIAN(4) 中央値 | //| =(高値+安値)/2 | //| ┣ PRICE_TYPICAL(5) 代表値 | //| =(高値+安値+終値)/3 | //| ┗ PRICE_WEIGHTED(6) 加重終値 | //| =(高値+安値+2*終値)/4 | //| ○ aGetShift MA取得シフト | //| ○ aJudgeNumber 上昇/下降判断に使うMAの数 | //| ○ aRate 各MA点の上昇/下降の割合(%) | //| ○ aUseStrict より厳しい判断条件を使用するか | //| | //|【戻値】0:MAは横ばい | //| 1:MAは上昇 | //| 2:MAは下降 | //| | //|【備考】なし | //+------------------------------------------------------------------+ int judgeUpAndDownMA(string aSymbol, int aTimeframe, int aPeriod, int aShowShift, int aMethod, int aAppliedPrice, int aGetShift, int aJudgeNumber, double aRate, bool aUseStrict) { double MA_Array[]; // 配列の要素数を決定(変数宣言時に配列の要素数を変数で設定できないため、仕方なく) int elementCount = ArrayResize(MA_Array, aJudgeNumber); if(elementCount <= 1){ Print("judgeUpAndDownMA関数で、引数が正しく設定されていません。 aJudgeNumber = ", aJudgeNumber); return(0); } // ある一定期間内のMAのデータセット for(int i = 0; i < aJudgeNumber; i++){ MA_Array[i] = iMA(aSymbol, aTimeframe, aPeriod, aShowShift, aMethod, aAppliedPrice, aGetShift + i); } // ----------------------------------------------------------------- // ある一定期間内で、あるMAがその1つ前のMAより大きい間隔の割合が多い // ----------------------------------------------------------------- bool plusFlg1 = false; bool minusFlg1 = false; int plusCount = 0; int minusCount = 0; // 間隔の数は点の数より1少ないから、aJudgeNumberから1を引く int distanceNumber = aJudgeNumber - 1; for(i = 0; i < distanceNumber; i++){ double dif = MA_Array[i] - MA_Array[i + 1]; if(dif > 0){ plusCount += 1; }else if(dif < 0){ minusCount += 1; } } double plusRate = (double)(plusCount) / (double)distanceNumber * 100.0; double minusRate = (double)(minusCount) / (double)distanceNumber * 100.0; if(plusRate > aRate){ plusFlg1 = true; }else if(minusRate > aRate){ minusFlg1 = true; } // ----------------------------------------------------------------- // ある一定期間内で、最新のMAが最古のMAより大きい // ----------------------------------------------------------------- bool plusFlg2 = false; bool minusFlg2 = false; int lastIndex = aJudgeNumber - 1; if(MA_Array[0] > MA_Array[lastIndex]){ plusFlg2 = true; }else if(MA_Array[0] < MA_Array[lastIndex]){ minusFlg2 = true; } // ----------------------------------------------------------------- // 最新のMAがその1つ前のMAより大きい(オプション) // ----------------------------------------------------------------- bool plusFlg3 = false; bool minusFlg3 = false; if(aUseStrict){ if(MA_Array[0] > MA_Array[1]){ plusFlg3 = true; }else if(MA_Array[0] < MA_Array[1]){ minusFlg3 = true; } }else{ plusFlg3 = true; minusFlg3 = true; } // ----------------------------------------------------------------- // 上記条件を全て満たした場合にMAが上昇/下降と判断 // ----------------------------------------------------------------- if(plusFlg1 && plusFlg2 && plusFlg3){ int result = 1; }else if(minusFlg1 && minusFlg2 && minusFlg3){ result = 2; }else{ result = 0; } return(result); }
では、解説していきます。まず、関数名はjudgeUpAndDownMAとしました。関数を実行した結果、返ってくる値は0~2の整数値で、1なら上昇、2なら下降、0ならどちらでもないという意味になります。
引数は沢山ありますが、殆どがiMAを呼ぶのに必要なもので、オリジナルな引数としては、後ろ3つのaJudgeNumber、aRate、aUseStrictだけです。
aJudgeNumberは、MAの上昇/下降を判定するためのMAの点数。aRateは、そのMA点の間隔うち、何割が上昇/下降していれば、MAが上昇/下降していると判断するかを表す割合。最後のaUseStrictは、3つの任意の条件を有効にするかどうかのフラグです。
冒頭にある以下のif文はMAの点数が2未満の場合、エラーとして返すための処置です。
if(elementCount <= 1){ Print("judgeUpAndDownMA関数で、引数が正しく設定されていません。 aJudgeNumber = ", aJudgeNumber); return(0); }
iMA関数を呼び出して配列にMA値を格納します。これが、”ある一定期間内”を表すことになります。
あとは、1つ1つの条件をプログラミングしていきます。
plusFlg1~3、minusFlg1~3は、各条件の結果を格納する変数です。trueなら条件が成立したことを表します。
plusFlg1~3が全てtrueなら上昇、minusFlg1~3が全てtrueなら下降というわけです。これが最後の条件となっており、各フラグを用いて、MAの上昇/下降を最終判断しています。
3つ目の条件で、aUseStrictがどういう役割を果たしているかにご注目ください。aUseStrictがtrueの場合は後続に条件式がありますが、falseの場合、無条件で条件成立としていますね。このテクニックは時々使いますので、ぜひここで覚えておきましょう。
ところで、プログラミングに慣れていない人にとって、配列のインデックス(配列内の番号)は混乱しやすいものです。基本的に配列は0から付番しますので、くれぐれもご注意ください。
例えば、N個繰り返したい場合は、0からN-1までということになります(Nまで繰り返すと1個余分になっちゃいます)。
どうしても頭がついていかない場合は、思い切って最初の0番は欠番として使わないという手もあります。場所としては確保しているけど、敢えて使わないわけですね。但し、その場合、要素数はN+1必要になります。この辺はご自身の理解しやすい方法で構いませんが、世の中に出回っているプログラムの殆どは0始まりなので、長い目で見たら0スタートで慣れておいたほうが良いと個人的には思います。
サンプルEA
上記関数を使ったサンプルEAを載せておきます。どんな風に使うかの参考になれば幸いです。
以下のサンプルEAは確定足で判断するようにしています。従って、1本の足が確定したタイミングでのみ稼働するような仕掛けになっていることにもぜひご注目ください。
なお、矢印オブジェクトはチャート上のどこでMAが上昇/下降と判定されたかを示すためのものです。見やすくするためのものなので、無くても構いません。
//+------------------------------------------------------------------+ //| Sample.mq4 | //| Copyright (c) 2015, りゅーき | //| https://autofx100.com/ | //+------------------------------------------------------------------+ #property copyright "Copyright (c) 2015, りゅーき" #property link "https://autofx100.com/" #property version "1.00" //+------------------------------------------------------------------+ //| ライブラリ | //+------------------------------------------------------------------+ #include <stderror.mqh> #include <stdlib.mqh> #include <WinUser32.mqh> #include <Original/Application.mqh> #include <Original/Basic.mqh> #include <Original/DateAndTime.mqh> #include <Original/LotSizing.mqh> #include <Original/OrderHandle.mqh> #include <Original/OrderReliable.mqh> //+------------------------------------------------------------------+ //| EAパラメータ設定情報 | //+------------------------------------------------------------------+ extern string Note01 = "=== General =================================================="; extern int SlippagePips = 5; extern string Note02 = "=== MA Trend Judge ==========================================="; extern string MA_Symbol = NULL; extern int MA_Timeframe = 0; extern int MA_Period = 20; extern int MA_ShowShift = 0; extern int MA_Method = 0; extern int MA_AppliedPrice = 0; extern int MA_GetShift = 1; extern int MA_JudgeNumber = 30; extern bool MA_UseStrict = true; extern double MA_Rate = 70.0; //+------------------------------------------------------------------+ //| グローバル変数 | //+------------------------------------------------------------------+ // 共通 double gPipsPoint = 0.0; int gSlippage = 0; color gArrowColor[6] = {Blue, Red, Blue, Red, Blue, Red}; //BUY: Blue, SELL: Red int gPrvBars = 0; //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { gPipsPoint = currencyUnitPerPips(Symbol()); gSlippage = getSlippage(Symbol(), SlippagePips); gPrvBars = Bars; return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { int currentBars = Bars; // 新しい足を生成した時ではない場合は、スキップ if(currentBars == gPrvBars){ gPrvBars = currentBars; return; } // 移動平均線の上昇/下降の判断 int judgeType = judgeUpAndDownMA(MA_Symbol, MA_Timeframe, MA_Period, MA_ShowShift, MA_Method, MA_AppliedPrice, MA_GetShift, MA_JudgeNumber, MA_Rate, MA_UseStrict); if(judgeType == 1){ Print("移動平均線(", MA_JudgeNumber, "点)で 上昇 と判断"); string name = "up" + currentBars; ObjectCreate(name, OBJ_ARROW, 0, Time[1], High[1] + 20 * Point); ObjectSet(name, OBJPROP_WIDTH, 2); ObjectSet(name, OBJPROP_ARROWCODE, 233); ObjectSet(name, OBJPROP_COLOR, Blue); }else if(judgeType == 2){ Print("移動平均線(", MA_JudgeNumber, "点)で 下降 と判断"); name = "down" + currentBars; ObjectCreate(name, OBJ_ARROW, 0, Time[1], Low[1] + 20 * Point); ObjectSet(name, OBJPROP_WIDTH, 2); ObjectSet(name, OBJPROP_ARROWCODE, 234); ObjectSet(name, OBJPROP_COLOR, Red); } gPrvBars = currentBars; }