Skip to content

Commit

Permalink
1.08
Browse files Browse the repository at this point in the history
1. Added an input parameter (DoNotDisableEquityTS) to not disable equity trailing stop upon triggering.
2. Added an input parameter (AlertOnEquityTS) for alerts upon equity trailing stop closing trades.
3. Added an input parameter (CloseMostDistantFirst) to specify closing order for positions.
4. Renamed input parameter DoNotResetConditions to DoNotDisableConditions and DoNotResetActions to DoNotDisableActions.
5. Fixed a bug that prevented position closing when log file name wasn't specified.
6. Fixed a bug that prevented logging from working sometimes.
  • Loading branch information
EarnForex committed Jun 15, 2022
1 parent 4676abd commit b3c3f53
Show file tree
Hide file tree
Showing 6 changed files with 45 additions and 35 deletions.
11 changes: 7 additions & 4 deletions MQL4/Experts/Account Protector/Account Protector.mq4
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
//+------------------------------------------------------------------+
#property copyright "EarnForex.com"
#property link "https://www.earnforex.com/metatrader-expert-advisors/Account-Protector/"
#property version "1.07"
string Version = "1.07";
#property version "1.08"
string Version = "1.08";
#property strict

#property description "Protects account balance by applying given actions when set conditions trigger."
Expand All @@ -19,8 +19,10 @@ input int Slippage = 2; // Slippage
input string LogFileName = "log.txt"; // Log file name
input Enable EnableEmergencyButton = No; // Enable emergency button
input bool PanelOnTopOfChart = true; // PanelOnTopOfChart: Draw chart as background?
input bool DoNotResetConditions = false; // DoNotResetConditions: Don't reset conditions on trigger?
input bool DoNotResetActions = false; // DoNotResetActions: if true, actions won't be reset on trigger.
input bool DoNotDisableConditions = false; // DoNotDisableConditions: Don't disable conditions on trigger?
input bool DoNotDisableActions = false; // DoNotDisableActions: Don't disable actions on trigger?
input bool DoNotDisableEquityTS = false; // DoNotDisableEquityTS: Don't disable equity TS on trigger?
input bool AlertOnEquityTS = false; // AlertOnEquityTS: Alert when equity trailing stop triggers?
input bool DisableFloatLossRisePerc = false; // Disable floating loss rises % condition.
input bool DisableFloatLossFallPerc = true; // Disable floating loss falls % condition.
input bool DisableFloatLossRiseCurr = false; // Disable floating loss rises currency units condition.
Expand Down Expand Up @@ -51,6 +53,7 @@ input int DelayOrderClose = 0; // DelayOrderClose: Delay in milliseconds.
input bool UseTotalVolume = false; // UseTotalVolume: enable if trading with many small trades and partial position closing.
input double AdditionalFunds = 0; // AdditionalFunds: Added to balance, equity, and free margin.
input string Instruments = ""; // Instruments: Default list of trading instruments for order filtering.
input bool CloseMostDistantFirst = false; // CloseMostDistantFirst: Close most distant trades first?

CAccountProtector ExtDialog;

