【裁量/EA両対応】移動平均のクロスでクローズ(手仕舞い)するEA(ソースコードあり)

移動平均のクロスで手仕舞いするEA MetaTrader4

移動平均のクロスで手仕舞い

移動平均のクロスでクローズ(手仕舞い)するEA

以前コメントにてAutoOrderModifyProに「移動平均のクロスで手仕舞いする機能をつけて欲しい」という要望をいただいたのですが、AutoOrderModifyシリーズにそういったテクニカルによるクローズロジックを付加するのは方向性が異なると思ったのでお断りさせていただきました…

単純なテクニカルで手仕舞いするロジックというのは比較的簡単に作れますし、他のEAと干渉する可能性も低いので別EAとして作ってみることにしました。

もちろん、AutoOrderModifyシリーズと併用できますので、両方稼働すれば「AutoOrderModifyに機能が拡張された」場合と同じ動作になります。

とりあえずソースコード(全文)を以下に掲載します。

ソースコード(全文)

//+------------------------------------------------------------------+
//|                                                  ExitMACross.mq4 |
//|                                 Copyright 2016, TradeAndSoftware |
//|                                                https://tasfx.net |
//+------------------------------------------------------------------+
#property copyright "Copyright 2016, TradeAndSoftware"
#property link      "https://tasfx.net"
#property version   "1.00"
#property strict

enum MAMode
{
   SMA = MODE_SMA,
   EMA = MODE_EMA,
   SMMA = MODE_SMMA,
   LWMA = MODE_LWMA
};

enum MATime
{
   M1 = PERIOD_M1,
   M5 = PERIOD_M5,
   M15 = PERIOD_M15,
   M30 = PERIOD_M30,
   H1 = PERIOD_H1,
   H4 = PERIOD_H4,
   W1 = PERIOD_W1,
   MN1 = PERIOD_MN1
};

enum MAPrice
{
   CLOSE = PRICE_CLOSE,
   OPEN = PRICE_OPEN,
   HIGH = PRICE_HIGH,
   LOW = PRICE_LOW,
   MEDIAN = PRICE_MEDIAN,
   TYPICAL = PRICE_TYPICAL,
   WEIGHTED = PRICE_WEIGHTED
};


input string MagicListExample = "ex)123456,77777,42515";   //MagicListExample(MAX10)
input string MagicList = "";
input MAMode MAMethod = EMA;     //MA Methord
input MATime MATimeFrame = H1;   //MA TimeFrame
input MAPrice MAAppliedPrice = CLOSE;  //MA Applied Price
input int FastMAPeriod = 5;   //Fast MA Period
input int SlowMAPeriod = 20;  //Slow MA Period
input int Slippage = 3;

int MagicListCount = 0;
int Magic[10];

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
   if(MagicList != "")
   {
      MagicListCount = SetStringToArrayList(Magic);
   }
   
   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
//---
   
}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   for(int i=OrdersTotal()-1; i>=0; i--)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
      bool isExistsList = ExistsMagicList(OrderMagicNumber());
      
      if(isExistsList)
      {
         double fastMAPrev = iMA(OrderSymbol(),MATimeFrame,FastMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,2);
         double fastMA = iMA(OrderSymbol(),MATimeFrame,FastMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,1);
         double slowMAPrev = iMA(OrderSymbol(),MATimeFrame,SlowMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,2);
         double slowMA = iMA(OrderSymbol(),MATimeFrame,SlowMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,1);
      
         if(OrderType()==OP_BUY)
         {
            if(fastMAPrev >= slowMAPrev && fastMA < slowMA)
            {
               if(!OrderClose(OrderTicket(),OrderLots(),MarketInfo(OrderSymbol(),MODE_BID),Slippage))
               {
                  Print("OrderClose Error : " + IntegerToString(GetLastError()));
               }
            }
         }
         else if(OrderType()==OP_SELL)
         {
            if(fastMAPrev <= slowMAPrev && fastMA > slowMA)
            {
               if(!OrderClose(OrderTicket(),OrderLots(),MarketInfo(OrderSymbol(),MODE_ASK),Slippage))
               {
                  Print("OrderClose Error : " + IntegerToString(GetLastError()));
               }
            }
         }
      }
   }
}

