MT4のEAソース公開「移動平均線のゴールデンクロス、デッドクロス」編

※本ページはプロモーションが含まれています

トレードやったことがある方ならば、誰しもが一度はやった事がある移動平均線のクロスでのエントリー。MQL4でEAを作ってみました。やはり限界がありますね。めちゃ損します。ソースも公開しているので、いい感じで工夫点あれば、よかったらコメントで教えてください。

目次

MQL4での移動平均線のクロス判定

クロスの判定は簡単です。以下の図のように、移動平均線のクロスは1本前の移動平均線と現在の移動平均線の数値が入れ替わります。この大小を比較すればOKです。

MQLで判定すると、以下のようになります。

//単純移動平均線
double ma_20   = iMA(NULL,PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE,0); //20SMA
double ma_20_1 = iMA(NULL,PERIOD_CURRENT,20,0,MODE_SMA,PRICE_CLOSE,1); //1本前の20SMA
double ma_40   = iMA(NULL,PERIOD_CURRENT,40,0,MODE_SMA,PRICE_CLOSE,0); //40SMA
double ma_40_1 = iMA(NULL,PERIOD_CURRENT,40,0,MODE_SMA,PRICE_CLOSE,1); //1本前の40SMA

//ゴールデンクロス
if(ma_40_1 > ma_20_1 && ma_40 < ma_20){
    //ロング処理
}
//デッドクロス
else if(ma_40_1 < ma_20_1 && ma_40 > ma_20){
    //ショート処理
}
ざっくりと説明すると、上記のような処理をOnTickに入れます。売買のルーチンがしっかりと作成できていれば結構シンプルです。

ソースコード

このままリアル口座で実施しても損します。あくまでも参考にしてください。

移動平均線Aと移動平均線Bのゴールデンクロスでロング、デッドクロスでショートのソースです。パラメーターで移動平均線値を入れれるようにしました。

#include <stdlib.mqh> 

#property copyright "Copyright 2022, Yanoteck"
#property link      "https://elite-collections.com/"
#property version   "0.1"
#property strict

#define MAGIC 202211292329
 
extern double Lots = 0.01;       //lots
extern int Slippage = 30;        //slipage
extern double Profitrate = 50;   //Take Profit 値幅(pips)
extern double SLrate = 25;       //Stop Loss 損切り(pips)

extern int ma_A = 20; //移動平均線A
extern int ma_B = 40; //移動平均線B
 


//グローバル変数
//大量エントリー回避用(チケット番号)
int g_ticket_no = 0;

//時間足1本目だけ実施したい
int g_Volume = 0;
 
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
  {
//---
   g_ticket_no = 0;
   g_Volume = 0;
 
//---
   return(INIT_SUCCEEDED);
  }
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
//---
   g_ticket_no = 0;
   g_Volume = 0;

  }
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
  {
   //単純移動平均線
   double sma_A   = iMA(NULL,PERIOD_CURRENT,ma_A,0,MODE_SMA,PRICE_CLOSE,0); //SMA
   double sma_A_1 = iMA(NULL,PERIOD_CURRENT,ma_A,0,MODE_SMA,PRICE_CLOSE,1); //1本前のSMA
   double sma_B   = iMA(NULL,PERIOD_CURRENT,ma_B,0,MODE_SMA,PRICE_CLOSE,0); //SMA
   double sma_B_1 = iMA(NULL,PERIOD_CURRENT,ma_B,0,MODE_SMA,PRICE_CLOSE,1); //1本前のSMA
   
//---
   if (Volume[0] < g_Volume) {
      //バーの始まりで1回だけ処理したい内容
 
      //オーダーの有無とフラグのチェック
      if(g_ticket_no != 0){   //エントリー中?
   
         //オーダー情報なし。途中でSL、TPで強制決済した
         if(OrdersTotal() == 0){
         
            //グローバル変数の初期化
            g_ticket_no = 0;
         }
      }
      
      //バーカウント初期化
      g_Volume = 0;

   }else{
   
      //ゴールデンクロス
      if(sma_B_1 > sma_A_1 && sma_B < sma_A){
          //ロング処理
          My_Long(Lots,Slippage,SLrate,Profitrate,MAGIC);
      }
      //デッドクロス
      else if(sma_B_1 < sma_A_1 && sma_B > sma_A){
          //ショート処理
          My_Short(Lots,Slippage,SLrate,Profitrate,MAGIC);
      }
      
      //今のVolume[0]をセット
      g_Volume = Volume[0];
 
   }
  }
 
