高収益・無料EA「TrendFollow_Pyramid」をチェック ▶︎

無料・高収益 MQL5 EA「TrendFollow_Pyramid」のソースコード・使い方

  • URLをコピーしました!

収益性の高いMQL5 EAが開発しました。口座縛りなしでソースコードを無料公開します。

パラメーターもある程度最適化しており、メタエディターにコピペしてコンパイルすれば、すぐに運用できます。

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

目次

TrendFollow_Pyramidの無料ソースコード(コピペ可能)

ソースコードはこちら。右上のクリップボードからコピペできます。

//+------------------------------------------------------------------+
//| v1.13 ATR期間・エントリーSLバー数・トレーリング参照バー数を外部化 |
//+------------------------------------------------------------------+
#property copyright "TrendFollow_Pyramid"
#property version   "1.13"

#include <Trade\Trade.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\SymbolInfo.mqh>

input string   Group1 = "=== Risk & Money Management ===";
input double   FixedLot          = 0.01;
input int      MaxPositions      = 10;
input double   PyramidScale      = 2.0;

input string   Group2 = "=== Trend Filter ===";
input int      H4_FastMA         = 15;
input int      H4_SlowMA         = 40;
input int      D1_FastMA         = 5;
input int      D1_SlowMA         = 15;

input string   Group3 = "=== Entry & Pyramid Logic ===";
input int      M15_TriggerMA     = 60;
input double   PullbackAtrMult   = 10.0;
input int      SwingSettleBars   = 1;
input double   PyramidAtrMult    = 0.8;
input int      EntrySlBars       = 15;

input string   Group4 = "=== Trade Management ===";
input int      M15_AtrPeriod       = 14;
input int      TrailingLookback    = 14;
input double   TrailingAtrMultMax  = 8.0;
input double   TrailingAtrMultMin  = 2.0;
input double   TrailingAtrMultStep = 1.0;
input int      MaxSpread         = 50;

input string   Group5 = "=== EA System ===";
input int      MagicNumber       = 100013;
input int      Slippage          = 30;

CTrade         trade;
CPositionInfo  posInfo;
CSymbolInfo    symInfo;

int handle_H4_FastMA, handle_H4_SlowMA;
int handle_D1_FastMA, handle_D1_SlowMA;
int handle_M15_TriggerMA;
int handle_M15_ATR;

ENUM_POSITION_TYPE currentTradeDirection = WRONG_VALUE;

ulong failedTickets[];

int OnInit()
  {
   trade.SetExpertMagicNumber(MagicNumber);
   trade.SetDeviationInPoints(Slippage);
   symInfo.Name(_Symbol);
   symInfo.Refresh();

   handle_H4_FastMA     = iMA(_Symbol, PERIOD_H4, H4_FastMA, 0, MODE_SMA, PRICE_CLOSE);
   handle_H4_SlowMA     = iMA(_Symbol, PERIOD_H4, H4_SlowMA, 0, MODE_SMA, PRICE_CLOSE);
   handle_D1_FastMA     = iMA(_Symbol, PERIOD_D1, D1_FastMA, 0, MODE_SMA, PRICE_CLOSE);
   handle_D1_SlowMA     = iMA(_Symbol, PERIOD_D1, D1_SlowMA, 0, MODE_SMA, PRICE_CLOSE);
   handle_M15_TriggerMA = iMA(_Symbol, PERIOD_M15, M15_TriggerMA, 0, MODE_SMA, PRICE_CLOSE);
   handle_M15_ATR       = iATR(_Symbol, PERIOD_M15, M15_AtrPeriod);

   if(handle_H4_FastMA == INVALID_HANDLE || handle_H4_SlowMA == INVALID_HANDLE ||
      handle_D1_FastMA == INVALID_HANDLE || handle_D1_SlowMA == INVALID_HANDLE ||
      handle_M15_TriggerMA == INVALID_HANDLE || handle_M15_ATR == INVALID_HANDLE)
     {
      Print("Failed to load indicators. EA initialization aborted.");
      return(INIT_FAILED);
     }

   ArrayResize(failedTickets, 0);
   RestoreTradeDirection();

   Print("Momentum Pyramid Master EA v1.13 initialized. Symbol=", _Symbol,
         " Magic=", MagicNumber, " FixedLot=", FixedLot, " Direction=", currentTradeDirection);
   return(INIT_SUCCEEDED);
  }

void RestoreTradeDirection()
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
        {
         currentTradeDirection = (ENUM_POSITION_TYPE)posInfo.PositionType();
         Print("Trade direction restored: ", currentTradeDirection);
         return;
        }
     }
   currentTradeDirection = WRONG_VALUE;
  }

