MT4で平均足EAを作成|無料MQL4サンプルコード解説

平均足EAのトレードイメージ
EAサンプル・ノウハウ
記事内に商品プロモーションを含む場合があります
スポンサーリンク

はじめに

「平均足を使ったトレンド判定を、自動売買EAとして試してみたい!」
そんな方に向けて、この記事では MT4(MQL4)で動作する平均足ベースの無料EAサンプルコード を紹介します。

平均足は、ローソク足を平滑化してトレンドを視覚的にわかりやすくしたチャートです。
裁量トレードでもよく使われますが、EAに組み込むことで「陽線→陰線の転換で自動的に売買」など、シンプルかつ強力なロジックを実現できます。


📈 平均足とは?

平均足(Heikin Ashi)は、ローソク足の始値・終値を加工して描画するチャートで、次の特徴があります。

  • 陽線・陰線が連続して表示されやすく、トレンドを把握しやすい
  • ローソク足の「ノイズ」を除去して相場の流れを明確にする
  • 実体が大きく、ヒゲが短い足は強いトレンドを示す

👉 裁量トレードでは「陽線が続けば買い」「陰線が続けば売り」といった判断によく活用されています。


EAの概要とポイント

  • MT4標準インジケータ「Heiken Ashi」を iCustom() で取得
  • 陽線から陰線、陰線から陽線への転換を売買条件に採用
  • 日足フィルターを追加して上位足の方向に合わせる
  • TP(利確)・SL(損切り)をパラメータで設定可能
  • 実体とヒゲの比率で「ダマシ」を回避する工夫

エントリーロジック解説

買いエントリー条件

  • 平均足が陰線 → 陽線に転換
  • 日足も陽線方向
  • 実体がヒゲより優位(トレンドの強さあり)

売りエントリー条件

  • 平均足が陽線 → 陰線に転換
  • 日足も陰線方向
  • 実体がヒゲより優位

👉 単純な転換だけでなく、ヒゲの長さと実体の大きさを比較することで「反転サインの精度」を高めています。


⚠️ 注意点

  • レンジ相場では陽線・陰線が頻繁に切り替わり、ダマシが増える
  • 今回はTP/SLは使わないので、パラメータ設定で1000pipsにしています
  • フィルター(移動平均線・ADXなど)を組み合わせるとさらに精度UP
  • マジックナンバーは考慮していません。(このEA単独で動かしてください)

ADXを使ったEAサンプルはこちら


ダウンロード

EAファイルはこちらから👇

“平均足EA” をダウンロード 04_HeikinAshi.ex4 – 3765 回のダウンロード – 13.30 KB

📜 平均足EAのサンプルコード解説

以下にMQL4サンプルコードを掲載します。
そのままコピーしてMetaEditorに貼り付けてコンパイルすれば動作します。

//+------------------------------------------------------------------+
//|                                                04_HeikinAshi.mq4 |
//|                                Copyright © 2020-2025 ぷろぐらむFX |
//|                                             https://fx-prog.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2020-2025 ぷろぐらむFX"
#property link      "https://fx-prog.com/"
#property version   "1.00"
#property strict


input double Lots = 0.01;              //ロット
input int TP = 1000;                   //利確(pips)
input int SL = 1000;                   //損切り(pips)
//***【パラメータ設定↓】平均足***//
input double HEIKIN_SIKII = 0.5;       //ヒゲ比率しきい値
//***【パラメータ設定↑】平均足***//

string Ea_Name = "HeikinAshi";         //オーダーコメントに使用

