EAで平均足を使う(サンプルソース)

EA
スポンサーリンク

FXで売買タイミングとして使う平均足の、簡単なMT4のEAサンプルです。

以前ローソク足の情報だけでプラス収支にできるか試したのですがうまくいかず、何とか値動きだけでEAを作れないか試行錯誤した末、平均足を使ってみました。

今回、以下の記事では順張り目線でエントリーしていくサンプルを載せています。

主な構文

iCustom()

【使用例】
iCustom(NULL,0,"Heiken Ashi",0,1);

iCustom()関数で平均足の値を取得しています。

引数は以下の通りです。

  1. 通貨ペア(NULLで当該通貨)
  2. 時間軸(0で当該時間軸)
  3. インジケータ名称(平均足を使う場合”Heiken Ashi”でOKです)
  4. 取得する値(0:平均足High値 1:平均足Low値 2:平均足Open値 3:平均足Close値)
  5. バーシフト
MT4の平均足の名称は”Heikin Ashi”ではなく、”Heiken Ashi”という名称なので注意が必要です。

以下サンプルソースでは、平均足の1つ前と2つ前を比較してエントリー及び決済するようにしています。

ソースコード


//+------------------------------------------------------------------+
//|                                                   HeikinAshi.mq4 |
//|                                    Copyright 2020, mef Software. |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright 2020, mef Software."
#property link      "https://fx-prog.com/"
#property version   "1.00"

input int A_SPREAD = 10;
input double Lots = 0.01;

datetime prevtime;
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
  {
//---
        
//---
   return(INIT_SUCCEEDED);
  }
     
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
int start()
{
//---
int orderPtn=0; //0:何もしない 1:買い 2:売り
int total=0;
    
double ea_order_stop_price=0,ea_order_good_price=0,ea_order_entry_price=0; //ストップロスレート,利確レート,エントリーレート
    
//---
   //新しい足ができた時だけやる
       
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return(0);
   }
    
//平均足の戻り値確認用(※削除可能スペース)
double a = iCustom(NULL,0,"Heiken Ashi",0,1);//平均足High
double b = iCustom(NULL,0,"Heiken Ashi",1,1);//平均足Low
double c = iCustom(NULL,0,"Heiken Ashi",2,1);//平均足Open
double d = iCustom(NULL,0,"Heiken Ashi",3,1);//平均足Close
double e = iCustom(NULL,0,"Heiken Ashi",4,1);//なし
    
Print(" a=",a," b=",b," c=",c," d=",d," e=",e);  
//平均足の戻り値確認用(※削除可能スペース)
       
    
    //***平均足の確認***//
   double haOpen1 = iCustom(NULL,0,"Heiken Ashi",2,1);//1つ前の足の平均足始値
   double haClose1 = iCustom(NULL,0,"Heiken Ashi",3,1);//1つ前の足の平均足終値
       
   double haOpen2 = iCustom(NULL,0,"Heiken Ashi",2,2);//2つ前の足の平均足始値
   double haClose2 = iCustom(NULL,0,"Heiken Ashi",3,2);//2つ前の足の平均足終値
       
   double HaDifference1 = haClose1 - haOpen1;//マイナスなら赤色、プラスなら白色
   double HaDifference2 = haClose2 - haOpen2;//マイナスなら赤色、プラスなら白色
       
   double Open_Day = iCustom(NULL,PERIOD_D1,"Heiken Ashi",2,0);
   double Close_Day = iCustom(NULL,PERIOD_D1,"Heiken Ashi",3,0);
   double Difference_Day = Close_Day - Open_Day;
    
    
//***売買判断箇所***//
   //平均足が陰線から陽線に変わったら買い注文
   if(HaDifference2<0 && HaDifference1>0 && Difference_Day>0){
      orderPtn = 1;
   }
       
   //平均足が陽線から陰線に変わったら売り注文
   if(HaDifference2>0 && HaDifference1<0 && Difference_Day<0){
      orderPtn = 2;
   }
    
    
    //同一方向
    if((orderPtn == 1 || orderPtn == 2) && OrderSelect(OrdersHistoryTotal()-1,SELECT_BY_POS,MODE_HISTORY) == true)
    {
         if(OrderProfit() < 0  && OrderType() == OP_BUY && orderPtn==1){
            if(OrderCloseTime() > iTime(NULL, 0, 3) ){
               orderPtn=0;             
            }
         }
         if(OrderProfit() < 0 && OrderType() == OP_SELL && orderPtn==2){
            if(OrderCloseTime() > iTime(NULL, 0, 2) ){
               orderPtn=0;    
            }
         }      
    }
    