Expand Down
59 changes: 35 additions & 24 deletions MQL4/Experts/Account Protector/Account Protector.mqh
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ private:
double m_DPIScale;
bool NoPanelMaximization; // Crutch variable to prevent panel maximization when Maximize() is called at the indicator's initialization.
CArrayLong *PartiallyClosedOrders; // Stores order tickets that have been partially closed by Eliminate_Orders().
double PositionsByProfit[][2]; // Stores position (order) ticket and its floating profit/loss. Gets filled and sorted when some condition is met and actions are about to get triggered.
double PositionsByProfit[][2]; // Stores position (order) ticket and its floating profit/loss. When CloseMostDistantFirst == true, the profit is replaced with absolute distance. Gets filled and sorted when some condition is met and actions are about to get triggered.
ENUM_CONDITIONS TriggeredCondition; // Checked for position array sorting (when optimal) and for partial position closure cancellation when UseTotalVolume is enabled.
double ClosedVolume; // How much of the volume has already been closed - for partial closure when UseTotalVolume == true.
double TotalVolume; // Store the total volume of filtered trades.
Expand Down Expand Up @@ -399,7 +399,7 @@ EVENT_MAP_END(CAppDialog)
CAccountProtector::CAccountProtector()
{
m_FileName = "AP_" + IntegerToString(ChartID()) + ".txt";
LogFile = -1;
LogFile = INVALID_HANDLE;
QuantityClosedMarketOrders = 0;
QuantityDeletedPendingOrders = 0;
IsANeedToContinueClosingOrders = false;
Expand Down Expand Up @@ -3669,12 +3669,12 @@ void CAccountProtector::Close_All_Positions()

// Check condition to know whether and how to sort PositionsByProfit.
// Switching sorting modes because the array is traversed backwards - from total - 1 to 0.
if ((TriggeredCondition == Floating_loss_rises_to_perecentage) || (TriggeredCondition == Floating_loss_rises_to_currency_units) || (TriggeredCondition == Floating_loss_rises_to_points)) ArraySort(PositionsByProfit, WHOLE_ARRAY, 0, MODE_DESCEND);
if (CloseMostDistantFirst) ArraySort(PositionsByProfit, WHOLE_ARRAY, 0, MODE_ASCEND); // Regardless of condition.
else if ((TriggeredCondition == Floating_loss_rises_to_perecentage) || (TriggeredCondition == Floating_loss_rises_to_currency_units) || (TriggeredCondition == Floating_loss_rises_to_points)) ArraySort(PositionsByProfit, WHOLE_ARRAY, 0, MODE_DESCEND);
else if ((TriggeredCondition == Floating_profit_rises_to_perecentage) || (TriggeredCondition == Floating_profit_rises_to_currency_units) || (TriggeredCondition == Floating_profit_rises_to_points)) ArraySort(PositionsByProfit, WHOLE_ARRAY, 0, MODE_ASCEND);
// Otherwise no sorting needed.

int total = ArrayRange(PositionsByProfit, 0); // We already have an array with all tickets.

// Closing market orders. Going backwards to delete an order from the array after closing it.
for (int i = total - 1; i >= 0; i--)
{
Expand Down Expand Up @@ -4088,14 +4088,21 @@ void CAccountProtector::EquityTrailing()
string AdditionalFunds_Asterisk = "";
if (AdditionalFunds != 0) AdditionalFunds_Asterisk = "*";
Logging("Account Protector: Equity stop-loss of " + DoubleToString(sets.doubleCurrentEquityStopLoss, 2) + " hit at " + DoubleToString(AE, 2) + AdditionalFunds_Asterisk + ". Closing all positions.");
if (AlertOnEquityTS) Alert("Account Protector: Equity stop-loss of " + DoubleToString(sets.doubleCurrentEquityStopLoss, 2) + " hit at " + DoubleToString(AE, 2) + AdditionalFunds_Asterisk + ". Closing all positions.");
Logging_Condition_Is_Met();
Close_All_Positions();

sets.boolEquityTrailingStop = false;
m_ChkEquityTrailingStop.Checked(false);

m_LblCurrentEquityStopLoss.Hide();
m_BtnResetEquityStopLoss.Hide();
if (!DoNotDisableEquityTS)
{
sets.boolEquityTrailingStop = false;
m_ChkEquityTrailingStop.Checked(false);
m_LblCurrentEquityStopLoss.Hide();
m_BtnResetEquityStopLoss.Hide();
}
else
{
sets.doubleCurrentEquityStopLoss = 0;
}

SaveSettingsOnDisk();
MoveAndResize();
Expand Down Expand Up @@ -4236,8 +4243,8 @@ void CAccountProtector::Logging_Condition_Is_Met()
double floating_profit = 0;
ClosedVolume = 0;
TotalVolume = 0;
if (LogFileName == "") return;
for (i = 0; i < OrdersTotal(); i++)
{
if (!OrderSelect(i, SELECT_BY_POS)) Logging("Account Protector: OrderSelect failed " + IntegerToString(GetLastError()));
else
{
Expand All @@ -4255,14 +4262,18 @@ void CAccountProtector::Logging_Condition_Is_Met()
floating_profit += order_floating_profit;
market++;
ArrayResize(PositionsByProfit, market, 100); // Reserve extra physical memory to increase the resizing speed.
PositionsByProfit[market - 1][0] = order_floating_profit;
if (!CloseMostDistantFirst) PositionsByProfit[market - 1][0] = order_floating_profit; // Normal profit.
else PositionsByProfit[market - 1][0] = MathAbs(OrderOpenPrice() - OrderClosePrice()) / SymbolInfoDouble(OrderSymbol(), SYMBOL_POINT);
PositionsByProfit[market - 1][1] = OrderTicket();
TotalVolume += OrderLots();
}
else if ((OrderType() == OP_BUYSTOP) || (OrderType() == OP_SELLSTOP) || (OrderType() == OP_BUYLIMIT) || (OrderType() == OP_SELLLIMIT)) pending++;
break; // Order already processed - no point to process this order with other magic numbers.
}
}
}