//+------------------------------------------------------------------+
//| ロング
//| OP_BUYのエントリー。リトライあり
//| 引数: my_lot = ロット
//|       my_slipage = スリッページ
//|       my_sl = 損切り
//|       my_tp = 利益
//|
//| 戻値: チケット番号
//+------------------------------------------------------------------+
int My_Long(double my_lot,int my_slipage , double my_sl, double my_tp, int my_magic){
 
   int ticket_no=0;  //ticket no
   int errorcode=0;  // エラーコード
 
   double losscut_normalize=0;
   double profit_normalize=0;
   double losscut_rate=0;
   double profit_rate=0;
 
   if(g_ticket_no != 0){
       return -1;
   }

   // ロスカット価格
   losscut_rate  = Ask - ( 10 * Point() * my_sl); 
 
   // 決済価格
   profit_rate   = Ask + ( 10 * Point() * my_tp); 
 
 
   //正規化
   losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS)));
   profit_normalize  = NormalizeDouble(profit_rate,int(MarketInfo(Symbol(),MODE_DIGITS)));
 
 
   //ロングエントリー処理
   ticket_no = OrderSend(Symbol(),OP_BUY,my_lot,Ask,my_slipage,losscut_normalize,profit_normalize,"Buy order",my_magic,0,clrGreen);
   printf("OrderSend OP_BUY , price=%f , sl=%f , tp=%f",Ask,losscut_normalize,profit_normalize);
 
   if(ticket_no < 0){
      errorcode = GetLastError();      // エラーコード取得
      printf("Send Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode));
 
      //再設定
      losscut_rate  = Ask - ( 10 * Point() * my_sl); 
      profit_rate   = Ask + ( 10 * Point() * my_tp); 
      losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS)));
      profit_normalize  = NormalizeDouble(profit_normalize,int(MarketInfo(Symbol(),MODE_DIGITS)));
 
      //オーダー送信と同時に損切り、利益設定エラー対策
      ticket_no = OrderSend(Symbol(),OP_BUY,my_lot,Ask,my_slipage,0,0,"Buy order",my_magic,0,clrGreen);
      printf("Resend OrderSend OP_BUY , price=%f , sl=%f , tp=%f",Ask,losscut_normalize,profit_normalize);
 
      //オーダー修正
      LimitStop_Set(ticket_no,OP_BUY,clrGreen);
 
   }else {
      printf("Send Done. Ticket NO = %d",ticket_no);
   }

   g_ticket_no = ticket_no;
   
   return ticket_no;
}
 
