収益性の高いMQL5 EAが開発しました。口座縛りなしでソースコードを無料公開します。
パラメーターもある程度最適化しており、メタエディターにコピペしてコンパイルすれば、すぐに運用できます。
TrendFollow_Pyramidの無料ソースコード(コピペ可能)
ソースコードはこちら。右上のクリップボードからコピペできます。
//+------------------------------------------------------------------+
//| v1.10 エントリー・トレーリングロジック堅牢化 |
//+------------------------------------------------------------------+
#property copyright "TrendFollow_Pyramid"
#property version "1.10"
#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 string Group4 = "=== Trade Management ===";
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 = 100010;
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, 14);
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.10 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);
}
// 再起動直後の誤発動を防ぐため、初回は現在バー時刻で初期化して false を返す
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());
}
}
// エントリー・ピラミッド発注後に約定チケットのSLを検証しキューに積む
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;
}
// ポジションとして存在するか確認(成行約定の場合 ResultDeal がポジションIDに対応しない場合がある)
// ポジション一覧から最新チケットを特定して確認する
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;
}
// SLが正しく設定されていない場合はキューに積む
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(15);
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(15);
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))
{
// GetSecureSyncSL が 0 の場合(全ポジションSL未設定)はピラミッドを見送る
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;
}
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]) return 1;
if(isDownTrendD1 && isDownTrendH4 && isPulledBack)
if(close[1] > m15_ma[1] && close[0] < m15_ma[0]) 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]) return false;
if(dir == POSITION_TYPE_SELL && (lastOpenPrice - symInfo.Ask()) < atr[0]) 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);
// SL未設定ポジションへの補完は、補完後のSLを再取得してからトレーリング判定に使う
double current_sl = GetSecureSyncSL();
if(current_sl != 0)
FillMissingStopLoss(current_sl);
// 補完後に改めて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, 20, 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, 20, 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;
}TrendFollow_Pyramidの仕様
ご提示いただいたMQL5コード(EA名: Momentum Pyramid Master EA v1.10)の仕様を分かりやすく解説します。
このEAは、マルチタイムフレームでトレンドを判定し、押し目・戻り目でエントリー後、利益が乗るごとに増し玉(ピラミッディング)を行い、ポジションが増えるほどタイトになる動的トレーリングストップで決済する「トレンドフォロー型・堅牢EA」です。
以下に詳細な仕様をまとめます。
1. 基本仕様
- 想定時間足: 15分足(M15)ベースで動作(内部で日足・4時間足を環境認識に使用)
- 動作タイミング: 新規エントリーおよびピラミッディングの判定は「15分足の始値(バーの切り替わり時)」のみ実行。トレーリングストップやエラー修正は毎ティック実行されます。
- スプレッドフィルター: 現在のスプレッドが
MaxSpread (初期値50ポイント)を超えている場合は、エントリーを見送ります。
2. 新規エントリーロジック
以下の3つの条件が全て揃ったときに最初のエントリー(初期ポジション)を持ちます。
- 環境認識(マルチタイムトレンド)
- 買い: 日足の短期MA(5) > 長期MA(15) & 4時間足の短期MA(15) > 長期MA(40)
- 売り: 日足の短期MA(5) < 長期MA(15) & 4時間足の短期MA(15) < 長期MA(40)
- プルバック(押し目・戻り目)判定
- 現在の価格と4時間足短期MAの距離が、「15分足ATRの
PullbackAtrMult(10.0)倍」以内であること。(価格が移動平均線から離れすぎていないかを確認)
- エントリートリガー(15分足)
- 買い: 15分足の終値が、1本前は15分足MA(60)より下で、今回は上抜けた時(ゴールデンクロス的な動き)
- 売り: 15分足の終値が、1本前は15分足MA(60)より上で、今回は下抜けた時(デッドクロス的な動き)
- 初期ストップロス(SL):
- 買い: 過去15本の最安値
- 売り: 過去15本の最高値
3. ピラミッディング(増し玉)ロジック
トレンドが継続し、利益が乗った場合にポジションを追加します。
- 発動条件: 現在のポジション数が
MaxPositions(10)未満であること。 - 追加条件: 最後に持ったポジションの建値から、順行方向(利益が出る方向)に 15分足の1ATR以上 価格が進んでいること。
- ロットサイズ:
初期ロット(FixedLot) × PyramidScale(初期値2.0)で計算。 - (例: 初期0.01ロットなら、追加ポジションはすべて0.02ロットになります)
- 追加ポジションのSL: すでに保有しているポジションの最新SL(同期されたSL)と同じ価格に設定されます。
4. 決済・トレーリングストップ仕様
利益を確保するため、全ポジションのストップロス(SL)を同時に引き上げる(下げる)「動的ATRトレーリングストップ」を採用しています。
- 動的トレイリング幅(ポジション数に応じて変化)
ポジション数が増える(=トレンドの終盤に近づく)ほど、利確幅をタイト(狭く)にします。 - 計算式:
TrailingAtrMultMax(8.0) - (ポジション数 - 1) × TrailingAtrMultStep(1.0) - 下限値:
TrailingAtrMultMin(2.0)より小さくはなりません。 - (例: 1ポジション時は8ATR幅で追従、2ポジション時は7ATR幅、7ポジション以上は最小の2ATR幅で追従)
- SL更新ロジック:
- 買い: 「過去20本の最高値 - (ATR × 動的トレイリング幅)」を計算し、現在のSLより上(有利)なら更新。
- 売り: 「過去20本の最安値 + (ATR × 動的トレイリング幅)」を計算し、現在のSLより下(有利)なら更新。
- 決済は指値(TP)を使わず、このトレーリングストップに引っかかった時点での全決済となります。
5. リスク管理・システム堅牢化(エラー対策)
EAが通信エラーやブローカーの仕様で誤作動を起こさないための安全機能が組み込まれています。
- SL未設定の補完:
約定したにもかかわらずサーバーエラー等でSLが設定されなかった場合、既存のポジション群のSL値を取得し、自動でSLを補完します。 - エラーチケットのキュー管理 (
VerifyAndQueueNewPosition,RetryFailedSLModify):
注文が通った後に「ポジションが存在するか」「SLが正しく入っているか」を検証します。失敗した場合は内部のキュー(リスト)に登録し、毎ティックごとにSL設定の再試行(リトライ)を行います。 - ストップレベルの考慮 (
CheckStopLevel):
ブローカーが定める最低ストップレベル(現在価格に近すぎるとSLを置けない仕様)をチェックし、引っかかる場合は許容されるギリギリのラインにSLを補正します。 - EA再起動時の方向復元 (
RestoreTradeDirection):
MT5の再起動やEAの再設定が行われた際、すでに保有しているマジックナンバーのポジションをスキャンし、EAが現在「買いモード」か「売りモード」かを自動で復元します。
パラメータ(初期値)まとめ
- ロット: 0.01ロット(追加時は0.02ロット)
- 最大ポジション数: 10
- 日足MA: 5 / 15
- 4時間足MA: 15 / 40
- 15分足トリガーMA: 60
- トレイリングATR乗数: 初期8.0 → 追加ごとに-1.0(最小2.0まで)
非常に理にかなったトレンドフォロー・ピラミッディングロジックと、実運用におけるエラー対策(SLが入らない問題の解決策)が高度に実装されたプロフェッショナルな仕様のEAです。
TrendFollow_Pyramidの使い方・設定方法
このEA(Momentum Pyramid Master EA v1.10)をMetaTrader 5(MT5)で実際に使用するための手順と、パラメータの設定方法、運用のコツを分かりやすく解説します。
ステップ1:MT5への導入とコンパイル
まず、いただいたコードをMT5で動かせる状態(ex5ファイル)にします。
- MT5を起動し、上部メニューの「ツール」⇒「MetaQuotes言語エディタ(F4)」をクリックします。(MetaEditorが開きます)
- MetaEditorの左上「新規作成」をクリックし、「エキスパートアドバイザ(テンプレート)」を選択して「次へ」へ。
- 名前を「
Experts\Momentum_Pyramid_v1.10」などと入力し、完了まで進みます。 - 開いた画面に最初から書かれているコードをすべて消去し、先ほどご提示いただいたコードをすべて貼り付けます。
- 画面上部の「コンパイル(F7)」ボタンを押します。
- 画面下部の「エラー」タブに
0 errors, 0 warningsと表示されれば成功です。MT5に戻ります。
ステップ2:チャートへの適用
このEAは「15分足」を基準に作られていますので、チャートの設定が重要です。
- MT5でトレードしたい通貨ペアのチャートを開きます。
- ※トレンドフォロー型なので、EURUSD, GBPUSD, USDJPY, XAUUSD(ゴールド) など、トレンドが長く続きやすい銘柄がおすすめです。
- チャートの時間足を 「M15(15分足)」 に変更します。
- 左側の「ナビゲータ」ウィンドウから「エキスパートアドバイザ」を開き、先ほどコンパイルした「
Momentum_Pyramid_v1.10」をチャート上にドラッグ&ドロップします。
ステップ3:設定(パラメータ)の確認
チャートにドロップすると設定画面が開きます。「インプット」タブで以下の設定を確認・変更します。最初はデフォルトでも構いませんが、資金に合わせて調整してください。
- FixedLot(初期ロット):
0.01 - 最初のエントリーロットです。少額からテストすることをおすすめします。
- MaxPositions(最大保有ポジション数):
10 - ピラミッディング(増し玉)を最大何回行うか。リスクを抑えるなら
3や5に減らしてください。 - PyramidScale(増し玉の倍率):
2.0 - 次に持つロット数の倍率です。初期0.01なら、次は0.02になります。同じロットで追加したい場合は
1.0にします。 - MaxSpread(最大許容スプレッド):
50 - 単位は「ポイント」です(50ポイント=5.0pips)。スプレッドがこれより広い時はエントリーしません。ご自身の口座のスプレッドに合わせて調整してください。
- MagicNumber(マジックナンバー):
100010 - EAが自分のポジションを識別するための番号です。同じ口座内で別のEAを動かす場合や、別のチャート(通貨ペア)でこのEAを動かす場合は、必ず違う数字(例: 100011, 100012…)に変更してください。
設定が終わったら、設定画面の「共有」タブで「アルゴリズム取引を許可」にチェックを入れて「OK」を押します。
ステップ4:自動売買の開始
- MT5の画面上部にある 「アルゴリズム取引」 というボタンをクリックして、緑色の再生マーク(ON)にします。
- チャートの右上に表示されている帽子のアイコンが 「青色(または緑色)」 になっていれば、正常に稼働しています。
- 画面下部の「ツールボックス」⇒「エキスパート」タブを開き、
Momentum Pyramid Master EA v1.10 initialized...というメッセージが出ていれば準備完了です。あとは条件を満たすと自動でトレードを開始します。
⚠️ 運用のコツと注意点(必ずお読みください)
- まずはバックテスト(ストラテジーテスター)で試す
いきなりリアル口座で動かすのではなく、MT5の「表示」⇒「ストラテジーテスター」を開き、過去のデータでどのようなトレードをするか(特にピラミッディングによるロットの増加や証拠金の減り方)を確認してください。 - 証拠金に余裕を持たせる
このEAは利益が乗るたびにポジションを追加(倍率2倍なら 0.01 → 0.02 → 0.04…)します。トレンドに乗れば爆発的な利益を生みますが、急反落した際に耐えられるよう、ロット数と最大ポジション数(MaxPositions)は資金に対してかなり低めに設定してください。 - レンジ相場(もみ合い相場)に弱い
トレンドフォロー型の宿命として、相場に方向感がない時は「高値掴み・安値掴み(損切り貧乏)」になりやすいです。ボラティリティ(値動き)の少ない通貨ペアや時間帯には向きません。 - VPS(仮想サーバー)の利用
EAはパソコンが起動してMT5が開いている間しか動きません。特にこのEAは「トレーリングストップ(利益を追従してSLを動かす)」を毎秒計算しているため、24時間動かし続ける必要があります。本番環境ではVPSの利用を強く推奨します。
TrendFollow_Pyramidのバックテスト成績・挙動・取引データなど
2026年4月1日から4月30日まで。資金10,000ドル。パラメーターはデフォルト。ティックはリアルティック。

2026年1月1日から4月30日まで。

挙動は以下のような感じです。ピラミッディングエントリー・トレーリング決済、その後の直近最高値による損切りもきちんと機能しています。

デフォルト運用の場合、1日の収益は200-300ドルほど。0.01-0.02ロットでこの収益は素晴らしいです。

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

他のブローカー(AXIORY・Tradeviewなど)だとゴールドはレバレッジ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「ホームページ」で確認できます。