double PipValue;                       //ブローカー桁数に対応した Point 補正
datetime prevtime;                     //ローソク足時間保持用
//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit(){

   //デジットチェック
   if (Digits == 3 || Digits == 5){
      PipValue = Point * 10;   // 例: EURUSD 1.23456 → 1pip = 0.00010
   }else{
      PipValue = Point;        // 例: USDJPY 123.45 → 1pip = 0.01
   }

   return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason){

}
//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick(){

   int total = OrdersTotal();    //ポジショントータル数をセット  
   int ea_order_Type = -1; //0:OP_BUY  1:OP_SELL
   double ea_order_stop_price = 0; //ストップロスレート
   double ea_order_good_price = 0; //利確レート

   //新しい足ができた時だけやる
   if(Time[0] != prevtime){
      prevtime = Time[0];
   }else{
      return;
   }

   //ポジションがない場合はエントリー判定
   if(total == 0){

      bool openBuy  = false;    //買い新規注文判断
      bool openSell = false;    //売り新規注文判断
      
//***【新規注文判断↓】***//
   //=== 平均足シグナル判定===//
   bool isBullHeikinAshi_S = false;
   bool isBearHeikinAshi_S = false;
   
   //=== 直近2本の平均足取得 ===//
   double haOpen1  = iCustom(NULL, 0, "Heiken Ashi", 2, 1);
   double haClose1 = iCustom(NULL, 0, "Heiken Ashi", 3, 1);
   double haOpen2  = iCustom(NULL, 0, "Heiken Ashi", 2, 2);
   double haClose2 = iCustom(NULL, 0, "Heiken Ashi", 3, 2);
   
   double haDiff1 = haClose1 - haOpen1; //1本前の実体
   double haDiff2 = haClose2 - haOpen2; //2本前の実体
   
   // ヒゲの長さ(上下ヒゲの合計)
   double haHigh1 = iHigh(NULL, 0, 1);
   double haLow1  = iLow(NULL, 0, 1);
   double body1   = haDiff1;
   double wick1   = (haHigh1 - haLow1) - MathAbs(body1);
   
   // 日足の方向判定
   double dayOpen  = iCustom(NULL, PERIOD_D1, "Heiken Ashi", 2, 0);
   double dayClose = iCustom(NULL, PERIOD_D1, "Heiken Ashi", 3, 0);
   double dayDiff  = dayClose - dayOpen;
   
   // 実体とヒゲの比率しきい値
   double ratio = HEIKIN_SIKII; // 実体がヒゲの0倍以上なら強い足と判断
   
   //=== シグナル判定 ===//
   // 直近2本で陰→陽の転換、かつ日足も陽なら買い
   if(haDiff2 < 0 && haDiff1 > 0 && dayDiff > 0)
   {
       if(MathAbs(body1) > wick1 * ratio) // ヒゲが長すぎない=強い足
           isBullHeikinAshi_S = true;
   }
   
   // 直近2本で陽→陰の転換、かつ日足も陰なら売り
   else if(haDiff2 > 0 && haDiff1 < 0 && dayDiff < 0)
   {
       if(MathAbs(body1) > wick1 * ratio) // ヒゲが長すぎない=強い足
           isBearHeikinAshi_S = true;
   }
   
   //=== 直近損切り抑制(任意) ===//
   if(OrdersHistoryTotal() > 0)
   {
       if(OrderSelect(OrdersHistoryTotal()-1, SELECT_BY_POS, MODE_HISTORY))
       {
           datetime recentTime = iTime(NULL, 0, 3); // 直近3本以内
           bool isRecent = OrderCloseTime() > recentTime;
           bool sameDirectionLoss = isRecent &&
                                    (OrderProfit() < 0) &&
                                    ((OrderType() == OP_BUY && isBullHeikinAshi_S) ||
                                     (OrderType() == OP_SELL && isBearHeikinAshi_S));
           if(sameDirectionLoss)
           {
               isBullHeikinAshi_S = false;
               isBearHeikinAshi_S = false;
           }
       }
   }

   
//***【新規注文判断↑】***//

      //参照するサンプルEAの買いエントリーフラグをセットしてください
      if(isBullHeikinAshi_S){
         openBuy =true;
         ea_order_Type = OP_BUY;
      //参照するサンプルEAの売りエントリーフラグをセットしてください      
      }else if(isBearHeikinAshi_S){
         openSell =true;
         ea_order_Type = OP_SELL;
      }

//***【TP/SL判断↓】***//
      if(openBuy){
         ea_order_stop_price = Ask - SL * PipValue;
         ea_order_good_price = Ask + TP * PipValue;
      }else if(openSell){
         ea_order_stop_price = Bid + SL * PipValue;  
         ea_order_good_price = Bid - TP * PipValue;   
      }
//***【TP/SL判断↑】***//

      if(ea_order_Type > -1){
         funcOrder_Send(ea_order_Type,ea_order_stop_price,ea_order_good_price,Lots,Ea_Name,777,0);
      }
      
   //ポジションがある場合は決済判定   
   }else{
   
      bool closeBuy  = false;    //買いポジション決済判断
      bool closeSell = false;    //売りポジション決済判断
      
//***【決済注文判断↓】***//
      bool isBullHeikinAshi_C = false;
      bool isBearHeikinAshi_C = false;
   
       //***平均足の確認***//
      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;//マイナスなら赤色、プラスなら白色
      
      if(HaDifference1<0 && OrderType() == OP_BUY){
         isBullHeikinAshi_C = true;
      }
       
      if(HaDifference1>0 &&  OrderType() == OP_SELL){
         isBearHeikinAshi_C = true;
      }   
   
//***【決済注文判断↑】***//
   
      //参照するサンプルEAの買いポジション決済フラグをセットしてください
      if(isBullHeikinAshi_C){
         closeBuy =true;
         ea_order_Type = OP_BUY;
      //参照するサンプルEAの売りポジション決済フラグをセットしてください
      }else if(isBearHeikinAshi_C){
         closeSell =true;
         ea_order_Type = OP_SELL;
      }
      
      if(ea_order_Type > -1){
         funcOrder_CloseAll(ea_order_Type, -1, 0);
      }
      
   }

   return;
}