//+------------------------------------------------------------------+
//| ショート
//| OP_SELLのエントリー。リトライあり
//| 引数: my_lot = ロット
//|       my_slipage = スリッページ
//|       my_sl = 損切り
//|       my_tp = 利益
//|
//| 戻値: チケット番号
//+------------------------------------------------------------------+
int My_Short(double my_lot,int my_slipage , double my_sl, double my_tp, int my_magic){
 
   //ticket no
   int ticket_no=0;
   int errorcode=0;               // エラーコード
 
   double losscut_normalize=0;
   double profit_normalize=0;
   double losscut_rate=0;
   double profit_rate=0;

   if(g_ticket_no != 0){
       return -1;
   }
 
   // ロスカット価格
   losscut_rate  = Bid + ( 10 * Point() * my_sl); 
 
   // 決済価格
   profit_rate   = Bid - ( 10 * Point() * my_tp); 
 
 
   //正規化
   losscut_normalize = NormalizeDouble(losscut_rate,Digits());
   profit_normalize  = NormalizeDouble(profit_rate,Digits());
 
 
   //shortエントリー処理
   ticket_no = OrderSend(Symbol(),OP_SELL,my_lot,Bid,my_slipage,losscut_normalize,profit_normalize,"Sell order",my_magic,0,clrRed);
   printf("OrderSend OP_SELL , price=%f , sl=%f , tp=%f",Bid,losscut_normalize,profit_normalize);
 
   //エラー?
   if(ticket_no < 0){
      errorcode = GetLastError();      // エラーコード取得
      printf("Send Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode));
 
     // 再設定
      losscut_rate  = Bid + ( 10 * Point() * my_sl); 
      profit_rate   = Bid - ( 10 * Point() * my_tp); 
      losscut_normalize = NormalizeDouble(losscut_rate,int(MarketInfo(Symbol(),MODE_DIGITS)));
      profit_normalize  = NormalizeDouble(profit_normalize,int(MarketInfo(Symbol(),MODE_DIGITS)));
 
      //オーダー送信と同時に損切り、利益設定エラー対策
      ticket_no = OrderSend(Symbol(),OP_SELL,my_lot,Bid,my_slipage,0,0,"Sell order",my_magic,0,clrRed);
      printf("Resend sOrderSend OP_SELL , price=%f , sl=%f , tp=%f",Bid,losscut_normalize,profit_normalize);
 
      //オーダー修正
      LimitStop_Set(ticket_no,OP_SELL,clrRed);
 
 
   }else {
      printf("Send Done. Ticket NO = %d",ticket_no);
   }

   g_ticket_no = ticket_no;
   return ticket_no; 
}
 
//+------------------------------------------------------------------+
//| エントリー中のポジションのリミット・ストップを変更
//|
//| 引数 int in_ticket_no = Ticket no
//|     int buysell_type = OP_SELL or OP_BUY
//|     color mycolor = 表示色
//+------------------------------------------------------------------+
void LimitStop_Set( int in_ticket_no, int buysell_type , color mycolor) {
 
    int    modify_resend_num;       //変更試行回数
    bool   modify_ret;              //変更判定
    int    errorcode;               //エラーコード 
//    bool   selbool;
    double limit_rate,stop_rate;
    int    my_ticket_no;
 
    // オーダー中のチケット選択(チケットNo指定)
//    selbool = OrderSelect(in_ticket_no, SELECT_BY_TICKET); 
 
    if (buysell_type == OP_BUY){
       limit_rate = OrderOpenPrice() + ( 10 * Point() * Profitrate);
       stop_rate  = OrderOpenPrice() - ( 10 * Point() * SLrate);
     }else{
       limit_rate = OrderOpenPrice() - ( 10 * Point() * Profitrate);
       stop_rate  = OrderOpenPrice() + ( 10 * Point() * SLrate);
    }
 
    //リミット価格のNormalize(正規化)
    limit_rate = NormalizeDouble(limit_rate , Digits() );
    
    //ストップロス価格を正規化
    stop_rate  = NormalizeDouble(stop_rate  , Digits() );
 
    //オーダーチケット
    my_ticket_no = OrderTicket();
 
    //オーダーの修正
    for( modify_resend_num = 0; modify_resend_num < 30; modify_resend_num++ ) {
        modify_ret = OrderModify(
                                 my_ticket_no,      // チケットNo
                                 OrderOpenPrice(),  // 注文価格
                                 stop_rate,            // ストップロス価格
                                 limit_rate,           // リミット価格
                                 OrderExpiration(), // 有効期限
                                 mycolor               // 色
 
                     );
 
        printf("OrderModify ticket_no:%d , sl=%f , tp=%f",my_ticket_no , stop_rate,limit_rate);
 
        if ( modify_ret == false ) {             // 注文変更拒否
            Sleep(300);                         // 300msec待ち
            errorcode = GetLastError();        // エラーコード取得
            printf( "[%d]Modify Error! error_code:%d ,detail:%s ",
                                        modify_resend_num+1, errorcode ,  ErrorDescription(errorcode));
        } else {                                 // 決済注文約定
            Print("Done. Ticket NO=",in_ticket_no);
            break;
        }
    }
}
 
//+------------------------------------------------------------------+
//| ポジション強制決済  Long
//| 引数 int my_magic = マジックナンバー
//+------------------------------------------------------------------+
void My_force_close_long(int my_magic){
   int i;
   int my_ticket_no;
   double my_lots;
   double my_price = NormalizeDouble(Ask,int(MarketInfo(Symbol(),MODE_DIGITS)));
   bool chk;
   int errorcode;
 
   for(i=0;i<OrdersTotal();i++){
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false){
         break;
      }
 
      if(OrderMagicNumber() != my_magic || OrderSymbol() != Symbol()){
         continue;
      }
 
      my_ticket_no = OrderTicket();
      my_lots = OrderLots();
      
      //買いポジションのチェック
      if(OrderType() == OP_BUY){
         chk = OrderClose(my_ticket_no,my_lots,my_price,Slippage,White);
         printf("OrderClose ret=%d , ticket_no=%d , lots=%f , BUY price=%f",chk,my_ticket_no,my_lots,my_price);
         
         if(chk == False){
            errorcode = GetLastError();      // エラーコード取得
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode));
         }else{
            printf("OrderClose Done!");
         }
         break;
      }

      break;
   }
   g_ticket_no = 0;
}
 
