Problems with Invalid Stops

 

Hi

I've just started learning to code EA and try and get my head around MT5, ticks, points etc, so I apologise in advance if I have done something very daft or missed something obvious!

So I've been following a tutorial online that gradually developed a more complex EA. It seemed to make sense and work with a fixed stop, but once the stop was changed to a value based on ATR it generated a lot of failed trades for 'Invalid Stops'. 

That goes on until until 7:48 and it suddenly starts creating trades. 

When I initially googled this I found suggestions for replacing NormalizeDouble function, which I have done. It did not seem to make any difference. I found suggestions to check the Min allowed stop, but that seems to be 0.

So I added lots of debugging info and watched breakpoints  as I stepped through the code, all to no avail. I cannot isolate what causes a failure. ATR values themselves were ruled out as values that cause a fail at earlier time frames work fine later on. 

I have noticed an inconstancy in the output of the Strategy Tester, which maybe the problem. The Journal shows "GBPUSD: symbol synchronized," but my chart is EURUSD? I don't know why GBPUSD was being synchronised but changing the chart to GBPUSD did not help. I've attached a screen grab of my MT5 settings for testing at the bottom. 

Many thanks to anyone who takes the time to read this.

Pete

Here are my 'hopefully' relevant custom functions:

//global variables
//risk metrics
input bool   TslCheck          = true;   //Use Trailing Stop Loss?
input bool   RiskCompounding   = false;  //Use Compounded Risk Method?
double       StartingEquity    = 0.0;    //Starting Equity
double       CurrentEquityRisk = 0.0;    //Equity that will be risked per trade
input double MaxLossPrc        = 0.02;   //Percent Risk Per Trade
input    double atrProfitMulti   =2.0;//ATR profit multiple
input    double atrLossMulti     =1.0;//ATR Loss Multiple

//custom functions
//-- alternative code from forum moderator Fernando Carreiro at https://www.mql5.com/en/forum/390453
double Round2Ticksize( double price )
   {
   double tick_size = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_SIZE );
   return( round( price / tick_size ) * tick_size );  
   }

// code from William Roader to replace NormalizeDouble from https://www.mql5.com/en/forum/223861
double round_nearest(double v, double to){ return to * MathRound(v / to); }
double round_down(   double v, double to){ return to * MathFloor(v / to); }
double round_up(     double v, double to){ return to * MathCeil( v / to); }

double normalize_price(double p, double d=0.0)
   {
   double tickSize    = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
   if(d > 0)   return round_up(p,      tickSize);
   if(d < 0)   return round_down(p,    tickSize);
               return round_nearest(p, tickSize);
   }

double   normalize_lots(double lots)
   {
   double lotStep     = SymbolInfoDouble(Symbol(), SYMBOL_VOLUME_STEP);
   return round_down(lots, lotStep);
   return lots;
   }


//// Calculate Max Lot Size based on Maximum Risk from https://www.mql5.com/en/forum/388742
double dblLotsRisk( double dbStopLoss, double dbRiskRatio )
{
   double
      dbLotsMinimum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MIN       ),
      dbLotsMaximum  = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_MAX       ),
      dbLotsStep     = SymbolInfoDouble( _Symbol, SYMBOL_VOLUME_STEP      ),
      dbTickSize     = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_SIZE  ),
      dbTickValue    = SymbolInfoDouble( _Symbol, SYMBOL_TRADE_TICK_VALUE ),
      dbValueAccount = fmin( fmin( 
                       AccountInfoDouble( ACCOUNT_EQUITY      )  , 
                       AccountInfoDouble( ACCOUNT_BALANCE     ) ),
                       AccountInfoDouble( ACCOUNT_MARGIN_FREE ) ),
      dbValueRisk    = dbValueAccount * dbRiskRatio,
      dbLossOrder    = dbStopLoss * dbTickValue / dbTickSize,
      dbCalcLot      = fmin(  dbLotsMaximum,                  // Prevent too greater volume
                       fmax(  dbLotsMinimum,                  // Prevent too smaller volume
                       round( dbValueRisk / dbLossOrder       // Calculate stop risk
                       / dbLotsStep ) * dbLotsStep ) );       // Align to step value

   return ( dbCalcLot );
};


//seems sensible to check stop loss against broker min stop
//this is code from  which I have modified
//check calculated stop against broker stop limit and return the broker limit
//if teh calc limit is too close
//below is work in progress
double CheckStopLimits(double calcStop)
    {
        long slPriceLimit =0;
        double acceptableStop = 0;
        slPriceLimit = SymbolInfoInteger(Symbol(),SYMBOL_TRADE_STOPS_LEVEL);//returns the min points for a stop
        if (slPriceLimit ==0)
            {
            acceptableStop=calcStop;
            Print("stop loss is above Min level Set by Broker");
            return calcStop; 
            }
        else
            {
            acceptableStop= MathMin(slPriceLimit,calcStop);
            Print("stop loss adjusted from", calcStop, "to  ",acceptableStop );
            return acceptableStop;
            } 
    }

