含み損が出ているポジションを複数抱えているとき、
「1つ1つは大した額じゃないから」と
合計額を意識せずに放置していないでしょうか。
1つ1つの含み損が小さくても、
同時に持っているポジションの数が増えるほど、
合計の含み損は想像以上に膨らみます。
この記事では、保有中の全ポジションの含み損を合計し、
設定した上限額を超えた瞬間に全ポジションを強制決済し、
その日の新規エントリーを止めるEAの作り方を解説します。
・複数ポジションの含み損を合計する方法
・上限額を超えた瞬間に全決済するロジック
・決済後にその日の新規エントリーだけを止める設計
・実際に動くサンプルコード全体
なぜ「合計の含み損」を見る必要があるのか
個別のポジションだけを見ていると、見落としやすい問題を確認します。
1つ1つは小さくても合計は大きくなる
ナンピンや複数通貨ペアの同時保有では、
含み損が出ているポジションが
いつの間にか3つ、4つと積み重なっていることがあります。
1つあたりの含み損を見ている限り、
「まだ大丈夫」と感じやすい点が落とし穴です。
個別の損切り設定だけでは防げないケース
個々のポジションにストップロスを設定していても、
多数のポジションが同時に逆行すれば、
口座全体としての損失は想定より大きくなります。
この記事で作るEAは、個別のストップロスとは別に、
口座全体を監視するもう一段の安全装置という位置づけです。
全ポジションの含み損を合計する
保有中の全注文をループで走査し、含み損を合計する処理を作ります。
OrdersTotalとOrderSelectで保有中の注文を1件ずつ取り出す
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
// ここで1件ずつ処理する
}
}
`OrdersTotal()`は現在保有中(決済済みを除く)の注文数を返し、
`OrderSelect(i, SELECT_BY_POS, MODE_TRADES)`で
その中のi番目の注文を選択します。
OrderProfit・OrderSwap・OrderCommissionを合計する
double totalFloating = 0.0; totalFloating += OrderProfit() + OrderSwap() + OrderCommission();
`OrderProfit()`は為替差損益のみを返すため、
スワップ(`OrderSwap()`)と手数料(`OrderCommission()`)を
足し合わせないと、口座残高の実際の増減とズレる点に注意してください。
通貨ペアをまたいだ合計でよい理由
この記事では、通貨ペアごとではなく、
口座全体の含み損を1つの数値として合計します。
口座残高という「1つの財布」を守ることが目的のため、
どの通貨ペアで損失が出ているかは区別しません。
上限額を超えたら全決済するロジック
合計した含み損を上限額と比較し、超えた場合の処理を作ります。
マイナス方向のしきい値として比較する
extern double MaxLossAmount = 5000.0;
if(totalFloating <= -MaxLossAmount)
{
CloseAllOrders();
g_StopTradingToday = true;
}
含み損は負の値になるため、
`MaxLossAmount`(正の値で入力)に
マイナスを付けた値と比較します。
OrderCloseで保有中の注文を1件ずつ決済する
void CloseAllOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, 3);
}
}
}
決済すると注文数が減って配列の番号がズレるため、
ループは必ず後ろ(添字の大きい方)から回します。
買いポジションはBidで、売りポジションはAskで決済する点も、
通常のOrderClose呼び出しと同じです。
その日の新規エントリーだけを止めるフラグ設計
if(Day() != g_LastCheckedDay)
{
g_StopTradingToday = false;
g_LastCheckedDay = Day();
}
一度上限に達したら、
その日のうちは新規エントリーを止めたいため、
`g_StopTradingToday`というフラグで制御します。
日付が変わった時点でフラグをリセットし、
翌日は通常どおり取引を再開できるようにします。
実際に動くコード全体
ここまでの要素をまとめたEAの全体です。
//+------------------------------------------------------------------+
//| DailyLossLimitCloseAllEA.mq4 |
//| Copyright 2026, FX-EA System Project Creator |
//| https://creator.fx-ea-system-project.com/ |
//+------------------------------------------------------------------+
extern double MaxLossAmount = 5000.0;
extern int FastMAPeriod = 5;
extern int SlowMAPeriod = 20;
extern double LotSize = 0.1;
bool g_StopTradingToday = false;
int g_LastCheckedDay = -1;
double GetTotalFloating()
{
double total = 0.0;
for(int i = 0; i < OrdersTotal(); i++)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
total += OrderProfit() + OrderSwap() + OrderCommission();
}
return(total);
}
void CloseAllOrders()
{
for(int i = OrdersTotal() - 1; i >= 0; i--)
{
if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
{
double closePrice = (OrderType() == OP_BUY) ? Bid : Ask;
OrderClose(OrderTicket(), OrderLots(), closePrice, 3);
}
}
}
void CheckDailyReset()
{
if(Day() != g_LastCheckedDay)
{
g_StopTradingToday = false;
g_LastCheckedDay = Day();
}
}
void CheckLossLimit()
{
double totalFloating = GetTotalFloating();
if(totalFloating <= -MaxLossAmount && !g_StopTradingToday)
{
CloseAllOrders();
g_StopTradingToday = true;
Print("損失上限に達したため全決済しました。合計含み損=", totalFloating);
}
}
int start()
{
CheckDailyReset();
CheckLossLimit();
if(g_StopTradingToday)
return(0);
double fastMA = iMA(NULL, 0, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
double slowMA = iMA(NULL, 0, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE, 1);
if(OrdersTotal() == 0)
{
if(fastMA > slowMA)
OrderSend(Symbol(), OP_BUY, LotSize, Ask, 3, 0, 0);
if(fastMA < slowMA)
OrderSend(Symbol(), OP_SELL, LotSize, Bid, 3, 0, 0);
}
return(0);
}
MetaEditorでのコンパイル・実行手順
実際にMT4へ適用するまでの手順を確認します。
- MetaEditorで新規EA「DailyLossLimitCloseAllEA」を作成し、上のコードを貼り付けてコンパイルする
- MT4のナビゲーターからチャートへドラッグ&ドロップで適用する
- MaxLossAmountに、許容できる含み損の上限額(口座通貨建て)を入力する
- デモ口座で、わざと含み損を出した状態を作り、上限額を超えた瞬間に全決済されることを確認する
よくあるエラーと注意点
実装・運用でつまずきやすいポイントをまとめます。
決済ループを前から回して注文を取りこぼす
・決済のたびにOrdersTotal()が減り、前から回すと1件飛ばす
・必ずOrdersTotal()-1から0へ向かって後ろから回す
OrderCloseが失敗して一部だけ決済されずに残る
スリッページや価格の急変で
`OrderClose`が失敗することがあります。
戻り値を確認せずに放置すると、
一部のポジションだけが決済されずに残る可能性があるため、
実運用では戻り値がfalseの場合に`GetLastError()`で原因を確認し、
リトライする処理を加えることをおすすめします。
MQL5との違い
このコードはMQL4(`start()`形式、
OrderSend/OrderClose)を前提にしています。
MQL5では`PositionsTotal()`・
`PositionGetTicket()`・
`CTrade::PositionClose()`を使う形に
書き換える必要があり、
含み損の合計も`PositionGetDouble`
(POSITION_PROFIT)等を使う構成に変わります。
よくある質問
損失上限での全決済EAについて、よく検索される疑問に回答します。
口座残高に対する割合(%)で上限を決めるべきですか?
このコードは固定金額での判定ですが、
`AccountBalance()`を使えば
「残高の何%まで」という割合指定にも応用できます。
固定金額か割合かは、資金管理の方針に応じて選んでください。
全決済した後、EA自体を止めるべきですか?
このコードはその日の新規エントリーだけを止め、
EA自体は動かし続ける設計です。
翌日以降も自動で再開したい場合に向いていますが、
重大な局面と判断する場合は手動でEAを外す運用も検討してください。
個別のストップロス設定は不要になりますか?
いいえ、不要にはなりません。
このEAはあくまで口座全体を守る最後の安全装置であり、
個別のポジションには引き続き
ストップロスを設定することをおすすめします。
【MQL4】含み損の合計が一定額を超えたら全ポジションを自動決済し新規エントリーを止めるEAの作り方:まとめ
個別のポジションの含み損が小さくても、
複数のポジションを合計すると、
口座全体では大きな損失になっていることがあります。
OrdersTotal・OrderSelect・OrderCloseを組み合わせれば、
合計の含み損を監視し、
上限額を超えた瞬間に全決済する安全装置を自作できます。
コメント