This is a very simple trade management EA that will close all your Open Orders (or both Open and Pending Orders) when your drawdown against balance exceeds a set percentage limit.
E.g. Balance = $10,000 and “DrawdownPercent” is set at 2.0, if floating P&L is greater than -$200.00, all Orders will be closed.
//+------------------------------------------------------------------+ //| Drawdown Percent Close.mq4 | //| Copyright 2012-2018, www.raidenworks.com | //| contact@raidenworks.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2012-2018, www.raidenworks.com" #property link "contact@raidenworks.com" #define NL "\n" extern string _1 = "Settings"; extern double DrawdownPercent = 2.0; //e.g. for 2% drawdown extern int MaxSlippage = 3; //maximum pips slippage allowed (auto-adjusts for 4-digit or 5-digit brokers) extern bool ClosePendingToo = False; //set to True to close Pending Orders too extern string _2 = "Display"; extern int TopPadding = 0; //to shift Display X number of lines down from topleft corner extern int LeftPadding = 0; //to shift Display X number of spaces right from topleft corner int mt; int init(){ if(Digits==3||Digits==5)mt=10; else mt=1; return(0); } int start(){ if((1-AccountEquity()/AccountBalance())*100>NormalizeDouble(DrawdownPercent,2)){ if(ClosePendingToo){ CloseOpenOrders(); ClosePendingOrders(); } else CloseOpenOrders(); } Display(); return(0); } void CloseOpenOrders(){ bool result=false; for(int i=0;i<OrdersTotal();i++){ OrderSelect(i,SELECT_BY_POS); if(OrderType()==OP_BUY||OrderType()==OP_SELL)result=OrderClose(OrderTicket(),OrderLots(),OrderClosePrice(),MaxSlippage*mt,Blue); if(!result)Print("CloseOpenOrders failed with error#",GetLastError()); } } void ClosePendingOrders(){ bool result=false; for(int i=0;i<OrdersTotal();i++){ OrderSelect(i,SELECT_BY_POS); if(OrderType()==OP_BUYSTOP||OrderType()==OP_BUYLIMIT||OrderType()==OP_SELLSTOP||OrderType()==OP_SELLLIMIT)result=OrderDelete(OrderTicket()); if(!result)Print("ClosePendingOrders failed with error#",GetLastError()); } } void Display(){ string TOP=""; for(int line=0;line<TopPadding;line++)TOP=TOP+NL; string LEFT=""; for(int space=0;space<LeftPadding;space++)LEFT=LEFT+" "; string A1=StringConcatenate(TOP,NL); string A2=StringConcatenate(LEFT,"Balance: $",DoubleToStr(AccountBalance(),2),NL); string A3=StringConcatenate(LEFT,"Equity: $",DoubleToStr(AccountEquity(),2),NL); string A4=StringConcatenate(LEFT,"DD%Close: ",DoubleToStr(DrawdownPercent,2),NL); string A5=StringConcatenate(LEFT,"DD%Current: ",DoubleToStr((1-AccountEquity()/AccountBalance())*100,2),NL); string Display=StringConcatenate(A1,A2,A3,A4,A5); Comment(Display); return (0); }
A simple text display is included for some visual feedback.
Basic notes: It only needs to be attached to one chart to function. Ideally attach to the same currency pair as the one you trade on. If trading on multiple pairs, attach it to the one with the most frequent ticks.
Advanced notes: I’ve played around in the past with a constant regular refresh, i.e. regardless of whether ticks come in or not, the code still runs a pass every X milliseconds; however, too low a resolution and the EA will not capture a fast market properly, too high and it sucks memory and is redundant for quiet markets. Hence, I’ve not opted for it.
Update: 2021.06.25
Been meaning to update this for a while now as a potential issue is the way Positions are closed. We should always target to close the youngest Position first then go on to the next youngest, and all the way to the oldest.
That is, we should count down:
for(int i = OrdersTotal() - 1; i >= 0; i--)
Instead of count up:
for(int i = 0; i < OrdersTotal(); i++)
There’s a few good reasons why but I’m too lazy to explain.
Took the opportunity to write more robust code for multiple Position-closing and Pending Order-deletion attempts. It’s also optimised a little better and might actually be marginally faster.
Intentionally kept the display lean but added a bit more information. It shows the amount of Account Currency your set “Drawdown Trigger Percentage” is.
And obviously test it out on a demo account first to get an feel of it.


