- Do not normalise quote prices provided by the platform (ie. OHLC prices, Bid, Ask, etc). They are already normalised.
- For calculated prices, do not normalise based on digits or on point. Use the tick size. You have already been informed about this at least twice:
https://www.mql5.com/en/forum/444428#comment_46094491
https://www.mql5.com/en/forum/445072#comment_46126937
- Do not normalise quote prices provided by the platform (ie. OHLC prices, Bid, Ask, etc). They are already normalised.
- For calculated prices, do not normalise based on digits or on point. Use the tick size. You have already been informed about this at least twice:
https://www.mql5.com/en/forum/444428#comment_46094491
https://www.mql5.com/en/forum/445072#comment_46126937
"I have diligently attempted the recommended solutions, incorporating both the normalized price and the normalizedDouble function. However, the issue remains unresolved. While I acknowledge the guidance previously provided, I am pleased to report that I overcame the invalid stop errors with Alain's valuable assistance. Given that the current challenge appears distinct from prior issues, I am seeking further expertise to address this matter effectively. Your insightful suggestions and recommendations would be greatly appreciated.
Updated code same results:
void applyTrailingStop(void) { MqlTick currentTick; if(SymbolInfoTick(_Symbol, currentTick)) { double Bid = currentTick.bid; double Ask = currentTick.ask; int totalOrders = PositionsTotal(); double trailingStopPointsapply = trailingStopPoints * _Point; for(int i = 0; i < totalOrders; i++) { ulong ticket =PositionGetInteger(POSITION_TICKET); const string symbol = PositionGetString(POSITION_SYMBOL); if(symbol != _Symbol) continue; // Skip positions of other symbols if(PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; // Skip positions with a different magic number double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double stopLoss = PositionGetDouble(POSITION_SL); double positionTP = PositionGetDouble(POSITION_TP); double newStopLoss = 0; if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { newStopLoss = normalizePrice(Bid - trailingStopPointsapply); if(newStopLoss > openPrice && (stopLoss < openPrice || newStopLoss > stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, positionTP)) { if(!trade.PositionModify(ticket, newStopLoss, positionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { newStopLoss = normalizePrice(Ask + trailingStopPointsapply); if(newStopLoss < openPrice && (stopLoss > openPrice || newStopLoss < stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, positionTP)) { if(!trade.PositionModify(ticket, newStopLoss, positionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } } } }
What is the same results? Are you referring to the values in the debugger having many decimal points?
That is normal. Floating point numbers are binary numbers and cannot represent decimal numbers precisely. One is base 2 and the other is base 10.
Forum on trading, automated trading systems and testing trading strategies
MathRound fails for one particular number
Fernando Carreiro, 2018.01.01 22:08
He means that the value "0.69" cannot be exactly represented given the way a floating point number works (based on binary and not decimal representation) - hence, why the value gets represented as "0.68999999..." (see below).
You can never really "normalize" it and it is the main reason why both @whroeder1 and myself contest the use of the NormalizeDouble() function. It should in the very least be renamed to something like "RoundDigits()" because it is more of a "rounding" function and does not "normalize" in any way or fashion.
https://www.h-schmidt.net/FloatConverter/IEEE754.html
EDIT: Please note that the above images are for examples of representation in the 4-byte "float", and not the 8-byte "double" which offers more precision but still cannot represent the value "0.69" exactly due to the "binary" nature of the format.
EDIT2: For future readers that have difficulty understanding (or accepting) this concept, of decimal values not having an exact representation with a "float" or a "double", here is an article worth reading:
Why 0.1 Does Not Exist In Floating-Point
Many new programmers become aware of binary floating-point after seeing their programs give odd results: “Why does my program print 0.10000000000000001 when I enter 0.1?”; “Why does 0.3 + 0.6 = 0.89999999999999991?”; “Why does 6 * 0.1 not equal 0.6?” Questions like these are asked every day, on online forums like stackoverflow.com.
The answer is that most decimals have infinite representations in binary. Take 0.1 for example. It’s one of the simplest decimals you can think of, and yet it looks so complicated in binary:
Decimal 0.1 In Binary ( To 1369 Places
The bits go on forever; no matter how many of those bits you store in a computer, you will never end up with the binary equivalent of decimal 0.1.
... Read the rest of the article at: http://www.exploringbinary.com/why-0-point-1-does-not-exist-in-floating-point/
Forum on trading, automated trading systems and testing trading strategies
how to convert double with zeros in front to integer?
Fernando Carreiro, 2023.01.11 14:03
Take some time to learn how floating point number work and are stored:
To help understand (debugging purposes) the format of real number also look at the follow printing formats — PrintFormat
a
double
A real number in format [−]0xh.hhhh p±dd, where h.hhhh – mantissa in the form of hexadecimal digits, using "abcdef", dd - One or more digits of exponent. Number of decimal places is determined by the accuracy specification
A
double
A real number in format [−]0xh.hhhh P±dd, where h.hhhh – mantissa in the form of hexadecimal digits, using "ABCDEF", dd - One or more digits of exponent. Number of decimal places is determined by the accuracy specification
afael Santos #: Updated code same results:
What is the same results? Are you referring to the values in the debugger having many decimal points?
That is normal. Floating point numbers are binary numbers and cannot represent decimal numbers precisely. One is base 2 and the other is base 10.
Forum on trading, automated trading systems and testing trading strategies
//+------------------------------------------------------------------+ //| Trailing stop | //+------------------------------------------------------------------+ void applyTrailingStop(void) { MqlTick currentTick; if(SymbolInfoTick(_Symbol, currentTick)) { double Bid = NormalizeDouble(currentTick.bid,_Digits); double Ask = NormalizeDouble(currentTick.ask,_Digits); int totalOrders = PositionsTotal(); double trailingStopPointsapply = trailingStopPoints * _Point; for(int i = 0; i < totalOrders; i++) { ulong ticket =PositionGetInteger(POSITION_TICKET); const string symbol = PositionGetString(POSITION_SYMBOL); if(symbol != _Symbol) continue; // Skip positions of other symbols if(PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; // Skip positions with a different magic number double openPrice = NormalizeDouble(PositionGetDouble(POSITION_PRICE_OPEN),_Digits); double stopLoss = NormalizeDouble(PositionGetDouble(POSITION_SL),_Digits); double postionTP = NormalizeDouble(PositionGetDouble(POSITION_TP),_Digits); double newStopLoss = 0; if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { newStopLoss = NormalizeDouble(normalizePrice(Bid - trailingStopPointsapply), _Digits); if(newStopLoss > openPrice && (stopLoss < openPrice || newStopLoss > stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, postionTP)) { if(!trade.PositionModify(ticket, newStopLoss, postionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { newStopLoss = NormalizeDouble(Ask + trailingStopPointsapply, _Digits); if(newStopLoss < openPrice && (stopLoss > openPrice || newStopLoss < stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, postionTP)) { if(!trade.PositionModify(ticket, newStopLoss, postionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } } } }
The error lies in the highlighted function. The market moves too much in the opposite direction with random latency, so the calculated sl price becomes invalid.
Could you please provide the code?The error lies in the highlighted function. The market moves too much in the opposite direction with random latency, so the calculated sl price becomes invalid.
Could you please provide the code?Hello Amrali, I'll be happy to provide it to you. Please feel free to point out any discrepancies or potential issues.
I wanted to mention that I've called the checkStopLossTakeProfitLevels function within the checkForOpen function to initiate the initial trade, and I haven't encountered errors in both exact execution and random latency scenarios. However, errors seem to occur during random latency when the trailing stop function is invoked.
More information: https://www.mql5.com/en/forum/444428
//+------------------------------------------------------------------+ //| Test2.mq5 | //| Copyright 2023,Santos Family Holdings LLC | //+------------------------------------------------------------------+ #property copyright "Copyright 2023,Santos Family Holdings LLC" #property version "1.0" #include <Trade\Trade.mqh> #include <Trade\AccountInfo.mqh> #include <MovingAverages.mqh> input int magicNumber = 11162022;// Magic Number input int startHour = 0; //Start Hour input int startMinute = 0; //Start Minute input int endHour = 23; // End Hour input int endMinute = 59; // End minute input double maxDailyDrawdownPercent = 10.0; // Maximum daily drawdown percent input double lotSize = 0.1; // Lot size input int stopLossPoints =3000; // Stoploss in points input int takeProfitPoints =13000; // Take profit in points input int trailingStopPoints = 1000; //Trailing stoploss in points input bool tradeMonday = true; //Trade on Monday input bool tradeTuesday = true; //Trade on Tuesday input bool tradeWednesday = true; //Trade on Wednesday input bool tradeThursday = true; //Trade on Thursday input bool tradeFriday = true; //Trade on Friday //Global Variables double PointValue =0; //Trade classes and Handles CTrade trade; CAccountInfo AccInfo; int ma6Handle, ma26Handle, ma58Handle, ma60Handle; //+------------------------------------------------------------------+ //| Oninit | //+------------------------------------------------------------------+ int OnInit() { //Set Magic number and Filing type trade.SetTypeFillingBySymbol(_Symbol); trade.SetExpertMagicNumber(magicNumber); // Initialize moving averages ma6Handle = iMA(_Symbol, PERIOD_CURRENT, 6, 0, MODE_SMA, PRICE_CLOSE); ma26Handle = iMA(_Symbol, PERIOD_CURRENT, 26, 0, MODE_SMA, PRICE_CLOSE); ma58Handle = iMA(_Symbol, PERIOD_CURRENT, 58, 0, MODE_SMA, PRICE_CLOSE); ma60Handle = iMA(_Symbol, PERIOD_CURRENT, 60, 0, MODE_SMA, PRICE_CLOSE); if(ma6Handle == INVALID_HANDLE || ma26Handle == INVALID_HANDLE || ma58Handle == INVALID_HANDLE || ma60Handle == INVALID_HANDLE) { return INIT_FAILED; } else { Print("Moving average indicator handles have been initialized "); } //Check valid lotsize if(!checkValidVolume(lotSize)) { Print("Invalid Volume, Check lot size: ",lotSize); return INIT_FAILED; } else { Print("Lot size is withing the symbol requirements, lot size: ", lotSize); } //Check if trading is allowed bool tradeAllowed= TerminalInfoInteger(TERMINAL_TRADE_ALLOWED); if(!tradeAllowed) { Print("Terminal check: Trading is not allowed"); return INIT_FAILED; } else { Print("Terminal check: Trading is allowed"); } //Allow EA to hadle 3 and 5 digit symbols double MyPoint=_Point; if(_Digits==3 || _Digits==5) MyPoint=_Point*10; PointValue= MyPoint; if(!PointValue) { Print("Current symbol point value Error: ",GetLastError()); return INIT_FAILED; } else { Print("The current symbol point value is: ",PointValue); } //Check for sufficient price history if(!Bars(_Symbol,_Period)>100) { Print("There is not enough price history available on this chart."); return INIT_FAILED; } else {Print("There is enough price data for trading on this chart");} //Check stop loss levels if(stopLossPoints < SYMBOL_TRADE_STOPS_LEVEL || takeProfitPoints < SYMBOL_TRADE_STOPS_LEVEL) { Print("Invalid Stop loss or Take Profit. The minimum stop loss and take profit level for the current symbol is: ",SYMBOL_TRADE_STOPS_LEVEL); return INIT_FAILED; } else {Print("The stop loss and take profit are above the minimum stop level requirements for the current symbol. Minimum level allowed is : ", SYMBOL_TRADE_STOPS_LEVEL);} return INIT_SUCCEEDED; } //+------------------------------------------------------------------+ //| Check for open | //+------------------------------------------------------------------+ void CheckForOpen(void) { // Run Checks before placing trades if(!isTradingTime()) {Print("It is not time to trade, Start Hour: ", startHour,"Start Minute: ", startMinute);} else { if(!isTradingDay()) {Print("Today is not an active trading day, check inputs for more information");} else { if(!checkMaxDailyDrawdown()) {Print("The Max Daily drawdown has been reached There will be no more trading for today");} else { // Update moving average values double ma6Value[2], ma26Value[2], ma58Value[2], ma60Value[2]; if(CopyBuffer(ma6Handle, 0, 0, 2, ma6Value) != 2 || CopyBuffer(ma26Handle, 0, 0, 2, ma26Value) != 2 || CopyBuffer(ma58Handle, 0, 0, 2, ma58Value) != 2 || CopyBuffer(ma60Handle, 0, 0, 2, ma60Value) != 2) { Print("Error updating moving average values."); return; } ENUM_ORDER_TYPE signal=WRONG_VALUE; if(//---Buy conditions //example condition for testing ma6Value[0] > ma60Value[0] ) signal=ORDER_TYPE_BUY; // Set buy order type if(//---Sell Conditions //example condition for testing ma6Value[0] < ma60Value[0] ) signal=ORDER_TYPE_SELL; // Set sell order type // get the current Actual ask/bid price MqlTick currentTick; if(SymbolInfoTick(_Symbol, currentTick)) { double bidPrice = currentTick.bid; double askPrice = currentTick.ask; if(signal==ORDER_TYPE_BUY) { double entryPrice = normalizePrice(askPrice); double stopLoss = normalizePrice(entryPrice - stopLossPoints * PointValue); double takeProfit = normalizePrice(entryPrice + takeProfitPoints * PointValue); Print("Buy Order Entry Price: ", entryPrice," Buy Order Stoploss: ",stopLoss," Buy Order Take profit: ",takeProfit," Symbol trade stops level: ", SYMBOL_TRADE_STOPS_LEVEL*_Point," Symbol Freeze Level: ", SYMBOL_TRADE_FREEZE_LEVEL*_Point," checkStopLossTakeProfitLevels(): ", checkStopLossTakeProfitLevels(entryPrice,stopLoss,takeProfit)); double marginCheck = AccInfo.MarginCheck(_Symbol,ORDER_TYPE_BUY,lotSize,entryPrice); double freeMarginCheck = AccInfo.FreeMargin(); //Print("marginCheck: ", marginCheck,"freeMarginCheck: ",freeMarginCheck); //Check margin and stop levels if(marginCheck < freeMarginCheck && checkStopLossTakeProfitLevels(entryPrice,stopLoss,takeProfit)) { //Open buy trade if(!trade.PositionOpen(_Symbol,ORDER_TYPE_BUY,lotSize,entryPrice,stopLoss,takeProfit)) {Print("Buy trade Error code ", GetLastError());} } } else { if(signal==ORDER_TYPE_SELL) { double entryPrice = normalizePrice(bidPrice); double stopLoss = normalizePrice(entryPrice + stopLossPoints * PointValue); double takeProfit = normalizePrice(entryPrice - takeProfitPoints * PointValue); Print("Sell Order Entry Price: ", entryPrice," Sell Order Stoploss: ",stopLoss," Sell Order Take profit: ",takeProfit," Symbol trade stops level: ", SYMBOL_TRADE_STOPS_LEVEL*_Point," Symbol Freeze Level: ", SYMBOL_TRADE_FREEZE_LEVEL*_Point," checkStopLossTakeProfitLevels(): ", checkStopLossTakeProfitLevels(entryPrice,stopLoss,takeProfit)); double marginCheck = AccInfo.MarginCheck(_Symbol,ORDER_TYPE_SELL,lotSize,entryPrice); double freeMarginCheck = AccInfo.FreeMargin(); //Print("marginCheck: ", marginCheck,"freeMarginCheck: ",freeMarginCheck); //Check margin and stop levels if(marginCheck < freeMarginCheck && checkStopLossTakeProfitLevels(entryPrice,stopLoss,takeProfit)) { //Open sell trade if(!trade.PositionOpen(_Symbol,ORDER_TYPE_SELL,lotSize,entryPrice,stopLoss,takeProfit)) {Print("Sell trade Error code ", GetLastError());} } } } } } } } } //+------------------------------------------------------------------+ //|OnTick | //+------------------------------------------------------------------+ void OnTick() { //---If there are no open orders, check for open conditions if(TotalOrdersCount() <= 0) CheckForOpen(); //---Update trailing stop // if(trailingStopPoints > 0 && TotalOrdersCount() > 0) // applyTrailingStop(); //---Comments //---Ask-Bid-Spread -Bar Time remaining double Ask,Bid,Spread; string remainingTime = TimeRemaining(); Ask=SymbolInfoDouble(Symbol(),SYMBOL_ASK); Bid=SymbolInfoDouble(Symbol(),SYMBOL_BID); Spread= NormalizeDouble(MathAbs(Ask-Bid),_Digits); //--- Output values in three lines Comment(StringFormat("Ask = %G\nBid = %G\nSpread = %G\nBar closes in: %s\n",Ask,Bid,Spread,remainingTime)); } //+------------------------------------------------------------------+ //| Check for active trading time | //+------------------------------------------------------------------+ bool isTradingTime() { MqlDateTime mqlDateTime; TimeToStruct(TimeCurrent(), mqlDateTime); int currentHour = mqlDateTime.hour; int currentMinute = mqlDateTime.min; int startTimeInMinutes = startHour * 60 + startMinute; int endTimeInMinutes = endHour * 60 + endMinute; int currentTimeInMinutes = currentHour * 60 + currentMinute; if(startTimeInMinutes <= currentTimeInMinutes && currentTimeInMinutes <= endTimeInMinutes) { return true; } return false; } //+------------------------------------------------------------------+ //| Check volume | //+------------------------------------------------------------------+ bool checkValidVolume(double volume) { double minVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN); double maxVolume = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX); double volumeStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP); if(volume < minVolume || volume > maxVolume) { return false; } double volumeDifference = volume - minVolume; double stepCount = MathRound(volumeDifference / volumeStep); if(MathAbs(volumeDifference - stepCount * volumeStep) > 0.0001) { return false; } return true; } //+------------------------------------------------------------------+ //| Check SL TP Levels | //+------------------------------------------------------------------+ bool checkStopLossTakeProfitLevels(double entryPrice, double stopLoss, double takeProfit) { double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * _Point; double freezeLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL) * _Point; if(MathAbs(entryPrice - stopLoss) < minStopLevel || MathAbs(entryPrice - takeProfit) < minStopLevel) { return false; } if(MathAbs(entryPrice - stopLoss) < freezeLevel || MathAbs(entryPrice - takeProfit) < freezeLevel) { return false; } return true; } //+------------------------------------------------------------------+ //| Check for active trading day | //+------------------------------------------------------------------+ bool isTradingDay() { datetime currentTime = TimeCurrent(); MqlDateTime mqlDateTime; TimeToStruct(currentTime, mqlDateTime); ENUM_DAY_OF_WEEK currentDayOfWeek = (ENUM_DAY_OF_WEEK)mqlDateTime.day_of_week; switch(currentDayOfWeek) { case MONDAY: return tradeMonday; case TUESDAY: return tradeTuesday; case WEDNESDAY: return tradeWednesday; case THURSDAY: return tradeThursday; case FRIDAY: return tradeFriday; default: return false; } } //+------------------------------------------------------------------+ //|Check max daily drawdown | //+------------------------------------------------------------------+ bool checkMaxDailyDrawdown(void) { static datetime lastTradingDay = 0; static double initialBalance = 0; datetime currentDay = TimeCurrent() / 86400; if(currentDay > lastTradingDay) { lastTradingDay = currentDay; initialBalance = AccountInfoDouble(ACCOUNT_BALANCE); } double currentBalance = AccountInfoDouble(ACCOUNT_BALANCE); double drawdown = initialBalance - currentBalance; double drawdownPercent = (drawdown / initialBalance) * 100; //Print("Calculated draw down percent is :", drawdownPercent,"the max daily drawdown percent is: ",maxDailyDrawdownPercent); if(maxDailyDrawdownPercent == 0) { return true; } else { return drawdownPercent <= maxDailyDrawdownPercent; } } //+------------------------------------------------------------------+ //| Trailing stop | //+------------------------------------------------------------------+ void applyTrailingStop(void) { MqlTick currentTick; if(SymbolInfoTick(_Symbol, currentTick)) { double Bid = currentTick.bid; double Ask = currentTick.ask; int totalOrders = PositionsTotal(); double trailingStopPointsapply = trailingStopPoints * _Point; for(int i = 0; i < totalOrders; i++) { ulong ticket =PositionGetInteger(POSITION_TICKET); const string symbol = PositionGetString(POSITION_SYMBOL); if(symbol != _Symbol) continue; // Skip positions of other symbols if(PositionGetInteger(POSITION_MAGIC) != magicNumber) continue; // Skip positions with a different magic number double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double stopLoss = PositionGetDouble(POSITION_SL); double positionTP = PositionGetDouble(POSITION_TP); double newStopLoss = 0; if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) { newStopLoss = normalizePrice(Bid - trailingStopPointsapply); if(newStopLoss > openPrice && (stopLoss < openPrice || newStopLoss > stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, positionTP)) { if(!trade.PositionModify(ticket, newStopLoss, positionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } else if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) { newStopLoss = normalizePrice(Ask + trailingStopPointsapply); if(newStopLoss < openPrice && (stopLoss > openPrice || newStopLoss < stopLoss) && newStopLoss != stopLoss && checkStopLossTakeProfitLevels(openPrice, newStopLoss, positionTP)) { if(!trade.PositionModify(ticket, newStopLoss, positionTP)) { PrintFormat("Error: Failed to modify position. Error code: %d", GetLastError()); } } } } } } //+------------------------------------------------------------------+ //|Total Orders count | //+------------------------------------------------------------------+ int TotalOrdersCount() { int result=0; int posTotal=PositionsTotal(); for(int posIndex=posTotal-1; posIndex>=0; posIndex--) { ulong ticket=PositionGetTicket(posIndex); if(PositionSelectByTicket(ticket) && PositionGetInteger(POSITION_MAGIC)==magicNumber) result++; } return (result); } //+------------------------------------------------------------------+ //| Normalize price according to tick size | //+------------------------------------------------------------------+ double normalizePrice(double price) { double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); return(MathRound(price / tickSize) * tickSize); } //+------------------------------------------------------------------+ //|Time Remaining on current bar | //+------------------------------------------------------------------+ string TimeRemaining() { datetime currentTime = TimeCurrent(); datetime currentBarOpenTime = iTime(_Symbol, PERIOD_CURRENT, 0); int periodInSeconds = PeriodSeconds(PERIOD_CURRENT); datetime currentBarCloseTime = currentBarOpenTime + periodInSeconds; int remainingTime = (int)(currentBarCloseTime - currentTime); int hours = remainingTime / 3600; int minutes = (remainingTime % 3600) / 60; int seconds = remainingTime % 60; string remainingTimeString = StringFormat("%02d:%02d:%02d", hours, minutes, seconds); return remainingTimeString; } //+------------------------------------------------------------------+
![[Invalid Stops]- Print("Stop Level is: ", stops_level); returning 0 - How to fix an invalid stop error in my EA code [Invalid Stops]- Print("Stop Level is: ", stops_level); returning 0 - How to fix an invalid stop error in my EA code](https://c.mql5.com/36/62/invalid-stops-print-stop-level__1.jpg)
- 2023.03.28
- www.mql5.com

- Free trading apps
- Over 8,000 signals for copying
- Economic news for exploring financial markets
You agree to website policy and terms of use
I've encountered an "invalid parameters" error when invoking the following trailing stop function. Upon using the debugger, I observed that the NormalizeDouble function appears to work correctly for the ASK and newStopLoss prices but not for any of other prices. I believe that I've employed the same syntax and code structure for all prices. Could there be an oversight or a subtle difference in the implementation that I might have missed?
I only get this error when testing with Random latency. With zero latency, I am all clear