/---process open trades for buy and sell
//getting invalid stoploss and take profit values. -replaced NormalizedDouble
ulong ProcessTradeOpen(ENUM_ORDER_TYPE orderType, double currentATR)
{
   string   currentSymbol = Symbol();
   string   buyOrSell="";//for output message
   double   price = 0;
   double   stopLossPriceNormalized = 0;//
   double   takeProfitPriceNormalized =0;
   double   nonNormstopLossPrice=0;
   double   NonNormtakeProfitPrice =0;

   double   stopDist=0;
   double   profitDist=0;
   double   lowestAllowableStop=0;

      //create stop based on ATR value and a multiplier set by user
      stopDist  =currentATR * atrLossMulti;
      profitDist  = currentATR * atrProfitMulti;
      lowestAllowableStop=CheckStopLimits(stopDist);//returns the closest allowable stop


   if(orderType ==ORDER_TYPE_BUY)
      {
      buyOrSell ="Buy";
      price =  Round2Ticksize(SymbolInfoDouble(currentSymbol,SYMBOL_ASK));
      nonNormstopLossPrice    =price - lowestAllowableStop;
      NonNormtakeProfitPrice  =price + profitDist;
      stopLossPriceNormalized  =  Round2Ticksize(nonNormstopLossPrice);
      takeProfitPriceNormalized=  Round2Ticksize(NonNormtakeProfitPrice);
      }
   else if (orderType ==ORDER_TYPE_SELL)
      {
      buyOrSell ="Sell";  
      price          =  Round2Ticksize(SymbolInfoDouble(currentSymbol,SYMBOL_BID));
      nonNormstopLossPrice    =price + lowestAllowableStop;
      NonNormtakeProfitPrice  =price - lowestAllowableStop;     
      stopLossPriceNormalized  =  Round2Ticksize(nonNormstopLossPrice);
      takeProfitPriceNormalized=  Round2Ticksize(NonNormtakeProfitPrice);
      }
    string myInpTradeComment = StringConcatenate(buyOrSell, " at Price = ",DoubleToString(price),"Stop = ", DoubleToString(stopLossPriceNormalized), "target =",takeProfitPriceNormalized );
   
//get lot size
     double lotSize =dblLotsRisk(stopLossPriceNormalized,0.01);
   //execute trades
   Trade.PositionClose(currentSymbol);
   Trade.PositionOpen(currentSymbol,orderType,lotSize,price,stopLossPriceNormalized,takeProfitPriceNormalized,InpTradeComment);
   
   //get position ticket number
   ulong ticket   =   PositionGetTicket(0);//0 is the most current ticket
   
   PrintFormat("Price=%f,StopLoss=%f,lotSize=%f,TakeProfit=%f,currantATR=%f,distance of Stop=%f,dist of profit=%f,Ticket=%f",
                  price,stopLossPriceNormalized,lotSize,takeProfitPriceNormalized,currentATR,(price-stopLossPriceNormalized),(price-takeProfitPriceNormalized),ticket );   
   //add in error handling
   
   
return (ticket);

}

void    AdjustTsl(ulong ticket, double currentATR, double ATRMulti)
    {
    //set symbol sreing and variables
    string  currentSymbol    =Symbol();
    double  price =0.0;
    double  optimalStopLoss =0.0;

     //Check correct ticket number is selected for further position data to be stored. Return if error.
   if (!PositionSelectByTicket(ticket))
      return;

   //Store position data variables
   ulong  positionDirection = PositionGetInteger(POSITION_TYPE);
   double currentStopLoss   = PositionGetDouble(POSITION_SL);
   double currentTakeProfit = PositionGetDouble(POSITION_TP);
   
   //Check if position direction is long 
   if (positionDirection==POSITION_TYPE_BUY)
   {
      //Get optimal stop loss value
      price           = NormalizeDouble(SymbolInfoDouble(currentSymbol, SYMBOL_ASK), Digits());
      optimalStopLoss = NormalizeDouble(price - currentATR*ATRMulti, Digits());
      
      //Check if optimal stop loss is greater than current stop loss. If TRUE, adjust stop loss
      if(optimalStopLoss > currentStopLoss)
      {
         Trade.PositionModify(ticket,optimalStopLoss,currentTakeProfit);
         Print("Ticket ", ticket, " for symbol ", currentSymbol," stop loss adjusted to ", optimalStopLoss);
      }

      //Return once complete
      return;
   } 

   //Check if position direction is short 
   if (positionDirection==POSITION_TYPE_SELL)
   {
      //Get optimal stop loss value
      price           = NormalizeDouble(SymbolInfoDouble(currentSymbol, SYMBOL_BID), Digits());
      optimalStopLoss = NormalizeDouble(price + currentATR*ATRMulti, Digits());

      //Check if optimal stop loss is less than current stop loss. If TRUE, adjust stop loss
      if(optimalStopLoss < currentStopLoss)
      {
         Trade.PositionModify(ticket,optimalStopLoss,currentTakeProfit);
         Print("Ticket ", ticket, " for symbol ", currentSymbol," stop loss adjusted to ", optimalStopLoss);
      }
      
      //Return once complete
      return;
   } 
    }
 