void OnDeinit(const int reason)
  {
   IndicatorRelease(handle_H4_FastMA);
   IndicatorRelease(handle_H4_SlowMA);
   IndicatorRelease(handle_D1_FastMA);
   IndicatorRelease(handle_D1_SlowMA);
   IndicatorRelease(handle_M15_TriggerMA);
   IndicatorRelease(handle_M15_ATR);
  }

bool IsNewBar()
  {
   static datetime last_time = 0;
   static bool     initialized = false;
   datetime current_time = iTime(_Symbol, PERIOD_M15, 0);

   if(!initialized)
     {
      last_time   = current_time;
      initialized = true;
      return false;
     }

   if(current_time != last_time)
     {
      last_time = current_time;
      return true;
     }
   return false;
  }

double GetDynamicTrailingMult(int posCount)
  {
   double mult = TrailingAtrMultMax - (posCount - 1) * TrailingAtrMultStep;
   if(mult < TrailingAtrMultMin) mult = TrailingAtrMultMin;
   return mult;
  }

void AddFailedTicket(ulong ticket)
  {
   int sz = ArraySize(failedTickets);
   for(int i = 0; i < sz; i++)
      if(failedTickets[i] == ticket) return;
   ArrayResize(failedTickets, sz + 1);
   failedTickets[sz] = ticket;
  }

void RemoveFailedTicket(ulong ticket)
  {
   int sz = ArraySize(failedTickets);
   for(int i = 0; i < sz; i++)
     {
      if(failedTickets[i] == ticket)
        {
         for(int j = i; j < sz - 1; j++)
            failedTickets[j] = failedTickets[j + 1];
         ArrayResize(failedTickets, sz - 1);
         return;
        }
     }
  }

void RetryFailedSLModify()
  {
   if(ArraySize(failedTickets) == 0) return;

   double target_sl = GetSecureSyncSL();
   if(target_sl == 0) return;

   for(int i = ArraySize(failedTickets) - 1; i >= 0; i--)
     {
      ulong ticket = failedTickets[i];
      if(!posInfo.SelectByTicket(ticket))
        {
         RemoveFailedTicket(ticket);
         continue;
        }
      if(posInfo.Magic() != MagicNumber || posInfo.Symbol() != _Symbol)
        {
         RemoveFailedTicket(ticket);
         continue;
        }
      if(posInfo.StopLoss() == target_sl)
        {
         RemoveFailedTicket(ticket);
         continue;
        }
      if(trade.PositionModify(ticket, target_sl, 0))
         RemoveFailedTicket(ticket);
      else
         Print("Retry SL modify failed. Ticket=", ticket, " Error=", trade.ResultRetcode());
     }
  }

void VerifyAndQueueNewPosition(double expected_sl)
  {
   ulong ticket = trade.ResultDeal();
   if(ticket == 0) ticket = trade.ResultOrder();
   if(ticket == 0)
     {
      Print("VerifyNewPosition: could not retrieve ticket from result.");
      return;
     }

   ulong maxTicket = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
         if(posInfo.Ticket() > maxTicket) maxTicket = posInfo.Ticket();
     }

   if(maxTicket == 0)
     {
      Print("VerifyNewPosition: no position found after order. Order may be pending.");
      return;
     }

   if(!posInfo.SelectByTicket(maxTicket))
     {
      Print("VerifyNewPosition: SelectByTicket failed. Ticket=", maxTicket);
      return;
     }

   if(posInfo.StopLoss() != expected_sl)
     {
      Print("VerifyNewPosition: SL mismatch. Ticket=", maxTicket,
            " Expected=", expected_sl, " Actual=", posInfo.StopLoss(), " Queuing retry.");
      AddFailedTicket(maxTicket);
     }
  }