//***決済判断箇所***//
   total=OrdersTotal();
   if(total == 1 ){
       
      bool OrderKekka = OrderSelect(0,SELECT_BY_POS,MODE_TRADES); 
      
      if(HaDifference1<0 && OrderType() == OP_BUY){
         OrderKekka = funcOrder_Close();
      }
      
      if(HaDifference1>0 &&  OrderType() == OP_SELL){
         OrderKekka = funcOrder_Close();
      }
       
   }
//***エントリー箇所***//
   else if(total == 0 && orderPtn > 0)   
   {
      ea_order_stop_price = 0;
      ea_order_good_price = 0;         
    
      //新規注文
      OrderKekka = funcOrder_Send(orderPtn - 1,ea_order_stop_price,ea_order_good_price,0,0);
    
   }
     
     
   return(0);
}
    
    
    
//+------------------------------------------------------------------+
//|【関数】新規注文関数                                                 |
//|                                                                  |
//|【引数】 ea_order_entry_Type:売買(0:買 1:売)                         |
//|【引数】 ea_order_stop_price:損切値  ea_order_good_price:利確値      |
//|【引数】 orderComment:オーダーコメント    ea_order_MagicNo:マジックNo       |
//|                                                                  |
//|【戻値】True:成功                                                   |
//|                                                                  |
//| 
bool funcOrder_Send(int ea_order_entry_Type, double ea_order_stop_price, double ea_order_good_price,int orderComment,int ea_order_MagicNo)
{
    
   int order_resend_num;        // エントリー試行回数
   int ea_ticket_res;           // チケットNo
   int errorcode;               // エラーコード
   double ea_order_entry_price; // エントリーレート
   color order_Color;
   bool kekka;
       
   for( order_resend_num = 0; order_resend_num < 10; order_resend_num++ ) {    // エントリー試行回数上限:10回
    
      if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD){
    
         if(ea_order_entry_Type == OP_BUY){   
            ea_order_entry_price = Ask;               // 現在の買値でエントリー
            order_Color = clrBlue;
         }else if(ea_order_entry_Type == OP_SELL){        
            ea_order_entry_price = Bid;               // 現在の売値でエントリー
            order_Color = clrRed;            
         }
    
         // FXCMでは新規エントリー時にストップ/リミットを設定出来ない。
         ea_ticket_res = OrderSend(   // 新規エントリー注文
            NULL,                 // 通貨ペア
            ea_order_entry_Type,      // オーダータイプ[OP_BUY / OP_SELL]
            Lots,                     // ロット[0.01単位](FXTFは1=10Lot)
            ea_order_entry_price,     // オーダープライスレート
            20,                       // スリップ上限    (int)[分解能 0.1pips]
            ea_order_stop_price,      // ストップレート
            ea_order_good_price,      // リミットレート
            orderComment,             // オーダーコメント
            ea_order_MagicNo,         // マジックナンバー(識別用)
            0,                        // オーダーリミット時間
            order_Color               // オーダーアイコンカラー
            );
      }
                
      if ( ea_ticket_res == -1) {            // オーダーエラー
         errorcode = GetLastError();      // エラーコード取得
    
         if( errorcode != ERR_NO_ERROR) { // エラー発生
            printf("エラー");
         }
    
         Sleep(2000);                                           // 1000msec待ち
         RefreshRates();                                        // レート更新
    
         printf("再エントリー要求回数:%d, 更新エントリーレート:%g",order_resend_num+1 ,ea_order_entry_price);
    
      } else {    // 注文約定
         Print("新規注文約定。 チケットNo=",ea_ticket_res," レート:",ea_order_entry_price);
         Sleep(300);                                           // 300msec待ち(オーダー要求頻度が多過ぎるとエラーになる為)
                
         break;
      }
   }
   return kekka;   
}
    