// +------------------------------------------------------------------+
// | Drawdown Percent Close.mq4 |
// +------------------------------------------------------------------+
// MIT License
// Copyright © 2021 Michael Ng
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software
// and associated documentation files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
// BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#property link "www.raidenworks.com"
#property version "21.06"
#property strict
//-------------------------------------------------------------------------------------------------
input string _1 = ""; //_____Settings_____
input double e_d_DrawdownTrigger_Percent = 2.0; //Drawdown Trigger Percentage
input int e_i_MaxSlippage_Points = 30; //Maximum Slippage in Points
input bool e_b_DeletePendingToo = False; //Delete Pending Orders on trigger too
input string _2 = ""; //_____Display_____
input int e_i_TopPadding = 0; //Top Padding
input int e_i_LeftPadding = 0; //Left Padding
//-------------------------------------------------------------------------------------------------
string gi_s_AccountCurrency = "";
double g_d_AccountEquity = 0.0;
double g_d_AccountBalance = 0.0;
double g_d_CurrentDrawdown = 0.0;
double g_d_CurrentDrawdown_Percent = 0.0;
double g_d_CurrentDrawdown_AccountCurrency = 0.0;
//-------------------------------------------------------------------------------------------------
int OnInit(){
gi_s_AccountCurrency = AccountCurrency();
return(INIT_SUCCEEDED);
}
//-------------------------------------------------------------------------------------------------
void OnTick(){
g_d_AccountEquity = AccountEquity();
g_d_AccountBalance = AccountBalance();
g_d_CurrentDrawdown = g_d_AccountBalance - g_d_AccountEquity;
g_d_CurrentDrawdown_Percent = (1 - g_d_AccountEquity / g_d_AccountBalance) * 100;
g_d_CurrentDrawdown_AccountCurrency = g_d_AccountBalance * e_d_DrawdownTrigger_Percent / 100;
if(g_d_CurrentDrawdown_Percent > e_d_DrawdownTrigger_Percent){
CloseAllPositions(e_i_MaxSlippage_Points);
if(e_b_DeletePendingToo)DeleteAllPendingOrders();
}
Display();
}
//-------------------------------------------------------------------------------------------------
void Display(){
string TOP = "";
for(int line = 0; line < e_i_TopPadding; line++)TOP = TOP + "\n";
string LEFT = "";
for(int space = 0; space < e_i_LeftPadding; space++)LEFT = LEFT + " ";
string A1 = StringConcatenate(TOP);
string A2 = StringConcatenate(LEFT, "Balance: ", DoubleToStr(g_d_AccountBalance, 2), " ", gi_s_AccountCurrency, "\n");
string A3 = StringConcatenate(LEFT, "Equity: ", DoubleToStr(g_d_AccountEquity, 2), " ", gi_s_AccountCurrency, "\n");
string A4 = StringConcatenate(LEFT, "DD Trigger: ", DoubleToStr(g_d_CurrentDrawdown_AccountCurrency, 2), " ", gi_s_AccountCurrency, "\n");
string A5 = StringConcatenate(LEFT, "DD Current: ", DoubleToStr(g_d_CurrentDrawdown, 2), " ", gi_s_AccountCurrency, "\n");
string A6 = "\n";
string A7 = StringConcatenate(LEFT, "DD Trigger %: ", DoubleToStr(e_d_DrawdownTrigger_Percent, 2), "\n");
string A8 = StringConcatenate(LEFT, "DD Current %: ", DoubleToStr(g_d_CurrentDrawdown_Percent, 2));
Comment(StringConcatenate(A1,A2,A3,A4,A5,A6,A7,A8));
}
//-------------------------------------------------------------------------------------------------
// Purpose: Return OrderTicket of YOUNGEST detected Position
// Usage: CloseAllPositions()
// Outputs: function int OrderTicket()
int TicketOfYoungestPosition(){
for(int i = OrdersTotal() - 1; i >= 0; i--){
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))return(0);
if(OrderType() < 2)return(OrderTicket()); //OP_BUY = 0, OP_SELL = 1
}
return(0);
}
//-------------------------------------------------------------------------------------------------
// Purpose: Return OrderTicket of YOUNGEST detected Pending Order
// Usage: DeleteAllPendingOrders()
// Outputs: function int OrderTicket()
int TicketOfYoungestPendingOrder(){
for(int i = OrdersTotal() - 1; i >= 0; i--){
if(!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))return(0);
if(OrderType() > 1)return(OrderTicket()); //OP_BUY = 0, OP_SELL = 1, OP_BUYLIMIT = 2, OP_SELLLIMIT = 3, OP_BUYSTOP = 4, OP_SELLSTOP = 5
}
return(0);
}
//-------------------------------------------------------------------------------------------------
// Purpose: Close all Positions
// Usage: OnTick()
// Inputs: function int TicketOfYoungestPosition()
// Outputs: bool
// function bool OrderClose()
// function string ErrorDescription()
// NB: 'while' loop continues as long as TicketOfYoungestPosition() > 0, which itself has a loop to get the OrderTickets of existing Positions
bool CloseAllPositions(const int &p_i_MaxSlippage_Points){
int l_i_OrderTicket = 0, l_i_attempts = 3;
bool l_b_result = false;
while(l_i_attempts > 0){
l_i_OrderTicket = TicketOfYoungestPosition();
if(l_i_OrderTicket > 0){
l_b_result = OrderClose(l_i_OrderTicket, OrderLots(), OrderClosePrice(), p_i_MaxSlippage_Points, clrBlue); //we can call OrderLots(), OrderClosePrice() directly here as latest OrderSelect() is in TicketOfYoungestPosition() immediately above
if(!l_b_result){
Print(TimeCurrent(), " CloseAllPositions() failed to close Position #", l_i_OrderTicket, " : Error #", GetLastError());
l_i_attempts--;
Sleep(2000);
continue; //check this order again //do not need to OrderSelect(l_i_OrderTicket, SELECT_BY_TICKET, MODE_TRADES) to refresh OrderClosePrice() as TicketOfYoungestPosition() in next iteration of loop already does it
}
continue; //l_b_result = true; check next order
}
return(true); //return(true) as no Positions left
}
Print(TimeCurrent(), " Reached maximum attempts to close Positions");
return(false);
}
//-------------------------------------------------------------------------------------------------
// Purpose: Delete all Pending Orders
// Usage: OnTick()
// Inputs: function int TicketOfYoungestPendingOrder()
// Outputs: bool
// function bool OrderDelete()
// function string ErrorDescription()
// NB: 'while' loop continues as long as TicketOfYoungestPendingOrder() > 0, which itself has a loop to get the OrderTickets of existing Pending Orders
bool DeleteAllPendingOrders(){
int l_i_OrderTicket = 0, l_i_attempts = 3;
bool l_b_result = false;
while(l_i_attempts > 0){
l_i_OrderTicket = TicketOfYoungestPendingOrder();
if(l_i_OrderTicket > 0){
l_b_result = OrderDelete(l_i_OrderTicket);
if(!l_b_result){
Print(TimeCurrent(), " DeleteAllPendingOrders() failed to delete Pending Order #", l_i_OrderTicket, " : Error #", GetLastError());
l_i_attempts--;
Sleep(2000);
continue; //check this order again
}
continue; //l_b_result = true; check next order
}
return(true); //return(true) as no Pending Orders left
}
Print(TimeCurrent(), " Reached maximum attempts to close Pending Orders");
return(false);
}
Thanks for making this available, it’s very helpful!
This is really great. Thanks for sharing. Would it be possible for you to add the functionality to disable Auto Trading when the trigger is hit and also to generate push notification>
For notifications, you can put a SendNotification() (https://docs.mql4.com/common/sendnotification) immediately after the Position-closing logic within the main OnTick() function.
e.g.
This notification gets sent to your mobile MT4 app and you’ll need to configure both applications accordingly.
(https://www.metatrader4.com/en/trading-platform/help/setup/settings_notifications)
For disabling Auto Trading, you can technically use the keybd_event() function of user32.dll to unpress the “AutoTrading” button, however, it is not advisable to use another EA to manage another EA’s logic.
Each EA runs asynchronously (ever since MT4 build > ~640), as such you might have one EA constantly closing the freshly opened position of another EA, causing you to lose the spread every time this occurs. Which can be every tick!)
Hi Michael thanks for this, really helpful. How would you change this to target biggest loss trades first instead? So it doesn’t closes all trades just reduces the drawdown to an acceptable level?
Thanks
I wouldn’t advise it as the AccountBalance() changes when a position is closed. This causes your Current Drawdown % to change and it makes it impractical to know pre-trade exactly how much you are possibly going to lose. And if you’re going into trades without knowing how much you are going to risk, it’s a recipe for disaster.
I agree for normal use I don’t think it would be wise. However I think it would be quite useful when trying to complete a prop firm challenge.
For example if the profit target is to achieve 5% with a max drawdown of 5% in 1 month and I did hit a drawdown of 5% and had to close all my trades I would be at least -5% profit and it is even harder to achieve the profit target within the time limit to complete the challenge. On the other hand if trades were closed out one by one maintaining the max drawdown without violating the rule, you would prelong -5% loss (which is hard to comeback from in a challenge anyway) as long as possible and there could potentially be a pullback round the corner recovering the drawdown by only sustaining small losses therefore keeping the challenge achievable. Just a thought, its an issue I’ve had and prolonging the loss would be better than nothing in this situation only.
Than you for sharing, this is something i have been looking for a while to add to my EA
can someone share with me the ex4 file because my editor shows errors
Are you using the version as posted on 2021.06.25? If so, could you paste the error messages?
Metaquotes updates their platform frequently and there might be a non-backward compatible change.
oh hey have fixed it.
1 question could you add the function that all orders are closed?
And all ea´s are deleted and mt4 is closed then i could use it also for my live account ea´s ?