void OnTick()
  {
   symInfo.RefreshRates();

   RetryFailedSLModify();

   int posCount = CountPositions();
   if(posCount == 0) currentTradeDirection = WRONG_VALUE;

   if(posCount > 0)
      ProcessTrailingStop(posCount);

   if(IsNewBar())
     {
      if(symInfo.Spread() > MaxSpread)
        {
         Print("Spread too wide: ", symInfo.Spread(), " > ", MaxSpread, ". Skipping bar.");
         return;
        }

      if(posCount == 0)
        {
         int signal = CheckEntrySignal();
         if(signal == 1)
           {
            double sl = GetRecentLowest(EntrySlBars);
            sl = CheckStopLevel(symInfo.Ask(), sl, true);
            double lot = NormalizeLotSize(FixedLot);
            if(trade.Buy(lot, _Symbol, symInfo.Ask(), sl, 0, "Initial Long"))
              {
               if(ConfirmPositionExists(POSITION_TYPE_BUY))
                 {
                  currentTradeDirection = POSITION_TYPE_BUY;
                  VerifyAndQueueNewPosition(sl);
                 }
               else
                  Print("Initial Buy: order accepted but position not found.");
              }
            else
               Print("Initial Buy failed. Error: ", trade.ResultRetcode());
           }
         else if(signal == -1)
           {
            double sl = GetRecentHighest(EntrySlBars);
            sl = CheckStopLevel(symInfo.Bid(), sl, false);
            double lot = NormalizeLotSize(FixedLot);
            if(trade.Sell(lot, _Symbol, symInfo.Bid(), sl, 0, "Initial Short"))
              {
               if(ConfirmPositionExists(POSITION_TYPE_SELL))
                 {
                  currentTradeDirection = POSITION_TYPE_SELL;
                  VerifyAndQueueNewPosition(sl);
                 }
               else
                  Print("Initial Sell: order accepted but position not found.");
              }
            else
               Print("Initial Sell failed. Error: ", trade.ResultRetcode());
           }
        }
      else if(posCount > 0 && posCount < MaxPositions)
        {
         if(CheckPyramidSignal(currentTradeDirection))
           {
            double current_sl = GetSecureSyncSL();
            if(current_sl == 0)
              {
               Print("Pyramid skipped: no valid SL found across existing positions.");
               return;
              }

            double nextLot = NormalizeLotSize(FixedLot * PyramidScale);
            if(currentTradeDirection == POSITION_TYPE_BUY)
              {
               current_sl = CheckStopLevel(symInfo.Ask(), current_sl, true);
               if(trade.Buy(nextLot, _Symbol, symInfo.Ask(), current_sl, 0, "Pyramid Long"))
                  VerifyAndQueueNewPosition(current_sl);
               else
                  Print("Pyramid Buy failed. Error: ", trade.ResultRetcode());
              }
            else if(currentTradeDirection == POSITION_TYPE_SELL)
              {
               current_sl = CheckStopLevel(symInfo.Bid(), current_sl, false);
               if(trade.Sell(nextLot, _Symbol, symInfo.Bid(), current_sl, 0, "Pyramid Short"))
                  VerifyAndQueueNewPosition(current_sl);
               else
                  Print("Pyramid Sell failed. Error: ", trade.ResultRetcode());
              }
           }
        }
     }
  }

bool ConfirmPositionExists(ENUM_POSITION_TYPE dir)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
         if((ENUM_POSITION_TYPE)posInfo.PositionType() == dir)
            return true;
     }
   return false;
  }

bool IsSwingSettled(bool is_buy)
  {
   if(is_buy)
     {
      int lowestIdx = iLowest(_Symbol, PERIOD_M15, MODE_LOW, 15, 1);
      if(lowestIdx >= 1 && lowestIdx <= SwingSettleBars) return false;
     }
   else
     {
      int highestIdx = iHighest(_Symbol, PERIOD_M15, MODE_HIGH, 15, 1);
      if(highestIdx >= 1 && highestIdx <= SwingSettleBars) return false;
     }
   return true;
  }

int CheckEntrySignal()
  {
   double h4_fast[1], h4_slow[1], d1_fast[1], d1_slow[1];
   double m15_ma[2], atr[1], close[2];

   if(CopyBuffer(handle_H4_FastMA, 0, 1, 1, h4_fast) < 1)     return 0;
   if(CopyBuffer(handle_H4_SlowMA, 0, 1, 1, h4_slow) < 1)     return 0;
   if(CopyBuffer(handle_D1_FastMA, 0, 1, 1, d1_fast) < 1)     return 0;
   if(CopyBuffer(handle_D1_SlowMA, 0, 1, 1, d1_slow) < 1)     return 0;
   if(CopyBuffer(handle_M15_TriggerMA, 0, 1, 2, m15_ma) < 2)  return 0;
   if(CopyBuffer(handle_M15_ATR, 0, 1, 1, atr) < 1)           return 0;
   if(CopyClose(_Symbol, PERIOD_M15, 1, 2, close) < 2)         return 0;

   bool isUpTrendD1   = (d1_fast[0] > d1_slow[0]);
   bool isDownTrendD1 = (d1_fast[0] < d1_slow[0]);
   bool isUpTrendH4   = (h4_fast[0] > h4_slow[0]);
   bool isDownTrendH4 = (h4_fast[0] < h4_slow[0]);

   double distToH4   = MathAbs(close[0] - h4_fast[0]);
   bool isPulledBack = (distToH4 <= (atr[0] * PullbackAtrMult));

   if(isUpTrendD1 && isUpTrendH4 && isPulledBack)
      if(close[1] < m15_ma[1] && close[0] > m15_ma[0])
         if(IsSwingSettled(true)) return 1;

   if(isDownTrendD1 && isDownTrendH4 && isPulledBack)
      if(close[1] > m15_ma[1] && close[0] < m15_ma[0])
         if(IsSwingSettled(false)) return -1;

   return 0;
  }