int SetStringToArrayList(int &list[])
{
   int beginIndex = 0;
   int endIndex = 0;
   int length = 0;
   int count = 0;
   int i = 0;
   for(i=0; i<10; i++)
   {
      endIndex = StringFind(MagicList,",",beginIndex);
      if(endIndex == -1)
      {
         length = StringLen(MagicList);
         list[i] = (int)StringSubstr(MagicList,beginIndex,length);
         break;
      }
      length = endIndex - beginIndex;
      list[i] = (int)StringSubstr(MagicList,beginIndex,length);
      beginIndex = endIndex + 1;
   }
   count = i + 1;
   return count;
}

bool ExistsMagicList(int magic)
{
   if(MagicListCount == 0) return true;
   
   for(int i=0;i<MagicListCount;i++)
   {
      if(magic == Magic[i]) return true;
   }
   
   return false;   
}

MetaEditorにコピー&ペーストしてコンパイルすれば動くはずです。

ファイル名はExitMACross.mq4としていますが、別になんでも構いません。

解説

ゼロから始めるEA開発」の番外編の位置づけで記事を書いていますので、プログラムの解説をしていきます。(ついでにパラメーターの説明も書きます)

定義(enum)

enum MAMode
{
   SMA = MODE_SMA,
   EMA = MODE_EMA,
   SMMA = MODE_SMMA,
   LWMA = MODE_LWMA
};

enumは数字をわかりやすい文字(単語)に置き換えられます。

メリットはプログラムがわかりやすいことと、MT4のパラメーターで使用した場合、enumで定義した文字列がMT4のパラメーター画面で「リスト表示」されることがあります。

利用者に「SMAは0、EMAは1と入力して下さい」と説明するよりもリストでSMA/EMA/SMMA/LWMAの中から選択するほうが良いに決まっています。

SMA = 0,EMA = 1…と定義してもよいのですが、せっかくMT4で定義されている定数があるので今回はそれ(MODE_xxx)を使いました。

TimeFrameも同じです。TimeFrameってなんのこと?という人がいてもM1/M5/M15…とリストがあれば何を設定する項目かわかると思います。

パラメーター

input string MagicListExample = "ex)123456,77777,42515";   //MagicListExample(MAX10)
input string MagicList = "";
input MAMode MAMethod = EMA;     //MA Methord
input MATime MATimeFrame = H1;   //MA TimeFrame
input MAPrice MAAppliedPrice = CLOSE;  //MA Applied Price
input int FastMAPeriod = 5;   //Fast MA Period
input int SlowMAPeriod = 20;  //Slow MA Period
input int Slippage = 3;

MagicListExampleは使い方の説明です。カンマ区切りでマジックナンバーを指定しましょう…という意味で書いています。(無くても動作には影響ありません)

MagicListにはMagicListExampleに書かれているようにカンマ区切りでマジックナンバーを設定します。

裁量トレードを対象にする場合には0を設定して下さい。ちなみに空にすると全てのマジックナンバーを対象にします。

MAMethodは使用するMAの種類、MATimeFrameは使用するMAの時間軸です。

MAAppliedPriceはMAを計算する時に使用する価格です。CLOSEだとCLOSE価格を計算に使用します。

FastMAPeriodは短期MA、SlowMAPeriodは長期MAの本数で、Slippageは許容するスリッページの設定です。

初期化処理

int OnInit()
{
   if(MagicList != "")
   {
      MagicListCount = SetStringToArrayList(Magic);
   }
   
   return(INIT_SUCCEEDED);
}

初期化処理ではSetStringToArrayList関数を呼び出します。

SetStringToArrayList関数は「カンマ区切りで書かれた文字列を、配列に変換する」という処理になっています。

関数が用意されていて一発変換してくれる言語も多いのですが、MQL4では自作する必要があります。

なぜわざわざ配列化しているかというと、プログラムの中で扱いやすいからです。forループで回して1つずつチェックすれば良いだけですからね!

bool ExistsMagicList(int magic)
{
   if(MagicListCount == 0) return true;
   
   for(int i=0;i<MagicListCount;i++)
   {
      if(magic == Magic[i]) return true; ←ここでチェックしています
   }
   
   return false;   
}

