#property description "This Expert Advisor will Trail the Stop Loss Price"
#property description "following the Supertrend Line."
#property description " "
#property description "WARNING : You use this software at your own risk."
#property description "The creator of these plugins cannot be held responsible for any damage or loss."
#property description " "
#property description "Find More on EarnForex.com"
#property icon "\\Files\\EF-Icon-64x64px.ico"
#include <Trade\Trade.mqh>
#include <Trade\SymbolInfo.mqh>
#include <Trade\PositionInfo.mqh>
#include <Trade\OrderInfo.mqh>
enum ENUM_CONSIDER{
All=-1, //ALL ORDERS
Buy=POSITION_TYPE_BUY, //BUY ONLY
Sell=POSITION_TYPE_SELL, //SELL ONLY
};
enum ENUM_CUSTOMTIMEFRAMES{
CURRENT=PERIOD_CURRENT, //CURRENT PERIOD
M1=PERIOD_M1, //M1
M5=PERIOD_M5, //M5
M15=PERIOD_M15, //M15
M30=PERIOD_M30, //M30
H1=PERIOD_H1, //H1
H4=PERIOD_H4, //H4
D1=PERIOD_D1, //D1
W1=PERIOD_W1, //W1
MN1=PERIOD_MN1, //MN1
};
input string Comment_1="===================="; //Expert Advisor Settings
input double ATRMultiplier=2.0; //ATR Multiplier
input int ATRPeriod=100; //ATR Period
input int ATRMaxBars=100; //ATR Max Bars (Max 10.000)
input ENUM_CUSTOMTIMEFRAMES StopATRTimeframe=CURRENT; //ATR Supertrend Timeframe Calculation
input string Comment_2="===================="; //Orders Filtering Options
input bool OnlyCurrentSymbol=true; //Apply To Current Symbol Only
input ENUM_CONSIDER OnlyType=All; //Apply To
input bool UseMagic=false; //Filter By Magic Number
input int MagicNumber=0; //Magic Number (if above is true)
input bool UseComment=false; //Filter By Comment
input string CommentFilter=""; //Comment (if above is true)
input bool EnableTrailing=false; //Enable Trailing Stop
input string Comment_3="===================="; //Notification Options
input bool EnableNotify=false; //Enable Notifications feature
input bool SendAlert=true; //Send Alert Notification
input bool SendApp=true; //Send Notification to Mobile
input bool SendEmail=true; //Send Notification via Email
input string Comment_3a="===================="; //Graphical Window
input bool ShowPanel=true; //Show Graphical Panel
input string IndicatorName="MQLTA-STTS"; //Indicator Name (to name the objects)
input int Xoff=20; //Horizontal spacing for the control panel
input int Yoff=20; //Vertical spacing for the control panel
int OrderOpRetry=5;
int SuperTrendShift=0;
double TrendUpTmp[], TrendDownTmp[];
int changeOfTrend;
int MaxBars=ATRMaxBars;
CTrade trade;
bool isTrailingEnabled = EnableTrailing;
int OnInit(){
CleanPanel();
if(ShowPanel) DrawPanel();
return(INIT_SUCCEEDED);
}
void OnDeinit(const int reason){
CleanPanel();
}
void OnTick(){
CleanPanel();
if(isTrailingEnabled) TrailingStop();
if(ShowPanel) DrawPanel();
}
void OnChartEvent(const int id,
const long &lparam,
const double &dparam,
const string &sparam){
if(id==CHARTEVENT_OBJECT_CLICK){
if(sparam==PanelEnableDisable){
ChangeTrailingEnabled();
}
}
if(id==CHARTEVENT_KEYDOWN){
if(lparam==27){
if(MessageBox("Are you sure you want to close the EA?","EXIT ?",MB_YESNO)==IDYES){
ExpertRemove();
}
}
}
}
double GetATRTrend(string Instrument="", ENUM_TIMEFRAMES Timeframe=0, int Shift=0){
ArrayResize(TrendDownTmp,ATRMaxBars,0);
ArrayInitialize(TrendDownTmp,0);
ArrayResize(TrendUpTmp,ATRMaxBars,0);
ArrayInitialize(TrendUpTmp,0);
if(Instrument=="") Instrument=_Symbol;
if(Timeframe==0) Timeframe=Period();
CalculateSupertrendTmp(Instrument, Timeframe);
double ATRTrend1=TrendUpTmp[Shift];
double ATRTrend2=TrendDownTmp[Shift];
if(ATRTrend1>(iClose(Instrument,Timeframe,Shift)*2)){
return NormalizeDouble(ATRTrend2, (int)SymbolInfoInteger(Instrument,SYMBOL_DIGITS));
}
if(ATRTrend2>(iClose(Instrument,Timeframe,Shift)*2)){
return NormalizeDouble(ATRTrend1, (int)SymbolInfoInteger(Instrument,SYMBOL_DIGITS));
}
if(ATRTrend1==0){
Print("Please Check That you Have Installed the Indicator MQLTA Supertrend Line");
}
return 0;
}
void CalculateSupertrendTmp(string Instrument, ENUM_TIMEFRAMES Timeframe){
MaxBars=ATRMaxBars;
int limit, i, flag, flagh, trend[10000];
double up[10000], dn[10000], medianPrice, atr;
int counted_bars = 0;
if(counted_bars < 0) return;
if(counted_bars > 0) counted_bars--;
limit=iBars(Instrument,Timeframe)-counted_bars-1;
MaxBars--;
if(iBars(Instrument,Timeframe)<MaxBars+2+ATRPeriod) MaxBars=iBars(Instrument,Timeframe)-2-ATRPeriod;
if(MaxBars<=0){
Print("Need more historical data to calculate the Supertrend, Currently have only ", IntegerToString(iBars(Instrument,Timeframe))," Bars");
return;
}
for(i=MaxBars;i>=0;i--) {
TrendUpTmp[I]=EMPTY_VALUE;
TrendDownTmp[I]=EMPTY_VALUE;
atr=iATR(Instrument,Timeframe,ATRPeriod);
medianPrice = (iHigh(Instrument,Timeframe,i)+iLow(Instrument,Timeframe,i))/2;
up[I]=medianPrice+(ATRMultiplier*atr);
dn[I]=medianPrice-(ATRMultiplier*atr);
trend[I]=1;
if (iClose(Instrument,Timeframe,i)>up[i+1]) {
trend[I]=1;
if (trend[i+1] == -1) changeOfTrend = 1;
}
else if (iClose(Instrument,Timeframe,i)<dn[i+1]) {
trend[I]=-1;
if (trend[i+1] == 1) changeOfTrend = 1;
}
else if (trend[i+1]==1) {
trend[I]=1;
changeOfTrend = 0;
}
else if (trend[i+1]==-1) {
trend[I]=-1;
changeOfTrend = 0;
}
if (trend[I]<0 && trend[i+1]>0) {
flag=1;
}
else {
flag=0;
}
if (trend[I]>0 && trend[i+1]<0) {
flagh=1;
}
else {
flagh=0;
}
if (trend[I]>0 && dn[I]<dn[i+1])
dn[I]=dn[i+1];
if (trend[I]<0 && up[I]>up[i+1])
up[I]=up[i+1];
if (flag==1)
up[I]=medianPrice+(ATRMultiplier*atr);
if (flagh==1)
dn[I]=medianPrice-(ATRMultiplier*atr);
if(i==MaxBars) continue;
if (trend[I]==1) {
TrendUpTmp[I]=dn[I];
if (changeOfTrend == 1) {
TrendUpTmp[i+1] = TrendDownTmp[i+1];
changeOfTrend = 0;
}
}
else if (trend[I]==-1) {
TrendDownTmp[I]=up[I];
if (changeOfTrend == 1) {
TrendDownTmp[i+1] = TrendUpTmp[i+1];
changeOfTrend = 0;
}
}
}
ChartRedraw();
}
void TrailingStop(){
MqlTradeRequest request;
MqlTradeResult result;
for(int i=0;i<PositionsTotal();i++) {
if(!PositionSelect(i)) {
int Error = GetLastError();
string ErrorText = ErrorDescription(Error);
Print("ERROR - Unable to select the position - ", IntegerToString(Error));
Print("ERROR - ", ErrorText);
break;
}
if(OnlyCurrentSymbol && PositionGetString(POSITION_SYMBOL)!=_Symbol) continue;
if(UseMagic && PositionGetInteger(POSITION_MAGIC)!=MagicNumber) continue;
if(UseComment && StringFind(PositionGetString(POSITION_COMMENT),CommentFilter)<0) continue;
if(OnlyType!=All && PositionGetInteger(POSITION_TYPE)!=OnlyType) continue;
double NewSL=0;
double NewTP=0;
string Instrument=PositionGetString(POSITION_SYMBOL);
double ATRTrend=GetATRTrend(Instrument,(ENUM_TIMEFRAMES)StopATRTimeframe,SuperTrendShift);
if(ATRTrend==0){
Print("Not enough historical data, please load more candles for The selected timeframe");
return;
}
int eDigits = (int)SymbolInfoInteger(Instrument,SYMBOL_DIGITS);
double SLPrice=NormalizeDouble(PositionGetDouble(POSITION_SL),eDigits);
double TPPrice=NormalizeDouble(PositionGetDouble(POSITION_TP),eDigits);
double Spread = 0, StopLevel = 0;
if(!SymbolInfoDouble(Instrument, SYMBOL_SPREAD, Spread)){
Print("Error getting spread for symbol ", Instrument);
continue;
}
if(!SymbolInfoDouble(Instrument, SYMBOL_TRADE_STOPS_LEVEL, StopLevel)){
Print("Error getting stop level for symbol ", Instrument);
continue;
}
double Point;
if (!SymbolInfoDouble(Instrument, SYMBOL_POINT, Point)) {
Print("Error getting point value for symbol ", Instrument);
continue;
}
StopLevel *= Point;
double Bid = 0, Ask = 0;
if (!SymbolInfoDouble(Instrument, SYMBOL_BID, Bid)) {
Print("Error getting Bid value for symbol ", Instrument);
continue;
}
if (!SymbolInfoDouble(Instrument, SYMBOL_ASK, Ask)) {
Print("Error getting Ask value for symbol ", Instrument);
continue;
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_BUY && ATRTrend<Bid-StopLevel){
NewSL=NormalizeDouble(ATRTrend,eDigits);
NewTP=TPPrice;
if(NewSL>SLPrice+StopLevel || SLPrice==0){
ModifyOrder(PositionGetInteger(POSITION_TICKET),PositionGetDouble(POSITION_PRICE_OPEN),NewSL,NewTP, Instrument);
}
}
if(PositionGetInteger(POSITION_TYPE)==POSITION_TYPE_SELL && ATRTrend>Ask+StopLevel+Spread){
NewSL=NormalizeDouble(ATRTrend+Spread,eDigits);
NewTP=TPPrice;
if(NewSL<SLPrice-StopLevel || SLPrice==0){
ModifyOrder(PositionGetInteger(POSITION_TICKET),PositionGetDouble(POSITION_PRICE_OPEN),NewSL,NewTP, Instrument);
}
}
}
}
void ModifyOrder(ulong Ticket, double OpenPrice, double SLPrice, double TPPrice, string Instrument){
MqlTradeRequest request;
MqlTradeResult result;
if(!PositionSelectByTicket(Ticket)){
int Error = GetLastError();
string ErrorText = ErrorDescription(Error);
Print("ERROR - SELECT TICKET - error selecting order ", IntegerToString(Ticket)," return error: ", IntegerToString(Error));
return;
}
int eDigits = (int)SymbolInfoInteger(Instrument, SYMBOL_DIGITS);
SLPrice = NormalizeDouble(SLPrice, eDigits);
TPPrice = NormalizeDouble(TPPrice, eDigits);
double Bid = 0, Ask = 0;
if (!SymbolInfoDouble(Instrument, SYMBOL_BID, Bid)) {
Print("Error getting Bid value for symbol ", Instrument);
return;
}
if (!SymbolInfoDouble(Instrument, SYMBOL_ASK, Ask)) {
Print("Error getting Ask value for symbol ", Instrument);
return;
}
for(int i=1; i<=OrderOpRetry; i++){
ZeroMemory(request);
request.action = TRADE_ACTION_SLTP;
request.symbol = Instrument;
request.sl = SLPrice;
request.tp = TPPrice;
request.position = Ticket;
if(OrderSend(request, result)){
Print("TRADE - UPDATE SUCCESS - Order ", IntegerToString(Ticket), " new stop loss ", DoubleToString(SLPrice, eDigits), " new take profit ", DoubleToString(TPPrice, eDigits));
NotifyStopLossUpdate(Ticket, SLPrice, Instrument);
break;
}
else{
int Error = GetLastError();
string ErrorText = ErrorDescription(Error);
Print("ERROR - UPDATE FAILED - error modifying order ", IntegerToString(Ticket), " return error: ", IntegerToString(Error), " Open=", DoubleToString(OpenPrice, eDigits),
" Old SL=", DoubleToString(PositionGetDouble(POSITION_SL), eDigits), " Old TP=", DoubleToString(PositionGetDouble(POSITION_TP), eDigits),
" New SL=", DoubleToString(SLPrice, eDigits), " New TP=", DoubleToString(TPPrice, eDigits), " Bid=", DoubleToString(Bid, eDigits), " Ask=", DoubleToString(Ask, eDigits));
Print("ERROR - ", ErrorText);
}
}
return;
}
void NotifyStopLossUpdate(ulong OrderNumber, double SLPrice, string Instrument){
if(!EnableNotify) return;
if(!SendAlert && !SendApp && !SendEmail) return;
int eDigits = (int)SymbolInfoInteger(Instrument, SYMBOL_DIGITS);
string EmailSubject = IndicatorName + " " + Instrument + " Notification ";
string EmailBody = "\r\n" + AccountInfoString(ACCOUNT_COMPANY) + " - " + AccountInfoString(ACCOUNT_NAME) + " - " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + "\r\n\r\n" + IndicatorName + " Notification for " + Instrument + "\r\n\r\n";
EmailBody += "The Stop Loss for order " + IntegerToString(OrderNumber) + " was moved to " + DoubleToString(SLPrice, eDigits) + "\r\n\r\n";
string AlertText = IndicatorName + " - " + Instrument + " Notification\r\n";
AlertText += "The Stop Loss for order " + IntegerToString(OrderNumber) + " was moved to " + DoubleToString(SLPrice, eDigits) + "";
string AppText = AccountInfoString(ACCOUNT_COMPANY) + " - " + AccountInfoString(ACCOUNT_NAME) + " - " + IntegerToString(AccountInfoInteger(ACCOUNT_LOGIN)) + " - " + IndicatorName + " - " + Instrument + " - ";
AppText += "The Stop Loss for order " + IntegerToString(OrderNumber) + " was moved to " + DoubleToString(SLPrice, eDigits) + "";
if(SendAlert) Alert(AlertText);
if(SendEmail){
if(!SendMail(EmailSubject, EmailBody)) Print("Error sending email " + IntegerToString(GetLastError()));
}
if(SendApp){
if(!SendNotification(AppText)) Print("Error sending notification " + IntegerToString(GetLastError()));
}
datetime LastNotification = TimeCurrent();
Print(IndicatorName + "-" + Instrument + " last notification sent " + TimeToString(LastNotification));
}
string PanelBase = IndicatorName + "-P-BAS";
string PanelLabel = IndicatorName + "-P-LAB";
string PanelEnableDisable = IndicatorName + "-P-ENADIS";
int PanelMovX = 50;
int PanelMovY = 20;
int PanelLabX = 150;
int PanelLabY = PanelMovY;
int PanelRecX = PanelLabX + 4;
void DrawPanel(){
CleanPanel();
int Rows = 1;
if(ObjectCreate(0, PanelBase, OBJ_RECTANGLE_LABEL, 0, 0, 0)){
ObjectSetInteger(0, PanelBase, OBJPROP_XDISTANCE, Xoff);
ObjectSetInteger(0, PanelBase, OBJPROP_YDISTANCE, Yoff);
ObjectSetInteger(0, PanelBase, OBJPROP_XSIZE, PanelRecX);
ObjectSetInteger(0, PanelBase, OBJPROP_YSIZE, (PanelMovY + 2) * 1 + 2);
ObjectSetInteger(0, PanelBase, OBJPROP_BGCOLOR, clrWhite);
ObjectSetInteger(0, PanelBase, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, PanelBase, OBJPROP_STATE, false);
ObjectSetInteger(0, PanelBase, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, PanelBase, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(0, PanelBase, OBJPROP_COLOR, clrBlack);
}
if(ObjectCreate(0, PanelLabel, OBJ_EDIT, 0, 0, 0)){
ObjectSetInteger(0, PanelLabel, OBJPROP_XDISTANCE, Xoff + 2);
ObjectSetInteger(0, PanelLabel, OBJPROP_YDISTANCE, Yoff + 2);
ObjectSetInteger(0, PanelLabel, OBJPROP_XSIZE, PanelLabX);
ObjectSetInteger(0, PanelLabel, OBJPROP_YSIZE, PanelLabY);
ObjectSetInteger(0, PanelLabel, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, PanelLabel, OBJPROP_STATE, false);
ObjectSetInteger(0, PanelLabel, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, PanelLabel, OBJPROP_READONLY, true);
ObjectSetInteger(0, PanelLabel, OBJPROP_ALIGN, ALIGN_CENTER);
ObjectSetString(0, PanelLabel, OBJPROP_TOOLTIP, "Supertrend Trailing Stop Loss By MQLTA");
ObjectSetString(0, PanelLabel, OBJPROP_TEXT, "MQLTA STTS");
ObjectSetString(0, PanelLabel, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, PanelLabel, OBJPROP_FONTSIZE, 10);
ObjectSetInteger(0, PanelLabel, OBJPROP_COLOR, clrNavy);
ObjectSetInteger(0, PanelLabel, OBJPROP_BGCOLOR, clrKhaki);
ObjectSetInteger(0, PanelLabel, OBJPROP_BORDER_COLOR, clrBlack);
}
string EnableDisabledText="";
color EnableDisabledColor=clrNavy;
color EnableDisabledBack=clrKhaki;
if(isTrailingEnabled){
EnableDisabledText="TRAILING ENABLED";
EnableDisabledColor=clrWhite;
EnableDisabledBack=clrDarkGreen;
}
else{
EnableDisabledText="TRAILING DISABLED";
EnableDisabledColor=clrWhite;
EnableDisabledBack=clrDarkRed;
}
if(ObjectCreate(0, PanelEnableDisable, OBJ_EDIT, 0, 0, 0)){
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_XDISTANCE, Xoff + 2);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_YDISTANCE, Yoff + (PanelMovY + 1) * Rows + 2);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_XSIZE, PanelLabX);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_YSIZE, PanelLabY);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_BORDER_TYPE, BORDER_FLAT);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_STATE, false);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_READONLY, true);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_ALIGN, ALIGN_CENTER);
ObjectSetString(0, PanelEnableDisable, OBJPROP_FONT, "Consolas");
ObjectSetString(0, PanelEnableDisable, OBJPROP_TOOLTIP, "Click to Enable or Disable the Trailing Stop Feature");
ObjectSetString(0, PanelEnableDisable, OBJPROP_TEXT, EnableDisabledText);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_COLOR, EnableDisabledColor);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_BGCOLOR, EnableDisabledBack);
ObjectSetInteger(0, PanelEnableDisable, OBJPROP_BORDER_COLOR, clrBlack);
Rows++;
}
ObjectSetInteger(0, PanelBase, OBJPROP_XSIZE, PanelRecX);
ObjectSetInteger(0, PanelBase, OBJPROP_YSIZE, (PanelMovY + 1) * Rows + 3);
}
void CleanPanel(){
int Window=0;
for(int i=ObjectsTotal(ChartID(),Window,-1)-1;i>=0;i--){
if(StringFind(ObjectName(ChartID(),i,Window),IndicatorName+"-P-",0)>=0){
ObjectDelete(ChartID(),ObjectName(ChartID(),i,Window));
}
}
}
void ChangeTrailingEnabled(){
if(!isTrailingEnabled){
if(MQLInfoInteger(MQL_TRADE_ALLOWED)) isTrailingEnabled=true;
else{
MessageBox("You need to first enable Live Trading in your Metatrader options","WARNING",MB_OK);
}
}
else isTrailingEnabled=false;
DrawPanel();
}
string ErrorDescription(int error_code) {
string description = "";
switch(error_code) {
case 1: description = "No error"; break;
case 2: description = "Common error"; break;
case 3: description = "Invalid trade parameters"; break;
// Add more error cases as needed
default: description = "Unknown error"; break;
}
return description;
}