bool CheckPyramidSignal(ENUM_POSITION_TYPE dir)
  {
   double atr[1];
   if(CopyBuffer(handle_M15_ATR, 0, 1, 1, atr) < 1) return false;

   double lastOpenPrice = GetLastPositionOpenPrice();
   if(dir == POSITION_TYPE_BUY  && (symInfo.Bid() - lastOpenPrice) < atr[0] * PyramidAtrMult) return false;
   if(dir == POSITION_TYPE_SELL && (lastOpenPrice - symInfo.Ask()) < atr[0] * PyramidAtrMult)  return false;

   return true;
  }

void ProcessTrailingStop(int posCount)
  {
   double atr[1];
   if(CopyBuffer(handle_M15_ATR, 0, 1, 1, atr) < 1) return;

   double dynamicMult = GetDynamicTrailingMult(posCount);

   double current_sl = GetSecureSyncSL();
   if(current_sl != 0)
      FillMissingStopLoss(current_sl);

   current_sl = GetSecureSyncSL();

   double new_sl = 0;

   if(currentTradeDirection == POSITION_TYPE_BUY)
     {
      double highest = iHigh(_Symbol, PERIOD_M15, iHighest(_Symbol, PERIOD_M15, MODE_HIGH, TrailingLookback, 1));
      new_sl = NormalizeDouble(highest - (atr[0] * dynamicMult), symInfo.Digits());
      new_sl = CheckStopLevel(symInfo.Bid(), new_sl, true);
      if(current_sl == 0 || new_sl > current_sl)
         ModifyAllPositionsSL(new_sl);
     }
   else if(currentTradeDirection == POSITION_TYPE_SELL)
     {
      double lowest = iLow(_Symbol, PERIOD_M15, iLowest(_Symbol, PERIOD_M15, MODE_LOW, TrailingLookback, 1));
      new_sl = NormalizeDouble(lowest + (atr[0] * dynamicMult), symInfo.Digits());
      new_sl = CheckStopLevel(symInfo.Ask(), new_sl, false);
      if(current_sl == 0 || new_sl < current_sl)
         ModifyAllPositionsSL(new_sl);
     }
  }

void FillMissingStopLoss(double sl_price)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
        {
         if(posInfo.StopLoss() == 0)
           {
            Print("SL missing. Ticket=", posInfo.Ticket(), " Filling SL=", sl_price);
            if(!trade.PositionModify(posInfo.Ticket(), sl_price, 0))
              {
               Print("FillMissingSL failed. Ticket=", posInfo.Ticket(), " Error=", trade.ResultRetcode());
               AddFailedTicket(posInfo.Ticket());
              }
            else
               RemoveFailedTicket(posInfo.Ticket());
           }
        }
     }
  }

void ModifyAllPositionsSL(double sl_price)
  {
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
        {
         if(posInfo.StopLoss() != sl_price)
           {
            if(!trade.PositionModify(posInfo.Ticket(), sl_price, 0))
              {
               Print("SL Modify failed. Ticket=", posInfo.Ticket(), " Error=", trade.ResultRetcode());
               AddFailedTicket(posInfo.Ticket());
              }
            else
               RemoveFailedTicket(posInfo.Ticket());
           }
        }
     }
  }

double CheckStopLevel(double base_price, double sl_price, bool is_buy)
  {
   double stopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * symInfo.Point();
   if(stopLevel == 0) return sl_price;

   if(is_buy)
     {
      if(base_price - sl_price < stopLevel)
         return NormalizeDouble(base_price - stopLevel, symInfo.Digits());
     }
   else
     {
      if(sl_price - base_price < stopLevel)
         return NormalizeDouble(base_price + stopLevel, symInfo.Digits());
     }
   return sl_price;
  }

double NormalizeLotSize(double lot)
  {
   double minLot  = symInfo.LotsMin();
   double maxLot  = symInfo.LotsMax();
   double stepLot = symInfo.LotsStep();
   if(stepLot <= 0) return lot;
   lot = MathFloor(lot / stepLot) * stepLot;
   if(lot < minLot) lot = minLot;
   if(lot > maxLot) lot = maxLot;
   return lot;
  }

double GetRecentLowest(int period)  { return iLow(_Symbol,  PERIOD_M15, iLowest(_Symbol,  PERIOD_M15, MODE_LOW,  period, 1)); }
double GetRecentHighest(int period) { return iHigh(_Symbol, PERIOD_M15, iHighest(_Symbol, PERIOD_M15, MODE_HIGH, period, 1)); }

int CountPositions()
  {
   int count = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
         count++;
   return count;
  }

double GetLastPositionOpenPrice()
  {
   double price = 0;
   ulong maxTicket = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
         if(posInfo.Ticket() > maxTicket) { maxTicket = posInfo.Ticket(); price = posInfo.PriceOpen(); }
   return price;
  }

