トレーリングストップは、含み益が伸びるほど損切りライン(SL)を有利な方向へ動かし、利益を守りながらトレンドを追う決済方法です。
MT4のEAでは、自分の注文を1つずつ選び、OrderModify()でSLを書き換える処理を自前で書く必要があります。
MT4(MQL4)EAソースコード公開サイト11選と動かし方MT4(MQL4)のEAソースコードを無料で公開しているサイト11選。付属のMACD Sample、日本語の学習サイト、MQL5コードベース、GitHubをレベル別に紹介し、コンパイルして動かす手順も解説します。
ここでは固定幅・ATR・一定期間の高安値・移動平均の4方式を、MQL4のコード付きで紹介します。
4方式とも、pipsと価格の換算(5桁業者は10倍)とブローカーが決める最小距離(StopLevel)を処理する共通関数の上で動きます。
同じSLの再送で起きるエラー1も、この共通関数で防ぎます。
この記事で紹介するトレーリングストップの4方式
固定幅方式
- 一般的なトレーリングストップ
ATRベースの可変方式
- ボラティリティに応じてトレール幅を変える
一定期間の高値・安値
- 直近の高安値を基準に損切りラインを動かす
移動平均で動的調整
- 移動平均を使って損切り位置を動かす
EAでトレーリングストップを組む前に押さえる前提
4方式のコードに入る前に、どの方式でも共通する前提を整理します。
トレーリングストップの不具合は、方式の選び方よりも、単位の取り違えやブローカーの制限を無視したことが原因で起きるケースがほとんどです。
ここで挙げる4点は、次の節の共通関数にすべて組み込んであります。
MT4標準のトレーリングストップとの違い
MT4には、ターミナルの注文を右クリックして幅を指定する標準のトレーリングストップもあります。
ただし標準機能はMT4を起動している間だけ動き、幅も固定です。
EAで組めば、ATRや移動平均に合わせて幅を変えたり、含み益が一定以上になってから動かし始めたりと、条件を自由に決められます。
pipsを価格に直す(5桁業者は10倍)
Pointは「その通貨ペアの最小価格単位」で、1pipsとは限りません。
小数点以下が3桁・5桁の業者(ドル円なら150.123)では、1pips=Point×10です。
「15pips」をそのまま価格から引くと、ドル円では15円(1,500pips)もずれます。
5桁表示の業者で、トレール幅15pips・スプレッド12ポイントとした計算例です(試算)。
MODE_SPREADはpipsではなくポイント単位で返るため、pips換算の係数を掛けると10倍にずれます。
MT4開発者必見!リスク1%を守るロット計算コードの完全実装ガイドMT4 EA開発でリスク1%を守るロット計算コードを完全解説。MarketInfoを使った実装例や通貨ペアごとの計算方法、落とし穴を紹介します。
StopLevel・FreezeLevelより内側にはSLを置けない
ブローカーは、現在価格からSLまでの最小距離をMODE_STOPLEVEL(ポイント単位)で決めています。
買いならBidから、売りならAskから、この距離より近いSLはOrderModify()がエラー130(無効なストップ)で拒否されます。
さらに、既存のSL・TPが現在価格からMODE_FREEZELEVEL以内に近づいた注文は、変更そのものができません。
OrderModifyは変化があるときだけ送る(エラー1)
SLもTPも前回と同じ値のままOrderModify()を呼ぶと、エラー1(変更なし)が返ります。
毎ティック呼び出すEAでは、新しいSLが今のSLより有利な方向へ動いたときだけ送る判定が欠かせません。
あわせて読む
MQL4のOnTick・OnTimer等イベント関数の使い方MQL4のOnTick・OnTimer・OnInit・OnDeinitなどイベント関数が呼ばれるタイミングと書き方を一覧表とコードで解説。足の確定ごとの判定、MQL5(MT5)での違いやOnTradeTransaction等の固有関数もまとめました。
送る値はNormalizeDouble(価格, Digits)で桁をそろえ、TPにはOrderTakeProfit()を渡して消さないようにします。
自分の注文だけを選ぶ(マジックナンバーとシンボル)
OrderSelect()で注文を選ぶとき、通貨ペアとマジックナンバーで絞らないと、手動注文や別のEAの注文までSLが動きます。
OrderSelect()の戻り値も確認し、選べなかった注文は飛ばします。
トレーリングストップでよく出るエラーコードと原因
OrderModify()がfalseを返したら、GetLastError()の番号で原因を切り分けます。
エラー1は実害こそ小さいものの、毎ティック出るとログが埋まり、本当のエラーを見落とします。
固定幅(pips指定)のトレーリングストップはどう書く?
固定幅方式は、含み益が開始幅(startPips)を超えたら、現在価格から一定のpips(stopPips)だけ離れた位置にSLを追従させる、最も基本的な方式です。
固定幅トレーリングストップの動き(買い注文)
※開始幅(startPips)15pips・トレール幅(stopPips)15pips・初期SLはエントリーの20pips下とした架空の値動きの例です。
// このEAの注文だけを対象にするためのマジックナンバー
input int MagicNumber = 12345;
// 毎ティック呼び出す
void OnTick() {
// 含み益が20pipsを超えたら、現在価格の15pips後ろにSLを追従させる
trailAllOrders(20.0, 15.0);
}
// 自分の注文(同じ通貨ペア・同じマジックナンバー)だけを選んでトレールする
void trailAllOrders(double startPips, double stopPips) {
for (int i = OrdersTotal() - 1; i >= 0; i--) {
// 選択に失敗した注文は飛ばす
if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES)) continue;
if (OrderSymbol() != Symbol() || OrderMagicNumber() != MagicNumber) continue;
// 待機注文(指値・逆指値)は対象外
if (OrderType() != OP_BUY && OrderType() != OP_SELL) continue;
// ここを trailingStopATR / trailingStopHL / TrailingByMA に差し替えると他の方式になる
orderTrailingStopGeneral(startPips, stopPips);
}
}
// 固定幅のトレーリングストップ(幅はpipsで指定)
void orderTrailingStopGeneral(double startPips, double stopPips) {
string symbol = OrderSymbol();
double pip = pipsToPrice(symbol);
double bid = MarketInfo(symbol, MODE_BID);
double ask = MarketInfo(symbol, MODE_ASK);
if (OrderType() == OP_BUY) {
// 買いはBidで決済されるので、含み益もSLもBid基準で計算する
if (bid - OrderOpenPrice() >= startPips * pip)
commonTrailingStop(bid - stopPips * pip);
}
else if (OrderType() == OP_SELL) {
// 売りはAskで決済されるので、含み益もSLもAsk基準で計算する
if (OrderOpenPrice() - ask >= startPips * pip)
commonTrailingStop(ask + stopPips * pip);
}
}
// 新しいSL候補が「今より有利」かつ「ブローカーの制限内」のときだけOrderModifyする
bool commonTrailingStop(double newStopLoss) {
string symbol = OrderSymbol();
int digits = (int)MarketInfo(symbol, MODE_DIGITS);
double point = MarketInfo(symbol, MODE_POINT);
double bid = MarketInfo(symbol, MODE_BID);
double ask = MarketInfo(symbol, MODE_ASK);
// StopLevel・FreezeLevelはポイント単位で返るので、Pointを掛けて価格に直す
double stopLevel = MarketInfo(symbol, MODE_STOPLEVEL) * point;
double freezeLevel = MarketInfo(symbol, MODE_FREEZELEVEL) * point;
double curSL = OrderStopLoss();
double curTP = OrderTakeProfit();
// 価格の桁数をそろえる
newStopLoss = NormalizeDouble(newStopLoss, digits);
if (OrderType() == OP_BUY) {
// SLはBidからStopLevel以上離れた下側にしか置けない
if (bid - newStopLoss < stopLevel) return(false);
// TPを変えなくても、既存のTPがBidからStopLevel以内だと変更できない(エラー130)
if (curTP != 0.0 && curTP - bid < stopLevel) return(false);
// 今のSLよりPointの半分以上上がらなければ送らない(エラー1の防止)
if (curSL != 0.0 && newStopLoss < curSL + point * 0.5) return(false);
// 既存のSL・TPがFreezeLevel以内に入っていたら変更できない
if ((curSL != 0.0 && bid - curSL <= freezeLevel) || (curTP != 0.0 && curTP - bid <= freezeLevel)) return(false);
}
else if (OrderType() == OP_SELL) {
// SLはAskからStopLevel以上離れた上側にしか置けない
if (newStopLoss - ask < stopLevel) return(false);
// 既存のTPがAskからStopLevel以内でも変更できない(エラー130)
if (curTP != 0.0 && ask - curTP < stopLevel) return(false);
if (curSL != 0.0 && newStopLoss > curSL - point * 0.5) return(false);
if ((curSL != 0.0 && curSL - ask <= freezeLevel) || (curTP != 0.0 && ask - curTP <= freezeLevel)) return(false);
}
else return(false);
// TP(利確)は今の値をそのまま渡して消さない
if (!OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, curTP, 0, clrYellow)) {
Print("OrderModify失敗 ticket=", OrderTicket(), " error=", GetLastError());
return(false);
}
return(true);
}
// 1pipsを価格に換算する(3桁・5桁業者はPoint×10)
double pipsToPrice(string symbol) {
int digits = (int)MarketInfo(symbol, MODE_DIGITS);
double point = MarketInfo(symbol, MODE_POINT);
if (digits == 3 || digits == 5) return(point * 10.0);
return(point);
}
trailAllOrders()が自分の注文を1件ずつ選び、選んだ注文に方式ごとの関数を呼びます。
この後のATR・高安値・移動平均の関数も、trailAllOrders()の中のorderTrailingStopGeneral()と差し替えるだけで使えます。commonTrailingStop()とpipsToPrice()は4方式で共通です。
※2026年9月に、pipsを価格に換算していなかった点・スプレッドの単位・TPを0で上書きしていた点などを修正し、コードを差し替えました。
ATRでトレール幅を変えるトレーリングストップ
相場のボラティリティに応じて損切り位置を柔軟に調整したい場合、ATRを使ったトレーリングストップは有効です。
一般的な固定幅のトレーリングストップでは対応しきれない相場の変動に対応できるため、不必要な損切りを回避しやすいのが最大の利点です。
ここでは、2種類のATR期間を使い、大きい方の値を基準としてトレール幅を決定する手法を紹介します。
なぜATRを使うのか?
ATRを使う理由は2つあります。
- 相場が活発なとき:トレール幅を広げることで、ノイズによる早すぎる損切りを防ぐ
- 相場が静かなとき:トレール幅を狭めて、利益確定を迅速に行う
結果として、市場の状況に応じた合理的なトレーリングが可能になります。
以下の関数は、2つのATR期間を使用し、大きい方に倍率をかけた値をトレール幅とする設計です。
// ATRでトレール幅を変えるトレーリングストップ(対象の注文をOrderSelectしてから呼ぶ)
void trailingStopATR(int ATRPeriod1, int ATRPeriod2, double ATRMulti) {
string symbol = OrderSymbol();
// 確定した1本前の足のATRを2期間で計算し、大きいほうを使う
double atr = MathMax(iATR(symbol, 0, ATRPeriod1, 1), iATR(symbol, 0, ATRPeriod2, 1));
// ATRは価格単位の値なので、pips換算は不要
double stop = atr * ATRMulti;
// スプレッドはポイント単位で返るので、Pointを掛けて価格に直す
double spread = MarketInfo(symbol, MODE_SPREAD) * MarketInfo(symbol, MODE_POINT);
if (OrderType() == OP_BUY) {
// SL候補 = 1本前の高値 − ATR×倍率
commonTrailingStop(iHigh(symbol, 0, 1) - stop);
}
else if (OrderType() == OP_SELL) {
// チャートの安値はBid基準。売りのSLはAskで判定されるのでスプレッドを足す
commonTrailingStop(iLow(symbol, 0, 1) + stop + spread);
}
}
補足:トレール幅の計算方法
計算例です(試算)。ドル円でATR(14)=0.180、ATR(50)=0.220、ATRMulti=2.0とします。
トレール幅は、大きい方の0.220×2.0=0.440(44pips)です。
直前の足の高値が150.500なら、買いのSL候補は150.500−0.440=150.060になります。
2つのATRを計算し、大きい方に倍率を掛けることで、相場が急変したときでも柔軟に対応可能になります。
注意点・相場が極端に荒れているときはトレール幅が広がりすぎて、利益確定が遠のくことがあります。ATRMultiは相場に合わせて調整してください。
・ATRの期間の最適値は、スキャルピング・デイトレ・スイングといった取引スタイルによって変わります。
直近の高値・安値を使うトレーリングストップ
一定期間の高値や安値を基準に損切り価格を動かすトレーリングストップ手法です。
相場の「反転しやすいポイント」を活用することで、より自然なトレンドフォローとリスク管理が可能になります。
この方法では、直近のローソク足の高値・安値を基準に、損切り位置(ストップロス)を動的に調整します。
つまり、過去の値動きを踏まえながら、今の相場に沿った位置へ損切りラインを追従させていきます。
// 直近の高値・安値を使うトレーリングストップ(対象の注文をOrderSelectしてから呼ぶ)
void trailingStopHL(int candleNum) {
string symbol = OrderSymbol();
// スプレッドはポイント単位で返るので、Pointを掛けて価格に直す
double spread = MarketInfo(symbol, MODE_SPREAD) * MarketInfo(symbol, MODE_POINT);
if (OrderType() == OP_BUY) {
// 確定足candleNum本の最安値(チャートの安値も買いのSLもBid基準)
commonTrailingStop(iLow(symbol, 0, iLowest(symbol, 0, MODE_LOW, candleNum, 1)));
}
else if (OrderType() == OP_SELL) {
// 最高値はBid基準なので、Ask基準の売りSLにそろえるためスプレッドを足す
commonTrailingStop(iHigh(symbol, 0, iHighest(symbol, 0, MODE_HIGH, candleNum, 1)) + spread);
}
}
BUYポジションの場合
買い注文では、指定期間(確定足candleNum本)の最安値を新たな損切りライン候補とします。
MT4のチャートの安値はBid基準で、買いのSLもBidで判定されるため、スプレッドを足す必要はありません。
SELLポジションの場合売り注文の場合は、指定期間の最高値+スプレッド分を新しい損切り候補とします。
チャートの高値はBid基準ですが、売りのSLはAskで判定されます。スプレッドを足さないと、高値を超えていないのにスプレッドの分だけ早く損切りされます。
価格が再びその高値を超えるようであれば、下降トレンドが崩れたと判断し、ポジションをクローズします。
注意点ローソク足の本数(candleNum)の選定が重要:短すぎるとダマシに弱く、長すぎると反応が鈍くなる
移動平均(MA)を使うトレーリングストップ
移動平均方式は、MAの値から一定幅(indentPips)だけ離した位置をSLにする方式です。トレンドが続く間は、MAとともにSLも切り上がります(売りなら切り下がります)。
チャートの一定期間における価格の平均を参照して、損切り位置を動的に調整するため、相場の流れに応じて柔軟な対応が可能です。
// 移動平均を使うトレーリングストップ(対象の注文をOrderSelectしてから呼ぶ)
// indentPips: MAからSLまでの余裕(pips)
void TrailingByMA(int timeFrame, int MAPeriod, int MAShift, int MAMethod, int applPrice, int shift, double indentPips) {
string symbol = OrderSymbol();
double indent = indentPips * pipsToPrice(symbol);
// スプレッドはポイント単位で返るので、Pointを掛けて価格に直す
double spread = MarketInfo(symbol, MODE_SPREAD) * MarketInfo(symbol, MODE_POINT);
double ma = iMA(symbol, timeFrame, MAPeriod, MAShift, MAMethod, applPrice, shift);
if (OrderType() == OP_BUY) {
commonTrailingStop(ma - indent);
}
else if (OrderType() == OP_SELL) {
// MA(Bid基準の終値から計算)にスプレッドを足してAsk基準にそろえる
commonTrailingStop(ma + indent + spread);
}
}
BUY注文の場合
MA値から、indentPipsを価格に換算した幅を引いた価格が、トレーリングストップの新しい候補です。
つまり、現在価格が移動平均よりも上にあることを前提として、下方向に一定の余裕を持たせて損切りラインを設定します。
SELL注文の場合逆に、MA値にindentPipsの幅とスプレッド(買値と売値の差)を加えた価格が損切りライン候補です。
価格がMAより下にあることを前提とし、上方向に余裕をもたせて損切りラインを設定します。
注意点•MAのパラメータ設定(期間・種類)によっては過度に反応が遅くなることもある
トレーリングストップはどの相場で使い分ける?
同じトレーリングストップでも、相場状況やトレードスタイルに合わないと効果を発揮できません。
代表的な3つの場面で、向き・不向きを整理します。
トレーリングストップの使い分け
トレンドフォロー型の戦略
- トレンドを追いかけて利益を伸ばせる
- トレーリングストップと相性がよい
レンジ相場
- トレーリングストップが早く発動し、利益を伸ばしにくい
- 固定のストップロス・テイクプロフィットのほうがよいことも
高ボラティリティの環境
- トレール幅を広く取り、一時的な変動による損切りを防ぐ
トレンドフォロー型の戦略を用いる場合
トレーリングストップは、特にトレンドフォロー型のEAと相性がよいです。
市場が一定の方向に動き続ける場合、トレーリングストップを用いると、トレンドを追いかけて利益を得られるからです。
レンジ相場で取引を行う場合
一方、レンジ相場ではトレーリングストップは必ずしも最善の手段とは言えません。
なぜなら、価格が一定範囲内で上下に動くため、トレーリングストップが早くに発動してしまい、利益を十分に伸ばせない可能性があるからです。
この場合、固定のストップロスやテイクプロフィットを設定する方が良い結果になることもあります。
高ボラティリティの環境で取引を行う場合
高ボラティリティ(値動きが大きい相場)の環境では、価格が急激に上下します。
トレーリングストップの幅を広く取れば、一時的な値動きによる損切りを防げます。ATR方式のように、幅を値動きに合わせて自動で広げる方法が向いています。
トレーリングストップを使う市販EAの例
トレーリングストップを使ったEAで有名なので当サイトでも紹介している「一本勝ち」というEAです。
2026年9月時点のゴゴジャンの掲載情報は次のとおりです。
このEAは、レートが急激に動いた場面でトレーリングストップにより利益を伸ばす設計です。
利確幅を固定しないため、トレンドが続く限り利益を伸ばせる設計です。一方で、上の表のとおりプロフィットファクターは1.13、最大ドローダウンは17.68%で、損失の局面もあります。
実際に私もこのEAのトレーリングストップのおかげで、相場が予想外に急激に動いたときに大きな利益を得た経験があります。
あわせて読む
MT4 EA「一本勝ち」のロジックと12ストラテジー分散を検証MT4 EA「一本勝ち」をロジックと12ストラテジーの合成バックテストで検証。1/12ロット分散はアクエリアス単独よりPFが1.29→1.35に上がり、ドローダウンは半分以下に。分散稼働の設定手順も紹介。
MQL4のトレーリングストップでよくある質問
MQL4でトレーリングストップを組むときに、つまずきやすい点をQ&Aにまとめました。エラー番号・pips換算・TPの扱いなど、コードの不具合につながる質問が中心です。詳しい理由は、各方式の節と前提の節もあわせて確認してください。
Q1. EAのトレーリングストップはMT4を閉じても動きますか?
A1. いいえ、動きません。EAのトレーリングストップはMT4が起動し、EAが稼働している間だけSLを動かします。PCを閉じる運用なら、MT4をVPSで24時間動かす必要があります。
あわせて読む
MT4のVPSおすすめ4社比較|実質月額とEA数別の選び方MT4のEAを24時間動かすVPSを、お名前.com・ABLENET・さくら・ConoHaの4社で比較。2026年9月の公式料金にRDSライセンス代を足した実質月額と、MT4の稼働数に合うメモリの選び方がわかります。
Q2. OrderModifyでエラー1が出るのはなぜですか?
A2. 前回と同じSL・TPでOrderModify()を送ったためです。新しいSLが今のSLより有利な方向へPointの半分以上動いたときだけ送る判定を入れると止まります。
Q3. トレーリングストップでエラー130が出る原因は?
A3. SLが現在価格に近すぎるか、置けない側にあることが主な原因です。買いはBidから、売りはAskからMODE_STOPLEVEL×Point以上離し、買いのSLはBidより下、売りのSLはAskより上に置きます。TPを変えなくても、既存のTPが現在価格からStopLevel以内に近づいていると同じエラーになります。
Q4. 5桁業者でpipsを価格に換算するには?
A4. Digitsが3または5なら、1pips=Point×10です。ドル円(3桁)の1pipsは0.01、ユーロドル(5桁)の1pipsは0.0001になります。
Q5. トレーリングストップを動かすとTP(利確)が消えるのはなぜ?
A5. OrderModify()のTP引数に0を渡しているためです。TPを変えないときはOrderTakeProfit()をそのまま渡します。
Q6. 手動注文や別のEAと同じ口座で使っても大丈夫ですか?
A6. 注文を選ぶときにOrderSymbol()とOrderMagicNumber()で絞り込めば、ほかの注文のSLは動きません。手動注文のマジックナンバーは0です。
Q7. トレーリングストップの動きはバックテストで確認できますか?
A7. はい。MT4のストラテジーテスターでビジュアルモードを使うと、SLの線がローソク足に合わせて動く様子を確認できます。
まとめ
今回はMT4のEA作成に使えるトレーリングストップのソースコードを、固定幅・ATR・高安値・移動平均の4方式で紹介しました。
どの方式でも、pipsの価格換算・StopLevelの確認・変化があるときだけのOrderModify()・マジックナンバーでの絞り込みの4点は共通です。
最初は固定幅の方式をストラテジーテスターで動かし、SLが想定どおりの位置に動くかを確かめてから、ATRや移動平均の方式に広げると原因を切り分けやすくなります。
あわせて読む
FX EAの自作・作り方|MQL4で始める初心者向けEA開発入門FXのEAはMT4付属のMetaEditorとMQL4で無料で自作できます。必要なもの・難しさ、ひな形作成からコンパイル・バックテストまでの手順と全コード、未経験者向けのノーコードツール・ChatGPT・作成代行も解説します。
※本記事は情報提供を目的としたもので、特定のEA・金融商品の売買を推奨するものではありません。FX(外国為替証拠金取引)は元本が保証されておらず、レバレッジにより預けた証拠金を上回る損失が発生する可能性があります。記事内の成績・数値は過去の実績であり、将来の利益を保証するものではありません。投資の最終判断はご自身の責任でお願いします。