Documentation on MQL5: MQL5 programs / Program Running
Documentation on MQL5: MQL5 programs / Program Running
  • www.mql5.com
Program Running - MQL5 programs - MQL5 Reference - Reference on algorithmic/automated trading language for MetaTrader 5
 
Your topic has been moved to the section: Expert Advisors and Automated Trading — In the future, please consider which section is most appropriate for your query.
 
Fernando Carreiro #:
Your topic has been moved to the section: Expert Advisors and Automated Trading — In the future, please consider which section is most appropriate for your query.
Hi, Sorry I did not see the sections. I'm new to the forum too!
 

Hi Carl

Well 'learning MQ5 is what I am trying to do. Hence I am following a tutorial and asking questions here ;) 

Here is my OnTick:

//+------------------------------------------------------------------+
//| Expert tick function                                             |
//+------------------------------------------------------------------+
void OnTick()
{
   //Counts the number of ticks received  
   TicksReceivedCount++; 
   
   //Checks for new candle
   bool IsNewCandle = false;
   if(TimeLastTickProcessed != iTime(Symbol(),Period(),0))
   {
      IsNewCandle = true;
      TimeLastTickProcessed=iTime(Symbol(),Period(),0);
   }
   
   //If there is a new candle, process any trades
   if(IsNewCandle == true)
   {
      //Counts the number of ticks processed
      TicksProcessedCount++;

      //Check if position is still open. If not open, return 0.
      if (!PositionSelectByTicket(TicketNumber))
         TicketNumber = 0;

      //Initiate String for indicatorMetrics Variable. This will reset variable each time OnTick function runs.
      IndicatorMetrics ="";  
      StringConcatenate(IndicatorMetrics,Symbol()," | Last Processed: ",TimeLastTickProcessed," | Open Ticket: ", TicketNumber);

      //Money Management - ATR
      double CurrentAtr = GetATRValue(); //Gets ATR value double using custom function - convert double to string as per symbol digits
      StringConcatenate(IndicatorMetrics, IndicatorMetrics, " | ATR: ", CurrentAtr);

      //Strategy Trigger - MACD
      string OpenSignalMacd = GetMacdOpenSignal(); //Variable will return Long or Short Bias only on a trigger/cross event 
      StringConcatenate(IndicatorMetrics, IndicatorMetrics, " | MACD Bias: ", OpenSignalMacd); //Concatenate indicator values to output comment for user   
   
      //Strategy Filter - EMA
      string OpenSignalEma = GetEmaOpenSignal(); //Variable will return long or short bias if close is above or below EMA.
      StringConcatenate(IndicatorMetrics, IndicatorMetrics, " | EMA Bias: ", OpenSignalEma); //Concatenate indicator values to output comment for user
   
      //Enter trades and return position ticket number
      if(OpenSignalMacd == "Long" && OpenSignalEma == "Long")
         TicketNumber = ProcessTradeOpen(ORDER_TYPE_BUY,CurrentAtr);
      else if(OpenSignalMacd == "Short" && OpenSignalEma == "Short")
         TicketNumber = ProcessTradeOpen(ORDER_TYPE_SELL,CurrentAtr); 

      //Adjust Open Positions - Trailing Stop Loss
      if(TslCheck == true)
         AdjustTsl(TicketNumber, CurrentAtr, AtrLossMulti);
   }       
      
   //Comment for user
   Comment("\n\rExpert: ", InpMagicNumber, "\n\r",
         "MT5 Server Time: ", TimeCurrent(), "\n\r",
         "Ticks Received: ", TicksReceivedCount,"\n\r",
         "Ticks Processed: ", TicksProcessedCount,"\n\r"
         "Symbols Traded: \n\r", 
         IndicatorMetrics);         
}
 
I am curious to know how much topics there are about invalid stops on this forum. I would bet for at least 1000 !
 
Alain Verleyen #:
I am curious to know how much topics there are about invalid stops on this forum. I would bet for at least 1000 !

Hi Alain

There are many, many, many, posts, hence I have modified my code several times over the last couple of days to take into account the advice given by people such as William Roader and Fernando Carreiro. 

As I have understood it so far, most of the posts seem to focus around problems with the NormalizeDouble function, so as you can see from my annotated code (in the first post) I have replaced that function for calculating the stops. There was another recommendation to check for any limit to stop distance, I implemented that too. 

That's why I have asked for help, after days searching the forum and implementing the suggestions I find, I'm still getting the errors and I am still non-the wiser why there is a problem.

As well as fixing the code I'd like to understand.

 

Start to add printing in the log when there is an error, all the relevant values (the sl/tp but also open price, current bid/ask and the stoplevel).

Post the ALL relevant code ONLY please, so all related to the trade request and price/sl/tp. We don't care about the signals part or the lot size calculation. And in one post,not split across several messages.

And also post the log output.

Reason: