Product Validation fail MQL5 only. [Invalid Stops] - page 2

 

Tyler James Wanta #: Ok. That makes sense. Thanks for clearing that part up. 

I tried normalizing the price like so:

(I know I should place code instead of images but the code formatting was not working)

but this still isn't working. In fact, it lead to a larger number of [Invalid Stops] errors when running validation. Any thoughts?

Please paste actual code and not a screenshot of code. It is working ...use the CODE button (Alt-S) when inserting code.

Code button in editor

Showing just a small portion of your code is not going to be enough to solve the issue.

We don't know what the rest of the code is doing nor do we know what values are being passed, nor do we know how you are placing the order or modifying the position.

Print out your values and show the log results of actual testing in your strategy tester. Don't carry out a validation until the results in the tester produce the correct results.

Test under different circumstances and change the values in the Symbol's contract specifications so you can see how your code is behaving.

For example, you are using MathAbs, which is only acceptable if the rest of the code has already filtering out incorrect values. Imagine however, if you set the stop-loss on the wrong side of the price, then your code with MathAbs would not catch that.

Also, you are doing floating point comparison. That is not always good, because equality in floating point can fail. So, although it is not error, it is advisable, given that you have to align to tick size and that Stops Level is in points (and already am integer value), then convert and use integer arithmetic and comparison.

Forum on trading, automated trading systems and testing trading strategies

NormalizeDouble not "working"

Fernando Carreiro, 2017.09.23 00:41

Just as a side note.

Initially, when I started with MQL, I would manipulate prices as "doubles". Nowadays, especially in the more complex EA's, I manipulate prices as "ints". At the very first opportunity, I convert a price into ticks:

int priceTicks = (int) round( price / tickSize );

From then on, all my calculations and manipulations are done with "ints". Not only is it more memory compact and much faster, but comparisons are much easier to handle.  Doing a "priceA == priceB" for "doubles" is quite problematic, but not for "ints" because it gives exact matches. Not to mention, that in this way prices, stop sizes, etc. are ALWAYS aligned.

Then, just before I have to place or modify an order, I then convert it back:

priceTicks * tickSize
EDIT: I do the same for volume/lots by using the broker's Lot-Step.
 

Here is a bit more code:

int TradeManager::PlaceMarketOrder(TicketType ticketType, double lots, double entryPrice, double stopLoss, double takeProfit, int &ticket)
{
    if (ticketType != TicketType::Buy && ticketType != TicketType::Sell)
    {
        return Errors::WRONG_ORDER_TYPE;
    }

    if (stopLoss > 0.0)
    {
        int error = CheckStopLoss(ticketType, entryPrice, stopLoss);
        if (error != Errors::NO_ERROR)
        {
            return error;
        }
    }

    lots = CleanLotSize(lots);
    return VersionSpecificTradeManager::PlaceMarketOrder(ticketType, lots, entryPrice, stopLoss, takeProfit, ticket);
}

int TradeManager::CheckStopLoss(TicketType ticketType, double entryPrice, double stopLoss)
{
    if ((ticketType == TicketType::Buy || ticketType == TicketType::BuyLimit || ticketType == TicketType::BuyStop) && stopLoss >= entryPrice)
    {
        return Errors::STOPLOSS_PAST_ENTRY;
    }

    if ((ticketType == TicketType::Sell || ticketType == TicketType::SellLimit || ticketType == TicketType::SellStop) && stopLoss <= entryPrice)
    {
        return Errors::STOPLOSS_PAST_ENTRY;
    }

    MqlTick currentTick;
    SymbolInfoTick(Symbol(), currentTick);

    double tickSize = SymbolInfoDouble(Symbol(), SYMBOL_TRADE_TICK_SIZE);
    double normalised_price = round(MathAbs(entryPrice - stopLoss) / tickSize) * tickSize;
    int minStopLoss = SymbolInfoInteger(Symbol(), SYMBOL_TRADE_STOPS_LEVEL);

    Print("Entry: ", entryPrice, ", SL: ", stopLoss, ", Normalized SL Dist.: ", normalised_price, ", Original SL Dist.: ", MathAbs(entryPrice - stopLoss), ", Ticket Type: ",
          ticketType, ", Ask: ", currentTick.ask, ", Bid: ", currentTick.bid, ", Min SL: ", minStopLoss);

    if (minStopLoss > 0 && normalised_price <= (minStopLoss * _Point))
    {
        return Errors::STOP_LOSS_TOO_SMALL;
    }

    return Errors::NO_ERROR;
}


The issue is that the check in 'CheckStopLoss', for the Min SL, isn't working. 


Here is a log from a trade in the tester:

2023.06.10 17:09:06.242 2023.06.06 03:12:00   Entry: 1.07146, SL: 1.07095, Normalized SL Dist.: 0.00051, Original SL Dist.: 0.0005100000000000104, Ticket Type: 1, Ask: 1.07146, Bid: 1.07144, Min SL: 0

Ticket Type of 1 is a buy. We can see the Entry is at the Ask, as it should be. Also, the entry and exit are normalized.


The tricky part is that the min SL is 0. I am using the default metaquotes-Demo server on MT5 for testing. 


If I run this on MT4, where I do have an account with a broker, this is the log:

2023.06.10 17:16:53.099 2023.01.12 04:46:22  The Retirement Achiever EA EURUSD,H1: Entry: 1.0768, SL: 1.0755, Normalized SL Dist.: 0.0013, Original SL Dist.: 0.001300000000000079, Ticket Type: 1, Ask: 1.0768, Bid: 1.0767, Min SL: 10, Min SL Points: 0.0001


Is there a way to connect to a metaquotes-demo server on MT5 with a Min SL? Otherwise I don't see anything else that could be an issue.


 
Tyler James Wanta #:

Here is a bit more code:

The issue is that the check in 'CheckStopLoss', for the Min SL, isn't working. 

Here is a log from a trade in the tester:

2023.06.10 17:09:06.242 2023.06.06 03:12:00   Entry: 1.07146, SL: 1.07095, Normalized SL Dist.: 0.00051, Original SL Dist.: 0.0005100000000000104, Ticket Type: 1, Ask: 1.07146, Bid: 1.07144, Min SL: 0

Ticket Type of 1 is a buy. We can see the Entry is at the Ask, as it should be. Also, the entry and exit are normalized.

The tricky part is that the min SL is 0. I am using the default metaquotes-Demo server on MT5 for testing. 

If I run this on MT4, where I do have an account with a broker, this is the log:

2023.06.10 17:16:53.099 2023.01.12 04:46:22  The Retirement Achiever EA EURUSD,H1: Entry: 1.0768, SL: 1.0755, Normalized SL Dist.: 0.0013, Original SL Dist.: 0.001300000000000079, Ticket Type: 1, Ask: 1.0768, Bid: 1.0767, Min SL: 10, Min SL Points: 0.0001

Is there a way to connect to a metaquotes-demo server on MT5 with a Min SL? Otherwise I don't see anything else that could be an issue.

You are working with 3 current prices "Entry Price", "Ask", "Bid". What if somewhere in your code the entry price is not set correctly? Rethink your code to reduce the possibility of an error.

You have shown the output of your log prints, but you did not show the log output of the actual order being sent and filled.

Even if an account, does not have a Stops Level by default, set one yourself in the tester (on MT5):



 

The entry price is set here and is not wrong:

void StartOfDayTimeRangeBreakout::PlaceOrders()
{
    double entry = 0.0;
    double stopLoss = 0.0;

    if (SetupType() == SignalType::Bullish)
    {
        entry = CurrentTick().Ask();
        stopLoss = mTRB.RangeLow();
    }
    else if (SetupType() == SignalType::Bearish)
    {
        entry = CurrentTick().Bid();
        stopLoss = mTRB.RangeHigh();
    }

    EAOrderHelper::PlaceMarketOrder<StartOfDayTimeRangeBreakout>(this, entry, stopLoss);
    mStopTrading = true;
}

I set a Min SL and here is some more logging info. The trades are going through fine here at 10 