double GetSecureSyncSL()
  {
   double sl = 0;
   for(int i = PositionsTotal() - 1; i >= 0; i--)
     {
      if(posInfo.SelectByIndex(i) && posInfo.Magic() == MagicNumber && posInfo.Symbol() == _Symbol)
        {
         double posSL = posInfo.StopLoss();
         if(posSL == 0) continue;

         if(currentTradeDirection == POSITION_TYPE_BUY)
           {
            if(sl == 0 || posSL > sl) sl = posSL;
           }
         else if(currentTradeDirection == POSITION_TYPE_SELL)
           {
            if(sl == 0 || posSL < sl) sl = posSL;
           }
        }
     }
   return sl;
  }

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

TrendFollow_Pyramidの仕様

ご提示いただいたMQL5コード(TrendFollow_Pyramid v1.13)の仕様を解説します。

このEAは、マルチタイムフレーム(D1, H4, M15)を利用したトレンドフォロー型のピラミッディング(増し玉)EAです。トレンド方向に沿ってエントリーし、利益が乗るごとにポジションを追加、ポジションが増えるほどトレーリングストップをタイトにして一斉決済を狙うロジックになっています。

主な仕様は以下の通りです。


1. 動作環境

  • 対象通貨ペア: 制限なし(稼働させたチャートの通貨ペア)
  • 動作タイムフレーム: 内部ロジックでM15(15分足)を基準に動作するようにハードコードされています(チャートのタイムフレームに関わらず、D1、H4、M15のデータを利用します)。

2. 新規エントリーロジック

以下の条件がすべて揃った時(15分足の確定時)に最初のポジションを持ちます。

【買い(ロング)条件】

  1. トレンド判定(日足・4時間足):
  • D1の短期MA(5) > D1の長期MA(15)
  • H4の短期MA(15) > H4の長期MA(40)
  1. プルバック(押し目)判定:
  • 現在の価格が、H4の短期MAから「15分足ATR × 10.0(デフォルト設定)」の距離以内にあること(トレンドから乖離しすぎていないこと)。
  1. トリガー(15分足):
  • 1つ前の15分足終値が M15のトリガーMA(60)より下にあり、現在の終値がMAより上にある(MAを上抜け)。
  1. スイングの安定化(直近の安値更新ストップ):
  • 過去15本(M15)の最安値が、直近1本以内のローソク足で発生していないこと(下落が一旦落ち着いていること)。

売り(ショート)条件は上記と完全に逆の条件になります(MAを下抜け等)。

【初期ストップロス(SL)】

  • 買い:過去15本(M15)の最安値
  • 売り:過去15本(M15)の最高値

3. ピラミッディング(増し玉)ロジック

トレンドに乗り、価格が順行した場合にポジションを追加します。

  • 最大ポジション数: デフォルト10個 (MaxPositions)
  • 追加条件:
  • 直近に建てたポジションのオープン価格から、順行方向に「M15のATR × 0.8」以上価格が進んでいること。
  • 追加時のロット数:
  • 初期ロット(FixedLot) × 倍率(PyramidScale)
  • デフォルトでは 0.01 × 2.0 = 0.02 となり、2番目以降のポジションはすべて0.02ロットでエントリーされます(マーチンゲールのようにどんどん倍増していくわけではなく、2ポジ目以降は固定ロットです)。
  • 追加時のストップロス:
  • 既存のポジションの中で最も安全(現在価格に近い)なSL価格に、新しいポジションのSLも同期されます。

4. 決済ロジック(動的トレーリングストップ)

利益を伸ばしつつ、ポジション数が増えるにつれて決済ライン(SL)をタイト(厳しく)にしていく独自の決済システムです。すべてのポジションは同じSL価格に同期され、一斉に決済されます。

  • トレーリング基準: 過去14本(M15)の最高値/最安値を利用。
  • 動的ATRマルチプライヤー(乗数):
  • 最大乗数(8.0) - (現在のポジション数 - 1) × ステップ(1.0)
  • ポジションが1つの時は、ATRの8倍の余裕を持たせた緩いストップ。
  • ポジションが2つの時は、ATRの7倍…と、ポジションが増えるたびにストップの距離が縮まります(最小でATRの2倍まで)。
  • 買いポジションのストップ更新:
  • 「過去14本の最高値 - (ATR × 動的乗数)」が現在のSLよりも高くなれば、すべてのポジションのSLを引き上げます。

5. 安全・保護機能