//+------------------------------------------------------------------+
//| ポジション強制決済 short
//| 引数 int my_magic = マジックナンバー
//+------------------------------------------------------------------+
void My_force_close_short(int my_magic){
   int i;
   int my_ticket_no;
   double my_lots;
   double my_price = NormalizeDouble(Ask,int(MarketInfo(Symbol(),MODE_DIGITS)));
   bool chk;
   int errorcode;
 
   for(i=0;i<OrdersTotal();i++){
      if(OrderSelect(i,SELECT_BY_POS,MODE_TRADES) == false){
         break;
      }
 
      if(OrderMagicNumber() != my_magic || OrderSymbol() != Symbol()){
         continue;
      }
      
      my_ticket_no = OrderTicket();
      my_lots = OrderLots();
 
      //売りポジションのチェック
      if(OrderType() == OP_SELL){
         chk = OrderClose(my_ticket_no,my_lots,my_price,Slippage,White);
         printf("OrderClose ret=%d , ticket_no=%d , lots=%f , SELL price=%f",chk,my_ticket_no,my_lots,my_price);
 
         if(chk == False){
            errorcode = GetLastError();      // エラーコード取得
            printf("OrderClose Error! error_code:%d , detail:%s ",errorcode , ErrorDescription(errorcode));
         }else{
            printf("OrderClose Done!");
         }
 
         break;
      }

      break;
   }
   g_ticket_no = 0;
}

前提

肝心な部分はOnTick内だけです。うちのブログで公開しているEAのベースソースを基本として組んでます。売買部分をラッピング化して、利確と損切りをpipsで指定出来るようにしてます。以下の記事にまとめました。

あわせて読みたい
MT4でのEAの作り方。雛形のソースコード MT4のEAづくりで難解なのは、実稼働させた時にエントリーしても、原因不明のエラーコードが返って来ることです。 それを極力避けた、OnTick関数内に、条件式さえ入れれ...

バックテストの結果

半年間(2022/6/1〜2022/11/29)でバックテストしてみました。意外にもプラスですが、問題点だらけです。

レポートの結果は以下のようになりました。プロフィットファクタは1.15、勝率は37.5%

もっと悲惨な結果になるかと思った。もう少し改良すればいい感じになるのでは???。

問題点

とりあえずの問題点は以下のような感じです。

移動平均線が絡まるときエントリー繰り返す

目視で裁量でやってたら、絶対にエントリーしないタイミングです。どこでクロスしたのかもよくわからない絡まり具合ですが、プログラムは正確に判断して、こんな局面でもエントリーしてしまいます。

クロス系のMAでは必ず問題になる現象です。こういう局面はどうするかは考えないといけません。どうすっかなー。何か良いアイディアあったら教えてください。。

【解決済み】1つの足で何度もエントリー利確を繰り返す

OnTickでエントリーする場合の欠点なのですが、1つの足で利確した際には、何度もエントリーしてしまうことです。TP、SLを狭くすると、こんな感じになってしまいます。

2022.11.30 解決済み

ソースは修正しました。具体的には、足が変わった最初だけ処理させるようにしたら無事解決しました。

   //バーの最初だけ処理
   if (Volume[0] < g_Volume) {
      //何か処理
      
      //バーカウント初期化
      g_Volume = 0;

   }else{   
      //エントリー条件とか書く

      
      //今のVolume[0]をセット
      g_Volume = Volume[0];
 
   }

参考にしたブログは以下の記事です。本当に感謝ですねー。

FX情報商材を自作EAで徹底検証!
【MT4】MQL:足が変わった時に1回だけ(バーの始値のみ)処理を行う方法 新しい足が出来た時に1回だけ処理したって時ないですか? 多重エントリーを制御したい 始まり値でエントリーするEAを作りたい バックテストの精度を上げたい 他にもあると...

矢野テックのトレード結果やプログラム使った予測について

パスワードされた記事は、LINEでお友達になってくれた方に公開しています。

また、今ならお友達には矢野テック自作のMT4用のインジケーターをプレゼント中です。

友だち追加

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

仮想通貨botや、MT4のEA開発、インジケーターなどを作ってます。このブログではFXの基礎的な知識から検証のまとめなどを書いていきたいと思ってます。

プログラムや手法のご質問は各記事のコメント欄からお願いします。

なお、トレードの結果などはLINEでお友達になった方に報告してます。ぜひなってください

コメント

コメントする

目次