//★★★★★★★★★★★★★★★★★★★★ ここから下は共通関数 ★★★★★★★★★★★★★★★★★★★★
//+------------------------------------------------------------------+
//|【関数】新規注文関数
//|
//|【引数】 orderType:売買(0=BUY, 1=SELL)
//|【引数】 sl:損切り価格  tp:利確価格
//|【引数】 lots:取引ロット数
//|【引数】 orderComment:オーダーコメント
//|【引数】 magic:マジックNo
//|【引数】 maxSpreadPips: 許容スプレッド[Point] (0=制限なし)
//|
//|【戻値】true=成功 false=失敗
//+------------------------------------------------------------------+
bool funcOrder_Send(int orderType, double sl, double tp, double lots,
                    string orderComment, int magic, int maxSpreadPoints){
   int    ticket = -1;
   double price  = 0;
   color  clr    = clrNONE;
   double spread = 0;

   // 最大10回リトライ
   for(int retry=0; retry<10; retry++)
   {
      RefreshRates();
      
      // スプレッド取得(MT4表示と同じポイント単位)
      spread = MarketInfo(Symbol(), MODE_SPREAD);

      // スプレッドチェック(maxSpreadPoints=0 の場合はスキップ)
      if(maxSpreadPoints > 0 && spread > maxSpreadPoints){
         Print("スプレッド拡大中(", spread,
               "ポイント) → 待機中 (リトライ=", retry+1, ")");
         Sleep(2000);
         continue;
      }

      // エントリー価格と色を決定
      if(orderType == OP_BUY){
         price = Ask;
         clr   = clrBlue;
      }else if(orderType == OP_SELL){
         price = Bid;
         clr   = clrRed;
      }

      ticket = OrderSend(Symbol(), orderType, lots, price,
                         20, sl, tp, orderComment, magic, 0, clr);

      if(ticket > 0){
         Print("新規注文成功! チケットNo=", ticket,
               " レート=", price, " Lots=", lots,
               " スプレッド=", spread, "ポイント");
         return true;
      }else{
         int err = GetLastError();
         Print("新規注文失敗 エラーNo=", err,
               " リトライ回数=", retry+1);
         Sleep(2000);
      }
   }

   return false;
}

//+------------------------------------------------------------------+
//|【関数】全決済関数                                                 |
//|                                                                  |
//|【引数】 orderType : -1=すべて  0=BUYのみ  1=SELLのみ             |
//|【引数】 magic: 対象マジック番号 (-1=全マジック対象)               |
//|【引数】 maxSpreadPoints: 許容スプレッド[Point] (0=制限なし)       |
//|                                                                  |
//|【戻値】true=全成功 false=一部失敗あり                             |
//+------------------------------------------------------------------+
bool funcOrder_CloseAll(int orderType, int magic, int maxSpreadPoints){
   bool allSuccess = true;
   int totalOrders = OrdersTotal();
   double spread, closePrice, lots;
   int type, ticket;
   color  clr    = clrNONE;

   for(int i = totalOrders-1; i >= 0; i--)
   {
      //オーダーセレクト失敗でスキップ
      if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
      
      // 同一シンボル以外はスキップ
      if(OrderSymbol() != Symbol()) continue;

      // マジック番号指定がある場合は一致しないものをスキップ
      if(magic != -1 && OrderMagicNumber() != magic) continue;

      type   = OrderType();
      ticket = OrderTicket();
      lots   = OrderLots();

      // orderType指定でフィルタ
      if(orderType == 0 && type != OP_BUY) continue;
      if(orderType == 1 && type != OP_SELL) continue;

      // 最大10回リトライ
      bool result = false;
      for(int retry=0; retry<10; retry++)
      {
         RefreshRates();

         // スプレッド取得
         spread = MarketInfo(Symbol(), MODE_SPREAD);

         if(maxSpreadPoints > 0 && spread > maxSpreadPoints){
            Print("スプレッド拡大中(", spread,
                  "ポイント) → クローズ待機中 ticket=", ticket,
                  " リトライ=", retry+1);
            Sleep(2000);
            continue;
         }

         // クローズ価格決定
         if(type == OP_BUY){
            closePrice = Bid;
            clr   = clrBlue;
         }else if(type == OP_SELL){
            closePrice = Ask;
            clr   = clrRed;
         }

         result = OrderClose(ticket, lots, closePrice, 20, clr);

         if(result){
            Print("クローズ成功! チケットNo=", ticket,
                  " ロット=", lots, " クローズ価格=", closePrice,
                  " スプレッド=", spread, "ポイント");
            break;
         }else{
            int err = GetLastError();
            Print("クローズ失敗 ticket=", ticket,
                  " エラーNo=", err,
                  " リトライ=", retry+1);
            Sleep(2000);
         }
      }

      if(!result) allSuccess = false;
   }

   return allSuccess;
}