このEAには、実運用に耐えうるための堅牢なシステムが組み込まれています。

  • SL設定のリトライ機能:
    ブローカーのエラーや急激な価格変動でSLの変更に失敗した場合、エラーになったチケット番号を記憶し、毎ティック(価格変動ごと)にSLの修正を再試行します。
  • ストップレベルの補正:
    設定しようとしたSLがブローカーの規定するストップレベル(現在価格に近すぎる制限)に引っかかる場合、自動的に許容されるギリギリの価格にSLを補正します。
  • スプレッドフィルター:
    現在のスプレッドが MaxSpread (デフォルト50ポイント) を超えている場合は、スリッページ防止のために新規取引やピラミッディングを見送ります。
  • 再起動時の復元:
    MT5やEAを再起動した際、現在持っているポジションを読み込み、現在が買いモードか売りモードかを自動で復元します。

パラメータ設定の要約

グループパラメータ名意味デフォルト
資金管理FixedLot初期ロット0.01
MaxPositions最大保有ポジション数10
PyramidScale増し玉時のロット倍率 (0.01 × 2.0 = 0.02)2.0
トレンド判定H4_FastMA / SlowMA4時間足の短期/長期SMA15 / 40
D1_FastMA / SlowMA日足の短期/長期SMA5 / 15
エントリーM15_TriggerMA15分足のエントリー判定用SMA60
PullbackAtrMult押し目・戻りと判定する乖離の限界(ATR乗数)10.0
SwingSettleBars高値/安値の更新が止まったとみなす足の本数1
PyramidAtrMult次の増し玉までの間隔(ATR乗数)0.8
EntrySlBars初期ストップロスを決める直近高値/安値の期間15
トレード管理M15_AtrPeriodATRの期間14
TrailingLookbackトレーリングストップ計算の参照期間14
TrailingAtrMultMaxトレーリングの初期の余裕(ATR乗数)8.0
TrailingAtrMultMinトレーリングの最小の余裕(ATR乗数)2.0
TrailingAtrMultStepポジションが増えるごとに縮める余裕幅1.0
MaxSpread許容する最大スプレッド(ポイント)50
システムMagicNumberEA識別番号100013
Slippage許容スリッページ30

全体として、「中長期のトレンドが揃ったところの押し目をピンポイントで狙い、トレンドに乗ったら積極的にポジションを増やし、反転の兆しが見えたら一網打尽で利益確定(または損切り)する」という、非常にアグレッシブかつ理にかなった仕様になっています。

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

TrendFollow_Pyramidの使い方・設定方法

このEA(Momentum Pyramid Master EA v1.10)をMetaTrader 5(MT5)で実際に使用するための手順と、パラメータの設定方法、運用のコツを分かりやすく解説します。


ステップ1:MT5への導入とコンパイル

まず、いただいたコードをMT5で動かせる状態(ex5ファイル)にします。

  1. MT5を起動し、上部メニューの「ツール」⇒「MetaQuotes言語エディタ(F4)」をクリックします。(MetaEditorが開きます)
  2. MetaEditorの左上「新規作成」をクリックし、「エキスパートアドバイザ(テンプレート)」を選択して「次へ」へ。
  3. 名前を「Experts\Momentum_Pyramid_v1.10」などと入力し、完了まで進みます。
  4. 開いた画面に最初から書かれているコードをすべて消去し、先ほどご提示いただいたコードをすべて貼り付けます。
  5. 画面上部の「コンパイル(F7)」ボタンを押します。
  6. 画面下部の「エラー」タブに 0 errors, 0 warnings と表示されれば成功です。MT5に戻ります。

ステップ2:チャートへの適用

このEAは「15分足」を基準に作られていますので、チャートの設定が重要です。

  1. MT5でトレードしたい通貨ペアのチャートを開きます。
  • ※トレンドフォロー型なので、EURUSD, GBPUSD, USDJPY, XAUUSD(ゴールド) など、トレンドが長く続きやすい銘柄がおすすめです。
  1. チャートの時間足を 「M15(15分足)」 に変更します。
  2. 左側の「ナビゲータ」ウィンドウから「エキスパートアドバイザ」を開き、先ほどコンパイルした「Momentum_Pyramid_v1.10」をチャート上にドラッグ&ドロップします。

ステップ3:設定(パラメータ)の確認

チャートにドロップすると設定画面が開きます。「インプット」タブで以下の設定を確認・変更します。最初はデフォルトでも構いませんが、資金に合わせて調整してください。

  • FixedLot(初期ロット): 0.01
  • 最初のエントリーロットです。少額からテストすることをおすすめします。
  • MaxPositions(最大保有ポジション数): 10
  • ピラミッディング(増し玉)を最大何回行うか。リスクを抑えるなら 35 に減らしてください。
  • PyramidScale(増し玉の倍率): 2.0
  • 次に持つロット数の倍率です。初期0.01なら、次は0.02になります。同じロットで追加したい場合は 1.0 にします。
  • MaxSpread(最大許容スプレッド): 50
  • 単位は「ポイント」です(50ポイント=5.0pips)。スプレッドがこれより広い時はエントリーしません。ご自身の口座のスプレッドに合わせて調整してください。
  • MagicNumber(マジックナンバー): 100010
  • EAが自分のポジションを識別するための番号です。同じ口座内で別のEAを動かす場合や、別のチャート(通貨ペア)でこのEAを動かす場合は、必ず違う数字(例: 100011, 100012…)に変更してください。