戻り値は「カンマ区切りで幾つマジックナンバーが指定されていたか」を返しています。

マジックナンバーが一致するかどうか

   for(int i=OrdersTotal()-1; i>=0; i--)
   {
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false) break;
      bool isExistsList = ExistsMagicList(OrderMagicNumber());

最初のfor文で「オーダーの数だけ」ループします。オーダー(待機注文含む)があればループに入り、1つずつチェックしていきます。

ExistsMagicList関数はそのオーダーのマジックナンバーが、カンマ指定したマジックナンバーリストの中に「存在するか」調べる関数です。存在すればtrue、しなければfalseを返します。

MA算出

      if(isExistsList)
      {
         double fastMAPrev = iMA(OrderSymbol(),MATimeFrame,FastMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,2);
         double fastMA = iMA(OrderSymbol(),MATimeFrame,FastMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,1);
         double slowMAPrev = iMA(OrderSymbol(),MATimeFrame,SlowMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,2);
         double slowMA = iMA(OrderSymbol(),MATimeFrame,SlowMAPeriod,0,(int)MAMethod,(int)MAAppliedPrice,1);

マジックナンバーリストに指定されたマジックナンバーだった場合、MAを計算します。

iMAについては「ゼロから始めるEA開発」の中で解説していますので省略します。

判定

         if(OrderType()==OP_BUY)
         {
            if(fastMAPrev >= slowMAPrev && fastMA < slowMA)
            {
               if(!OrderClose(OrderTicket(),OrderLots(),MarketInfo(OrderSymbol(),MODE_BID),Slippage))
               {
                  Print("OrderClose Error : " + IntegerToString(GetLastError()));
               }
            }
         }

オーダーの種類が買いのオープンポジション(OP_BUY)だった場合、MAクロスの判定をします。

2本前(Prev)の短期MAが長期MA以上で、1本前の短期MAが長期MAより小さい場合には「デッドクロス」と判断して手仕舞いします。売りの場合は逆にします。(売りの場合の説明は省略)

「ゴールデンクロスで買いの手仕舞いをしたい」場合には売りと買いの条件を逆にして下さい。

他にも別のインジケーター(RSIなど)を併用するとか…クロスではなく単純に「短期MAが長期MAを下回ったら」という条件でも良さそうでです。

このあたりはご自身に合うよう作ってみてください。

カンマ文字列から配列に変換する関数

int SetStringToArrayList(int &list[])
{
   int beginIndex = 0;
   int endIndex = 0;
   int length = 0;
   int count = 0;
   int i = 0;
   for(i=0; i<10; i++)
   {
      endIndex = StringFind(MagicList,",",beginIndex);
      if(endIndex == -1)
      {
         length = StringLen(MagicList);
         list[i] = (int)StringSubstr(MagicList,beginIndex,length);
         break;
      }
      length = endIndex - beginIndex;
      list[i] = (int)StringSubstr(MagicList,beginIndex,length);
      beginIndex = endIndex + 1;
   }
   count = i + 1;
   return count;
}

実はAutoOrderModifyで作った関数をまるっと持ってきただけだったりします。

StringFind関数でカンマを探して、カンマより前に書いてある文字を抜き出して配列に格納。

最後だった場合(endIndex == -1)は残り全部を配列に入れています。

このあたりの記述は難しければ放置しても良いかと…。

この関数は、別のEAなどでも使い道は色々あると思います。

まとめ

チャートに設置するだけで、口座の全オーダーに対して機能しますので、裁量もEAも関係なく適用できます。

適用すると良くないEAがある場合には、適用したいEAや裁量のマジックナンバーをカンマ区切りで指定して下さい。

なお、「設置したチャートの通貨ペアや時間軸」は無関係です。適当なチャートに設置して下さい。

※ご利用の際はデモ口座などで良くご確認の上ご利用下さい。ざっと動作確認はしていますが動作保証はいたしかねます。

※不具合等ございましたら、コメント・メール等でご連絡下さい。

以上、「移動平均のクロスでクローズ(手仕舞い)するEA」の内容と解説でした。

コメント

  1. furukawa より:

    お聞きしたい事があります。
    MT4標準インジのMAを、右へ表示移動させたものと、
    ノーマルMA(同一パラメーター)のクロスで、シグナルを出したいのですが、可能でしょうか?

    また、EA作成も可能でしょうか?

    furukawa

    • admin より:

      >MT4標準インジのMAを、右へ表示移動させたものと
      標準MAのパラメーター「表示移動(Shift)」で移動しませんか?

      >ノーマルMA(同一パラメーター)のクロスで、シグナルを出したいのですが、可能でしょうか?
      標準ではありません。検索すれば出てくるのでは‥?
      このあたりとか‥。
      http://mt4indicator.net/blog/archives/88

      EA作成のご依頼はお引き受けしておりません。
      「EA作成代行」等で検索するといろいろ出てくると思います。

  2. 台所 より:

    はじめまして、こんばんわ
    私はとんでもないEA初心者です。
    プログラムという高度な技術を学んだこともありません。
    主様のわかりやすいご説明かなりわかりやすいです。
    上記の全文をコピーペイスト致しましたが、
    149行目 16列
    141行目  1列
    83行目  27列

    エラーが出てしまいます
    恐らく簡単なエラーだと思いますが、
    プログラムをした事のない私では簡単なエラーすら気づけません。
    どうかお力を貸していただけませんでしょうか?

    今週月曜日に「FXメタトレーダー実践プログラミング」という本を買い 今からプログラミングを学ぼうと思います。

    またもう一つご質問ですが、こちらのプログラミングはma crossで決済ですが、同時にma crossでエントリーは可能なのでしょうか?

    どうぞよろしくお願い致します。

    • admin より:

      すみません。コメントに気づかず、ずっと放置してしまいました‥。

      改めてコピーペイストしてみましたが、エラーはありませんでした。
      エラー箇所を見るとExistsMagicList関数が問題のようです。
      コピー時にミスしていませんか?
      たとえば最後の } をコピーしていなかった‥とか、そのようなことではないかと思います。

      もちろん、エントリーも可能です。OrderCloseの代わりにOrderSendを使用します。

  3. 板垣 より:

    お世話になっております。
    使用しているEAの決済機能がイマイチな為、こちらのEAを使用させて頂いております。
    そこでいくつか質問があります。

    1 エントリーは使用しているEA、クローズのみ移動平均線のデッドクロス、ゴールデンクロスで使用したいのですが、こちらのEAの移動平均線のパラメーター設定でゴールデンクロスが確認されるとロングエントリーしてしまい、1秒後に決済。それを何度も繰り返し行っています。デッドクロスの場合も同じです。

    ちなみにロウソク足が長期移動平均線にタッチ、もしくはクロスした時点でクローズしたいため、短期のMAは1、長期は5で設定しています。このパラメーター設定が原因なのでしょうか?

    2 設置したチャートの通貨ペアや時間軸は無関係で口座の全オーダーに対して適用されるとあります。
    例えばドル/円をロングでエントリー。移動平均線のデッドクロスでクローズしたい。この場合にユーロ/円やポンド/ドル等、全く関係ない通貨ペアのチャートにこちらのEAをセットした場合にもドル/円のポジションがクローズされるのでしょうか?
    それともドル/円のポジションをクローズしたい場合はドル/円のチャートにセットするべきなのでしょうか?

    大変素晴らしいEAなので是非とも活用したいです。返信宜しくお願い致します。

  4. emija より:

    確認が遅くなりまして申し訳ございません。

    1.についてはエントリー側の問題と思われます。
    当EAはクローズ条件に達した場合にクローズするだけです。
    クローズした後に、エントリー側のEAがエントリーしているので、またクローズ処理を実行しているのだと思います。
    エントリー側のEAでエントリーしないように設定や改修をしないと、どうにもならないのではないでしょうか‥。

    2.はソースコードをみるとOrderSymbol()でエントリーした通貨ペアを取得、その通貨ペアで移動平均の判定やクローズをしているので、どの通貨ペアに設定してもドル円でエントリーしたのであればドル円の移動平均で判定してクローズすると思います。

  5. 名無し より:

    お世話になります。
    過去のコメント欄にもあるのですが、MAクロスでクローズではなく、MAクロスでエントリーがしたい場合、ソースコードのどの欄を編集すればいいのでしょうか?プログラミングの知識は全く皆無なのでご教授お願い致します

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