iCustom() 関数の解説

平均足は MT4 にカスタムインジケータとして標準搭載されています。
よって、平均足の情報を取得するためにiCustom()関数を使っています。

【使用例】

double haOpen  = iCustom(NULL, 0, "Heiken Ashi", 2, 1);
double haClose = iCustom(NULL, 0, "Heiken Ashi", 3, 1);
  • バッファ2 … Heiken Ashi の始値
  • バッファ3 … Heiken Ashi の終値

👉 haClose > haOpen なら陽線、逆なら陰線と判定できます。

引数の意味

  • 引数1 … 通貨ペア(NULLで現在のチャート)
  • 引数2 … 時間軸(0で現在の時間足)
  • 引数3 … インジケータ名称(平均足を使う場合”Heiken Ashi”でOKです)
  • 引数4 … 取得モード
    • 0 … 平均足のHigh値
    • 1 … 平均足のLow値
    • 2 … 平均足のOpen値
    • 3 … 平均足のClose値
  • 引数5 … バーシフト(0で最新の足、1で1本前の足、2で2本前の足…)

平均足利用箇所

  • 新規注文処理(67〜126行目) … 平均足の転換や、ローソク足とヒゲの状態を判定
  • 決済処理(162〜181行目) … 平均足の転換を判定(TP/SLは1000pipsとしているので未使用扱い)

平均足の売買判定について

  • 実体(haClose – haOpen)が大きい → トレンドが強い
  • ヒゲ(High – Low)が大きい → 反転リスクあり

👉 本EAでは「実体 / ヒゲの長さが一定割合以上」の場合のみ有効シグナルと判定します。
(パラメータ HEIKIN_SIKII で調整可能)

平均足EAのトレードイメージ

平均足EAのトレードイメージ
  1. 陽線が続いてトレンド上昇
  2. 初めて陰線が出現 → 反転売りサイン
  3. TP / SL による決済管理

👉 チャート上で「平均足の色の変化に自動で追従」するイメージです。

新規、決済注文について

本記事の平均足 EAで使用している「新規注文、決済注文処理」は、当サイト共通のベースソースに含まれています。
EAを効率的に自作したい方は、以下の記事もあわせて参考にしてください。
👉 MT4 EAベースソースコード(無料配布・カスタマイズ容易)
👉 新規・変更・決済注文関数の詳しい解説はこちら


パラメータとカスタマイズ

今回のEAで設定できる主なパラメータ:

  • 取引ロット数(0.01で1000通貨)
  • 利確(pips)
  • 損切り(pips)
  • ヒゲ比率しきい値(HEIKIN_SIKII)
平均足EAのパラメータ設定

👉 ヒゲ比率しきい値例:体がヒゲの〇倍以上なら強い足と判断(0でチェックしない)


EAバックテストレポート

USDJPY(H1, 2024/08〜2025/08, 初期証拠金100万円, FXTFヒストリカル)での結果:

通貨ペア・時間足PF勝率最大DD取引回数備考
USDJPY H11.6445%0.45%222回大きな下げもなく好調
平均足EAのバックテストレポート

まとめ

平均足EAは「転換サインで売買するシンプルロジック」を自動化できます。

  • トレンドが続く局面では強力
  • ヒゲ比率フィルタでダマシを軽減可能
  • 他のトレンドフィルタと併用でさらに精度UP

まずはサンプルコードを試し、バックテストで挙動を確認してみてください。今回は中々良い結果になったので、平均足は十分使えそうですね!シンプルに平均足のみでも十分な結果ですが、ロジック改良やフィルタ追加をすれば理想的なEAに近づけます。

🛠️ EAを効率的にカスタマイズしたい方へ
【MT4 EAベースソース】を無料公開中!初心者でもすぐに使えます。


✅ 他にも多数のEAサンプルを公開中!

今回紹介した内容以外にも、当サイトではさまざまな
FX自動売買EAのサンプルコードを提供しています。

自分に合った戦略のEAを見つけたい方は、ぜひチェックしてみてください。


📊 【2025年最新】FX自動売買EAランキングも公開中!

当サイトの「どのEA(インジ)が実際に勝っているのか?」を知りたい方はこちら👇
PF(プロフィットファクター)や勝率で徹底比較した最新ランキングから、あなたに最適なEA(インジ)を見つけましょう。


EA開発初心者向けに、今後も実践的なMQL4関数を紹介していきます。
気になる機能やロジックがあれば、ぜひ他の記事もあわせてご覧ください!

コメント

  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をコピーしました