設定が終わったら、設定画面の「共有」タブで「アルゴリズム取引を許可」にチェックを入れて「OK」を押します。


ステップ4:自動売買の開始

  1. MT5の画面上部にある 「アルゴリズム取引」 というボタンをクリックして、緑色の再生マーク(ON)にします。
  2. チャートの右上に表示されている帽子のアイコンが 「青色(または緑色)」 になっていれば、正常に稼働しています。
  3. 画面下部の「ツールボックス」⇒「エキスパート」タブを開き、
    Momentum Pyramid Master EA v1.10 initialized... というメッセージが出ていれば準備完了です。あとは条件を満たすと自動でトレードを開始します。

⚠️ 運用のコツと注意点(必ずお読みください)

  1. まずはバックテスト(ストラテジーテスター)で試す
    いきなりリアル口座で動かすのではなく、MT5の「表示」⇒「ストラテジーテスター」を開き、過去のデータでどのようなトレードをするか(特にピラミッディングによるロットの増加や証拠金の減り方)を確認してください。
  2. 証拠金に余裕を持たせる
    このEAは利益が乗るたびにポジションを追加(倍率2倍なら 0.01 → 0.02 → 0.04…)します。トレンドに乗れば爆発的な利益を生みますが、急反落した際に耐えられるよう、ロット数と最大ポジション数(MaxPositions)は資金に対してかなり低めに設定してください。
  3. レンジ相場(もみ合い相場)に弱い
    トレンドフォロー型の宿命として、相場に方向感がない時は「高値掴み・安値掴み(損切り貧乏)」になりやすいです。ボラティリティ(値動き)の少ない通貨ペアや時間帯には向きません。
  4. VPS(仮想サーバー)の利用
    EAはパソコンが起動してMT5が開いている間しか動きません。特にこのEAは「トレーリングストップ(利益を追従してSLを動かす)」を毎秒計算しているため、24時間動かし続ける必要があります。本番環境ではVPSの利用を強く推奨します。

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

TrendFollow_Pyramidのバックテスト成績

リアルティックのデータがある2026年4月での運用成績です。

こちらは2026年1月から4月までの分。

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

VPSシャットダウン対策

VPS(Windowsサーバー)がWindowsアップデートやメンテナンスで再起動(シャットダウン)された後、自動的にMT5を起動して自動売買(EA)を再開させるには、いくつかの設定が必要です。

VPSの場合、「再起動後にログイン画面で止まってしまう」という特性があるため、単にスタートアップフォルダにMT5を入れるだけでは機能しないことがあります。

以下の手順で、①Windowsの自動サインイン設定②スタートアップへのMT5登録③MT5側の事前設定を行うのが最も確実で一般的な方法です。


ステップ1:Windowsの自動サインイン設定(重要)

VPSが再起動した際、自動的にデスクトップ画面まで進むように設定します。

  1. VPSにリモートデスクトップ接続します。
  2. キーボードの Windowsキー + R を押し、「ファイル名を指定して実行」を開きます。
  3. netplwiz と入力して「OK」をクリックします。
  4. 「ユーザーアカウント」画面が開きます。
  5. 「ユーザーがこのコンピューターを使うには、ユーザー名とパスワードの入力が必要」 のチェックを外します
    (※チェックボックスが無い場合の対処法は後述)
  6. 「適用」をクリックすると、パスワードを求められるので、VPSのログインパスワード(Administratorのパスワード)を2回入力して「OK」を押します。

※チェックボックスが表示されない場合
最近のWindows Serverでは非表示になっています。その場合、コマンドプロンプト(管理者)を開き、以下のコマンドをコピペして実行(Enter)してから、再度上記の手順をお試しください。
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\PasswordLess\Device" /v DevicePasswordLessBuildVersion /t REG_DWORD /d 0 /f


ステップ2:MT5をスタートアップ(自動起動)に登録する

自動ログイン後、MT5が立ち上がるように設定します。

  1. キーボードの Windowsキー + R を押し、「ファイル名を指定して実行」を開きます。
  2. shell:startup と入力して「OK」をクリックします。
    (「スタートアップ」という空のフォルダが開きます)
  3. デスクトップなどにあるMT5のショートカットアイコンをコピーし、この「スタートアップ」フォルダの中に貼り付け(ペースト)します。

これで、Windows起動時に自動でMT5が開くようになります。


ステップ3:MT5側の事前設定(超重要)