if (LogFileName == "") return;

string AdditionalFunds_Asterisk = "";
if (AdditionalFunds != 0) AdditionalFunds_Asterisk = "*";
Expand Down Expand Up @@ -4329,7 +4340,7 @@ void CAccountProtector::CheckOneCondition(T &SettingsEditValue, bool &SettingsCh
Logging("CONDITION IS MET: " + EventDescription);
TriggeredCondition = triggered_condition;
Trigger_Actions(EventDescription);
if (!DoNotResetConditions)
if (!DoNotDisableConditions)
{
SettingsCheckboxValue = false;
SettingsEditValue = 0;
Expand Down Expand Up @@ -4597,7 +4608,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Close all positions.
if (sets.ClosePos)
{
if (!DoNotResetActions) sets.ClosePos = false;
if (!DoNotDisableActions) sets.ClosePos = false;
Logging("ACTION IS TAKEN: Close positions (" + DoubleToString(sets.doubleClosePercentage, 2) + "% of " + EnumToString(sets.CloseWhichPositions) + ").");
PartiallyClosedOrders.Clear();
Close_All_Positions();
Expand All @@ -4608,7 +4619,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Delete all pending orders.
if (sets.DeletePend)
{
if (!DoNotResetActions) sets.DeletePend = false;
if (!DoNotDisableActions) sets.DeletePend = false;
Logging("ACTION IS TAKEN: Delete all pending orders.");
Delete_All_Pending_Orders();
sets.Triggered = true;
Expand All @@ -4618,7 +4629,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Disable autotrading.
if (sets.DisAuto)
{
if (!DoNotResetActions) sets.DisAuto = false;
if (!DoNotDisableActions) sets.DisAuto = false;
Logging("ACTION IS TAKEN: Disable autotrading.");
// Toggle AutoTrading button. "2" in GetAncestor call is the "root window".
if (TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) SendMessageW(GetAncestor(WindowHandle(Symbol(), Period()), 2), WM_COMMAND, 33020, 0);
Expand All @@ -4630,7 +4641,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Send emails.
if (sets.SendMails)
{
if (!DoNotResetActions) sets.SendMails = false;
if (!DoNotDisableActions) sets.SendMails = false;
Logging("ACTION IS TAKEN: Send email.");
PrepareSubjectBody(subject, body, title, TimeCurrent(), QuantityClosedMarketOrders, QuantityDeletedPendingOrders, WasAutoTradingDisabled, WasNotificationSent, false, WasPlatformClosed, WasAutoTradingEnabled, WasRecapturedSnapshots);
SendMailFunction(subject, body);
Expand All @@ -4641,7 +4652,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Send push notifications.
if (sets.SendNotif)
{
if (!DoNotResetActions) sets.SendNotif = false;
if (!DoNotDisableActions) sets.SendNotif = false;
Logging("ACTION IS TAKEN: Send push notifications.");
PrepareSubjectBody(subject, body, title, TimeCurrent(), QuantityClosedMarketOrders, QuantityDeletedPendingOrders, WasAutoTradingDisabled, false, WasMailSent, WasPlatformClosed, WasAutoTradingEnabled, WasRecapturedSnapshots, true);
SendNotificationFunction(subject, body);
Expand All @@ -4652,15 +4663,15 @@ void CAccountProtector::Trigger_Actions(string title)
// Close platform.
if (sets.ClosePlatform)
{
if (!DoNotResetActions) sets.ClosePlatform = false;
if (!DoNotDisableActions) sets.ClosePlatform = false;
Logging("ACTION IS TAKEN: Close platform.");
TerminalClose(0);
}

// Enable autotrading.
if (sets.EnableAuto)
{
if (!DoNotResetActions) sets.EnableAuto = false;
if (!DoNotDisableActions) sets.EnableAuto = false;
Logging("ACTION IS TAKEN: Enable autotrading.");
// Toggle AutoTrading button. "2" in GetAncestor call is the "root window".
if (!TerminalInfoInteger(TERMINAL_TRADE_ALLOWED)) SendMessageW(GetAncestor(WindowHandle(Symbol(), Period()), 2), WM_COMMAND, 33020, 0);
Expand All @@ -4671,7 +4682,7 @@ void CAccountProtector::Trigger_Actions(string title)
// Recapture snapshots.
if (sets.RecaptureSnapshots)
{
if (!DoNotResetActions) sets.RecaptureSnapshots = false;
if (!DoNotDisableActions) sets.RecaptureSnapshots = false;
Logging("ACTION IS TAKEN: Recapture snapshots.");
UpdateEquitySnapshot();
UpdateMarginSnapshot();
Expand All @@ -4688,14 +4699,14 @@ void CAccountProtector::Logging(string message)
{
if (StringLen(LogFileName) > 0)
{
string filename = LogFileName + ".log";
if (LogFile < 0) LogFile = FileOpen(filename, FILE_CSV | FILE_READ | FILE_WRITE, ' ');
if (LogFile < 1) Alert("Cannot open file for logging: ", filename, ".");
string filename = LogFileName;
if (LogFile == INVALID_HANDLE) LogFile = FileOpen(filename, FILE_CSV | FILE_READ | FILE_WRITE, ' ');
if (LogFile == INVALID_HANDLE) Alert("Cannot open file for logging: ", filename, ".");
else if (FileSeek(LogFile, 0, SEEK_END))
{
FileWrite(LogFile, TimeToString(TimeLocal(), TIME_DATE | TIME_MINUTES | TIME_SECONDS), " ", message);
FileClose(LogFile);
LogFile = -1;
LogFile = INVALID_HANDLE;
}
else Alert("Unexpected error accessing file: ", filename, ".");
}
Expand Down
Binary file modified MQL5/Experts/Account Protector/Account Protector.mq5
Binary file not shown.
Binary file modified MQL5/Experts/Account Protector/Account Protector.mqh
Binary file not shown.
Binary file modified MQL5/Experts/Account Protector/Defines.mqh
Binary file not shown.
10 changes: 3 additions & 7 deletions MQL5/Experts/Account Protector/WinUser32.mqh
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| WinUser32.mqh |
//| Copyright © 2013, MetaQuotes Software Corp. |
//| Copyright © 2013, MetaQuotes Software Corp. |
//| http://www.mql4.com/ |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2013, MetaQuotes Software Corp."
#property copyright "Copyright © 2013, MetaQuotes Software Corp."
#property link "http://www.mql4.com/"

#import "user32.dll"
Expand Down Expand Up @@ -367,10 +367,6 @@
#define MB_ICONEXCLAMATION 0x00000030
#define MB_ICONASTERISK 0x00000040
#define MB_USERICON 0x00000080
#define MB_ICONWARNING MB_ICONEXCLAMATION
#define MB_ICONERROR MB_ICONHAND
#define MB_ICONINFORMATION MB_ICONASTERISK
#define MB_ICONSTOP MB_ICONHAND
#define MB_DEFBUTTON1 0x00000000
#define MB_DEFBUTTON2 0x00000100
#define MB_DEFBUTTON3 0x00000200
Expand Down

0 comments on commit b3c3f53

Please sign in to comment.