//+------------------------------------------------------------------+
//|【関数】決済関数                                                     |
//|                                                                  |
//|【引数】 なし                          |
//|                                                                  |
//|【戻値】True:成功                                                   |
//|                                                                  |
//|  
bool funcOrder_Close()
{
   bool kekka,OrderKekka;
   int total,order_resend_num,errorcode;
       
   kekka=false;
   for(order_resend_num = 0; order_resend_num < 100; order_resend_num++ ) {   
      if(MarketInfo(NULL,MODE_SPREAD) < A_SPREAD){
         total=OrdersTotal();
             
         if(total >0 && OrderSelect(0,SELECT_BY_POS,MODE_TRADES)==true){
            if(OrderType() == OP_BUY){
               OrderKekka = OrderClose(OrderTicket(),OrderLots(),Bid,10,Violet);
            }
            else if(OrderType() == OP_SELL){
               OrderKekka = OrderClose(OrderTicket(),OrderLots(),Ask,10,Violet);
            }
                
            if(OrderKekka==false){
               errorcode = GetLastError();      // エラーコード取得
       
               if( errorcode != ERR_NO_ERROR) { // エラー発生
                  printf("決済エラー 要求回数:%d",order_resend_num+1);          
               }
               RefreshRates();
               Sleep(1000); 
            }
            else{
               kekka=true;
               break;
            }
         }
      }else{
          printf(MarketInfo(NULL,MODE_SPREAD) + "スプレッド拡大中");
          Sleep(5000);                                           // msec待ち
          RefreshRates();                                        // レート更新
      }
   }
   return kekka;   
}

エントリータイミング

2つ前の平均足が陰線で1つ前の平均足が陽線で確定したタイミングでロングエントリーします。逆に、2つ前の平均足が陽線で1つ前の平均足が陰線で確定したタイミングでショートエントリーします。

決済タイミング

平均足が陰線で確定したタイミングでロングポジションを決済します。逆に、平均足が陽線で確定したタイミングでショートポジションを決済します。

その他の仕様

設定値は以下の通りです。

  1. A_SPREAD = 10 ⇒ エントリー及び決済時に許容するスプレッドの値です(10 = 10pips)
  2. Lots = 1 ⇒ ロット数です(0.01=1000通貨)

既にポジションがある場合は追加でエントリーしません。

新しい足(バー)が出来た際に1度だけ処理を行います。※1時間足の場合だと1時間に1回処理する

当日日足の平均足も見ています。当日日足の平均足が陽線の場合に限りロングエントリー、日足の平均足が陰線の場合に限りショートエントリーします。

EAレポート結果

※サンプルEAの作り上、始値のみの確認となっています

2005年~2020年の期間の4時間足でバックテスト確認してみました。何とかプラスになりました。エントリータイミングも決済タイミングも平均足しか使っていないので、平均足は相場に対して十分有効だと思います。

ちなみに1分足~1時間足でも確認しましたがマイナス収支でした。ただ、15分足や1時間足はマイナスといっても微マイナス収支だったので、

・平均足の形状をもっと細かく見る(上ヒゲが長い等)

・決済タイミングなどを工夫する

等をすれば、プラス収支に持っていけるんではないかと思います。

 

※ EAのサンプルソースを一覧表にまとめました


※ オススメ記事(EAが使える国内FX業者の一覧)


※ 1からEAの作り方を学びたい人はこちら