MT5が起動しても、口座にログインしてEAが動かなければ意味がありません。以下の状態になっているか必ず確認してください。

  1. パスワードの保存
  • MT5を開き、左上の「ファイル」>「取引口座にログイン」を開きます。
  • ログインID、パスワード、サーバー名が入力されている状態で、「パスワードを保存する」に必ずチェックを入れてログインします。(これにより次回起動時に自動ログインされます)
  1. 自動売買の許可
  • MT5の上部メニューにある「アルゴリズム取引」のボタンが「緑色の再生マーク(ON)」になっていることを確認します。
  1. チャートとEAの保存
  • 稼働させたいチャートを開き、EAをセットしてニコちゃんマーク(または緑の帽子マーク)が正常に表示されている状態にします。
  • その状態で、MT5を右上の「×」ボタンで一度手動で閉じます。(MT5は正常に終了した時点のチャート状態を記憶するため、設定したら一度閉じるのが鉄則です)

最終確認(テスト)

設定がすべて完了したら、本当に自動で復旧するかテストすることを強くお勧めします。

  1. VPS内のWindowsのスタートメニューから「再起動」をクリックします。(※リモートデスクトップの接続を切るだけでなく、Windows自体を再起動させます)
  2. 接続が切断されるので、3〜5分ほど待ちます
  3. 再度リモートデスクトップでVPSに接続します。
  4. MT5が自動的に立ち上がり、EAが稼働している状態になっていれば成功です。

💡 補足:タスクスケジューラを使う方法について

「タスクスケジューラ」を使って「ユーザーがログオンしているかどうかにかかわらず実行する」という設定でMT5を自動起動する方法もありますが、この方法だとバックグラウンド(画面の裏側)でMT5が起動してしまい、後からリモートデスクトップで接続した際にMT5の画面(チャート等)が見えなくなるという仕様(セッション0問題)があります。
そのため、チャート画面を直接確認したいFXトレーダーには、上記で紹介した「自動サインイン + スタートアップ登録」の方法を推奨します。

おすすめブローカー:XM(ゴールドのレバレッジが1000倍)

ゴールドでスイング系ピラミッディングトレードをするなら、ブローカーの第一候補はXMがおすすめ。ゴールドのレバレッジが1000倍と非常に高いため、必要証拠金を少なく抑えられ、少ない資金でも大きな利益を出しやすくなります。

他のブローカー(AXIORYTradeviewなど)だとゴールドはレバレッジ100倍に抑えられていることが多いですが、XMは資金的に余裕があるためなのか、ゴールドでもレバレッジ1000倍で利用できます。

例えばこのbotをデフォルト運用するとトータルの運用ロットは0.19ロット(およそ0.2ロット)になります。ロットを10倍にすることで、トータルおよそ2.0ロットになります。ゴールド2.0ロットの必要証拠金は、レバレッジ100倍口座だと9,000ドルですが、レバレッジ1000倍口座だと900ドルに抑えられます。

リスクの高い海外FXブローカーを使うにしても、ロスカットを防ぐため、必要証拠金は有効証拠金の半分以下に抑えておきたいです。

レバレッジ100倍の口座でゴールド2.0ロット取引をするには有効証拠金は20,000ドル欲しいところですが、XMのようなレバレッジ1000倍の口座なら必要証拠金は900ドルと低めなので、有効証拠金は3,000ドル以上あれば運用できます。

XMはスプレッドが広いことで上級者の間ではあまり評判が良くありませんが、スイングトレードにおいてはスプレッドの広さは気になりません。それよりもゴールドレバレッジ1000倍のメリットの方が大きいです。

またXMは海外FXブローカーの中でも老舗かつ最大手でもありますので、出金拒否・利益没収のリスクは低めです。B-bookブローカーではありますが、HFTやアービトラージ取引、夜スキャなどをしない限りペナルティを受けるリスクも低いです。初心者ならまずはXMから始めるのが無難でしょう。

XMの仕様・詳細スペックは、XM「ホームページ」で確認できます。

おすすめブローカー:XM(公式)AXIORY(公式)Tradeview(公式)

役に立った記事はSNSシェア!
  • URLをコピーしました!
  • URLをコピーしました!

海外FXのおすすめな稼ぎ方

海外FXの稼ぎ方は、スイング軸のピラミッディングトレードがおすすめ。ボラの激しいゴールドで取引することで、爆発的な利益を狙います。4時間足・日足でトレンドを把握して、15分足のプルバックでエントリーします。

このトレードロジックはMQL5 EAで自動化できます。以下のページで、そのロジックのEAのソースコードを無料公開しています。

パラメーターもある程度最適化しているので、EA初心者でも利益を出しやすくなっています。

この記事を書いた人

海外FXの情報を備忘録としてまとめています。
運用は自己責任でお願いします。
Twitterで「海外FXの有益情報bot」も運用してます。

目次