2023.06.10 17:39:43.874 2023.06.05 05:00:04   Entry: 1.07009, SL: 1.07077, Normalized SL Dist.: 0.00068, Original SL Dist.: 0.0006800000000000139, Ticket Type: 2, Ask: 1.0701100000000001, Bid: 1.07009, Min SL: 10, Min SL Points: 0.0001

2023.06.10 17:39:43.874 2023.06.05 05:00:04   market sell 1.41 EURUSD sl: 1.07077 (1.07009 / 1.07011 / 1.07009)

2023.06.10 17:39:43.874 2023.06.05 05:00:04   deal #10 sell 1.41 EURUSD at 1.07009 done (based on order #10)

2023.06.10 17:39:43.874 2023.06.05 05:00:04   deal performed [#10 sell 1.41 EURUSD at 1.07009]

2023.06.10 17:39:43.874 2023.06.05 05:00:04   order performed sell 1.41 at 1.07009 [#10 sell 1.41 EURUSD at 1.07009]

2023.06.10 17:39:43.876 2023.06.05 05:00:04   CTrade::OrderSend: market sell 1.41 EURUSD sl: 1.07077 [done at 1.07009]


When upping the min SL to 100 and adding more logging, everything works as expected, and no order gets placed:

2023.06.10 17:42:38.864 2023.06.02 02:00:00   Entry: 1.07658, SL: 1.07586, Normalized SL Dist.: 0.00072, Original SL Dist.: 0.0007200000000000539, Ticket Type: 1, Ask: 1.07658, Bid: 1.07654, Min SL: 100, Min SL Points: 0.001

2023.06.10 17:42:38.864 2023.06.02 02:00:00   Normalized Price: 0.00072, is smaller than min SL: 0.001


Everything seems to be working as expected on my end but when uploading it to the marketplace it still fails validation with [Invalid Stops]. I'm not placing a TP and I am never modifying the orders. 


 
Tyler James Wanta #:   I set a Min SL and here is some more logging info. The trades are going through fine here at 10 When upping the min SL to 100 and adding more logging, everything works as expected, and no order gets placed:

Everything seems to be working as expected on my end but when uploading it to the marketplace it still fails validation with [Invalid Stops]. I'm not placing a TP and I am never modifying the orders. 

Ok, now show us the validation results, with your debugs prints still active. Show as much as possible. So we can pickup on clues if necessary.

 
test on EURUSD,H1 (netting)
 2019.04.05 02:21:30   failed instant buy 2.8 EURUSD at 1.12236 sl: 1.12200 [Invalid stops]
 2019.04.11 03:30:06   failed instant sell 3.1 EURUSD at 1.12732 sl: 1.12763 [Invalid stops]
 2019.04.16 04:21:25   failed instant sell 3.2 EURUSD at 1.12999 sl: 1.13030 [Invalid stops]
 2019.04.18 03:00:02   failed instant sell 2.85 EURUSD at 1.12960 sl: 1.12995 [Invalid stops]
 2019.04.23 02:00:34   failed instant buy 3.1 EURUSD at 1.12607 sl: 1.12575 [Invalid stops]
 2019.04.29 02:39:30   failed instant sell 3.55 EURUSD at 1.11476 sl: 1.11507 [Invalid stops]
 2019.05.03 03:00:42   failed instant sell 2.9 EURUSD at 1.11714 sl: 1.11751 [Invalid stops]
 2019.05.06 15:46:42   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:42   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:42   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:43   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:43   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:43   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:43   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:44   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:44   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:44   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:44   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:45   failed instant sell 0.5 EURUSD at 1.11788, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:45   failed instant sell 0.5 EURUSD at 1.11787, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 2019.05.06 15:46:45   failed instant sell 0.5 EURUSD at 1.11789, close #68 buy 0.5 EURUSD 1.12009 [Modification failed due to order or position being close to market]
 
Tyler James Wanta #: Modification failed due to order or position being close to market

Are these placement of new orders or modifications to existing positions?

For this error, you are not checking the Freeze Level.

Tyler James Wanta #: Invalid stops

For these, the same question — are these placement of new orders or modifications to existing positions?

Tyler James Wanta #: netting

Please note that the test is being run on a "netting" account.

Given that you mentioned that you are coding an EA for both MQL4 and MQL5, it would make more sense to be testing against a "hedging" account on MT5 and not a "netting" account.

Is you code compatible with netting account?

 

These should all be placement of new orders or close of existing orders. The EA doesn't do any modification to the orders once they are placed. Do I need to check Freeze level when closing a position? 

The code should work with a netting account. It only opens a single position at a time, and in only one direction at a time. I've tested this and made sure this works. 

 
Tyler James Wanta #: These should all be placement of new orders or close of existing orders. The EA doesn't do any modification to the orders once they are placed. Do I need to check Freeze level when closing a position? The code should work with a netting account. It only opens a single position at a time, and in only one direction at a time. I've tested this and made sure this works. 

For the closing of the positions you need to also check the Freeze Level.

As for the rest, without access to the complete code or more debug information (given that your prints are not being output in the verification process), it is difficult to analyse the issue.

Try doing a verification against a "hedging" account and see if a similar situation comes up or not. If the results are different between "hedging" and "netting", then there is definitely a "netting" incomparability.

 

By the way here are the equivalent requirements for MQL4 ... https://book.mql4.com/appendix/limits

Requirements and Limitations in Making Trades


Tables below show calculation values that limit the conduction of trades when opening, closing, placing, deleting or modifying orders.

To get the minimum distance to StopLevel and freezing distance FreezeLevel the MarketInfo() function should be called.


Requirements.

Correct prices used when performing trade operations.
Order Type
Open Price
Close Price
Open Price of a Pending Order
Transforming a Pending Order into aMarket Order
Buy
Ask
Bid


Sell
Bid
Ask


BuyLimit


Below the current Ask price
Ask price reaches open price
SellLimit


Above the current Bid price
Bid price reaches open price
BuyStop


Above the current Ask price
Ask price reaches open price
SellStop


Below the current Bid price
Bid price reaches open price

The possibility of deleting a pending order is regulated by the FreezeLevel parameter.


StopLevel Minimum Distance Limitation.

A trade operation will not be performed if any of the following conditions is disrupted.

Order Type
Open Price StopLoss (SL) TakeProfit (TP)
Buy
Modification is prohibited
Bid-SL StopLevel TP-Bid StopLevel
Sell
Modification is prohibited SL-Ask StopLevel Ask-TP StopLevel
BuyLimit
Ask-OpenPriceStopLevel OpenPrice-SL StopLevel TP-OpenPrice StopLevel
SellLimit
OpenPrice-Bid StopLevel SL-OpenPrice StopLevel OpenPrice-TP StopLevel
BuyStop
OpenPrice-Ask StopLevel OpenPrice-SL StopLevel TP-OpenPrice StopLevel
SellStop
Bid-OpenPrice StopLevel SL-OpenPrice StopLevel OpenPrice-TP StopLevel

FreezeLevel Limitation (Freezing Distance).

Market orders can not be closed if the StopLoss and TakeProfit values violate the FreezLevel parameter requirements.
StopLoss or TakeProfit orders can not be modified if StopLoss or TakeProfit values violate the StopLevel parameter requirements.
Pending orders can not be deleted or modified if the declared open price violates the FreezeLevel parameter requirements.

Order Type
Open Price StopLoss (SL) TakeProfit (TP)
Buy
Modification is prohibited Bid-SL > FreezeLevel TP-Bid > FreezeLevel
Sell
Modification is prohibited SL-Ask > FreezeLevel Ask-TP > FreezeLevel
BuyLimit
Ask-OpenPrice > FreezeLevel Regulated by the StopLevel parameter
Regulated by the StopLevel parameter
SellLimit
OpenPrice-Bid > FreezeLevel Regulated by the StopLevel parameter Regulated by the StopLevel parameter
BuyStop
OpenPrice-Ask > FreezeLevel Regulated by the StopLevel parameter Regulated by the StopLevel parameter
SellStop
Bid-OpenPrice > FreezeLevel Regulated by the StopLevel parameter Regulated by the StopLevel parameter