コメント

  1. ミナミ ケンイチ より:

    このサンプルコードで勉強しています。

    コード内で
    「 //同一方向」の内容で質問があります。
    このコードが記載されていないと、どんな不具合がありますか?
    最終的に
    OrderPtn=0;としているので注文を出さないようにしている事は理解しました

    • りょう りょう より:

      はじめましてミナミ ケンイチさん。返信が遅れてしまってスミマセン。
      「 //同一方向」の内容については、1つ前のトレード結果をみてロングで損失が発生している場合、再度すぐにロング注文しようとしたら注文を取り消すような仕組みです。ショートも同じ感じです。要は前回負けた場合、すぐに同一方向にまたすぐにエントリーしようとする行為を制限しています。
      なので、記載していなくても動きますが負け続ける事が結構出てくると思います。1度消した状態でバックテストで見てみても面白いと思います!

  2. 藤真一 より:

    こんにちは。EA初心者です。
    勉強させていただこうと思い、サンプルコードをそのままコピーさせていただきコンパイルしてストラテジーテスターにかけましたが、まったくポジションをとりません。
    サンプルコードのままでは動かないのでしょうか?

    • りょう りょう より:

      はじめまして、管理人のりょうです。

      スプレッドが1銭未満でしか新規注文しない設定にしているので、そこで引っかかってる可能性が高いです。
      A_SPREADの設定を10→30等に変更する。又は、ストラテジーテスターのスプレッド値を10未満にして頂きもう1度お試し頂けないでしょうか。

      • 藤真一 より:

        ご返信ありがとうございます。
        スプレッドを変更してみましたが、やはりポジションをとりませんでした。
        通貨ペアや時間足に指定はないですよね?
        他に何か原因は考えられますでしょうか?
        お手数ですが宜しくお願いいたします。

        • りょう りょう より:

          通化ペアや時間足に指定は無いです。

          テスターの操作履歴に何かエラーが出たりしていませんか?正常に動く場合、足が更新されるごとに以下のようなログが出ています。

          2021.12.03 21:03:37.964 2020.01.02 18:30:00 test EURUSD,M15: a=1.1189 b=1.1182 c=1.1189 d=1.1185 e=0

          • 藤真一 より:

            こんばんは。
            ストラテジーテスターの操作履歴の内容が違いますね。。

            エキスパート設定でA_SPREADを50に設定、
            ユーロドル M15、スプレッド5、開始日2021.11.10 終了日2021.12.03 でテストしたところ操作履歴には下記3行が記載されました。

            2021.12.04 01:15:50.125 EURUSD,M15: 1078927 tick events (1632 bars, 1079600 bar states) processed in 0:00:19.047 (total time 0:00:23.156)
            2021.12.04 01:15:31.071 2021.11.10 00:00:00 Heikin_moto inputs: A_SPREAD=50; Lots=0.01;
            2021.12.04 01:15:28.983 TestGenerator: spread set to 5

          • りょう りょう より:

            こんばんは。
            ご回答有難うございます。中々難しいですね。
            もう少しお聞きしたい事があります。面倒そうならスルーして下さい。

            ①他のEAは動作しているか
            ②テスト時のモデルを始値のみにしても事象は同じか
            ③MT4内に「Heiken Ashi」というインジケータは存在するか
            ④テスト時のモデルを全ティックにしている場合、1分足と15分足のヒストリカルデータは存在するか
            ⑤お使いのMT4が結構古い4年以上前とかのものではないか

            ここら辺を見て頂くと原因がわかるかもしれません。
            こういう事象は初めてなので、良ければ今後の参考にさせて頂ければと思います!

  3. 藤真一 より:

    遅くなり申し訳ありません。下記ご確認ください。

    ①他のEAは動作します。
    ②始値のみにしても事象は同じです。
    ③Heiken Ashiは存在します。
    ④1分足と15分足のヒストリカルデータは存在します。
    ⑤MT4のバージョンは、Version build 1350 です。

    2台のPC、複数のMT4でテストしても結果は同じでした。。

    • りょう りょう より:

      返信遅れてしまいすみません。
      うーん、見たところ問題無いですね。
      以下のようにソースコードにPrintを入れて実行して、操作履歴で確認してみるとどんな感じになるとかってわかりますか?

      //—
      //新しい足ができた時だけやる
      Print(“1”);
      if(Time[0] != prevtime){
      prevtime = Time[0];
      }else{
      Print(“2:” + Time[0]);
      return(0);
      }
      Print(“3”);
      //平均足の戻り値確認用(※削除可能スペース)

      • 藤真一 より:

        すみません!別のMT4のメタエディターで作業をしたら問題なく動きました。
        原因はわかりませんが・・。

        お騒がせして申し訳ありませんでした。本当に申し訳ありませんでした。
        今後ともよろしくお願いいたします。

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