Problem with average price and trailing stop

 
Hi everyone, I'm making an EA that uses a grid with trailing stops, to do this I use a function that calculates the average price of orders. Then I use it to move the stoploss of all orders when the price exceeds the average price + a certain amount of margin pips. All this does not report errors and works correctly 80% of the time, however there are situations that do not happen frequently where when all the stoploss are taken, it closes at a loss and I don't understand how this is possible. Do you notice any errors in the code?

if     (Point == 0.00001) p = 0.0001;
else if(Point == 0.001)   p = 0.01;
else                      p = Point;


void TrailingStopGrid()
{
    double attivazione = TrailingAttivazione * p;
    double passo = TrailingPasso * p;
    double step = TrailingStep * p;
    double WeightPrice = WeightedAvgPrice();
    
    double currentAsk = SymbolInfoDouble(Symbol(), SYMBOL_ASK);
    double currentBid = SymbolInfoDouble(Symbol(), SYMBOL_BID);

    for (int i = OrdersTotal() - 1; i >= 0; i--)
    {
        if (!OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
            continue;

        if (OrderSymbol() == Symbol())
        {
            if (OrderType() == OP_BUY && currentAsk > WeightPrice + attivazione)
            {
                if (OrderStopLoss() == 0)
                {
                        if (OrderModify(OrderTicket(), OrderOpenPrice(), WeightPrice + passo, 0, 0, Blue))
                        {
                            Print("TrailingStop Working");
                        }
                        else
                        {
                            Print("Error Trailing Stop: " + (string)GetLastError(), " Take Profit Trailing: ", newStopLoss);
                        }
                    }
                 if(OrderStopLoss() != 0 &&  Ask > OrderStopLoss() + passo + step) {
                       double newStopLoss = NormalizeDouble(OrderStopLoss() + passo, Digits);
                        if (OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, Blue))
                        {
                            Print("TrailingStop Working");
                        }
                        else
                        {
                            Print("Error Trailing Stop: " + (string)GetLastError(), " Take Profit Trailing: ", newStopLoss);
                        }
                 }   
            }
            else if (OrderType() == OP_SELL && currentBid < WeightPrice - attivazione)
            {
                if (OrderStopLoss() == 0)
                {
                        if (OrderModify(OrderTicket(), OrderOpenPrice(), WeightPrice - passo, 0, 0, Red))
                        {
                            Print("TrailingStop Working");
                        }
                        else
                        {
                            Print("Error Trailing Stop: " + (string)GetLastError(), " Take Profit Trailing: ", newStopLoss);
                        }
                    }
                  if(OrderStopLoss() != 0 && Bid < OrderStopLoss() - passo - step) {
                       double newStopLoss = NormalizeDouble(OrderStopLoss() - passo, Digits);
                        if (OrderModify(OrderTicket(), OrderOpenPrice(), newStopLoss, OrderTakeProfit(), 0, Blue))
                        {
                           Print("TrailingStop Working");
                        }
                        else
                        {
                            Print("Error Trailing Stop: " + (string)GetLastError(), " Take Profit Trailing: ", newStopLoss);
                        }
                 } 
       
            }
        }
    }
}

//+------------------------------------------------------------------+

double WeightedAvgPrice() 
{
   double price_buy = 0;
   double lots_buy  = 0;
   double price_sell = 0;
   double lots_sell  = 0;
   
   for (int i = OrdersTotal() - 1; i >= 0 && !_StopFlag; i--) 
   {
      if (OrderSelect( i, SELECT_BY_POS ) && OrderSymbol() == Symbol() ) 
      {
         if ( OrderType() == OP_BUY ) 
         {
            price_buy   += OrderOpenPrice() * OrderLots();
            lots_buy    += OrderLots();
         }
         
         if ( OrderType() == OP_SELL )
         {
            price_sell += OrderOpenPrice() * OrderLots();
            lots_sell  += OrderLots();
         }
      }
   }
   
   if ( price_buy  > 0 ) price_buy  /= lots_buy;
   if ( price_sell > 0 ) price_sell /= lots_sell;
      
   return ( NormalizeDouble( MathAbs(price_buy - price_sell), _Digits ));



In this chart it works well the first few times, then this descent occurs and as you can see the stoploss that are taken lead to a loss. This shouldn't happen
 
 return ( NormalizeDouble( MathAbs(price_buy - price_sell), _Digits ));

If you only have sell orders, you return a negative price, thus your problem.

Lots Weighted Average Price = ∑ op(i) × lot(i) ÷ ∑ lot(i). This is the break even price of all orders combined.
          Figuring out how to reopen several positions after forcibly closing them - Expert Advisors and Automated Trading - MQL5 programming forum #1 (2022)

For sell orders, lot is negative. Compute one sum.

 
  1.                    double newStopLoss = NormalizeDouble(OrderStopLoss() + passo, Digits);

    You used NormalizeDouble, It's use is usually wrong, as it is in your case.

    1. Floating point has an infinite number of decimals, it's you were not understanding floating point and that some numbers can't be represented exactly. (like 1/10.)
                Double-precision floating-point format - Wikipedia, the free encyclopedia

      See also The == operand. - MQL4 programming forum (2013)

    2. Print out your values to the precision you want with DoubleToString - Conversion Functions - MQL4 Reference.

    3. SL/TP (stops) need to be normalized to tick size (not Point) — code fails on non-currencies.
                On 5Digit Broker Stops are only allowed to be placed on full pip values. How to find out in mql? - MQL4 programming forum #10 (2011)

      And abide by the limits Requirements and Limitations in Making Trades - Appendixes - MQL4 Tutorial and that requires understanding floating point equality Can price != price ? - MQL4 programming forum (2012)

    4. Open price for pending orders need to be adjusted. On Currencies, Point == TickSize, so you will get the same answer, but it won't work on non-currencies. So do it right.
                Trailing Bar Entry EA - MQL4 programming forum (2013)
                Bid/Ask: (No Need) to use NormalizeDouble in OrderSend - MQL4 programming forum (2012)

    5. Lot size must also be adjusted to a multiple of LotStep and check against min and max. If that is not a power of 1/10 then NormalizeDouble is wrong. Do it right.
                (MT4 2013)) (MT5 2022))

    6. MathRound() and NormalizeDouble() are rounding in a different way. Make it explicit.
                MT4:NormalizeDouble - MQL5 programming forum (2017)
                How to Normalize - Expert Advisors and Automated Trading - MQL5 programming forum (2017)

    7. Prices you get from the terminal are already correct (normalized).

    8. PIP, Point, or Tick are all different in general.
                What is a TICK? - MQL4 programming forum (2014)

  2.                  if(OrderStopLoss() != 0 &&  Ask > OrderStopLoss() + passo + step) {

    You buy at the Ask and sell at the Bid. Pending Buy Stop orders become market orders when hit by the Ask.

    1. Your buy order's TP/SL (or Sell Stop's/Sell Limit's entry) are triggered when the Bid / OrderClosePrice reaches it. Using Ask±n, makes your SL shorter and your TP longer, by the spread. Don't you want the specified amount used in either direction?

    2. Your sell order's TP/SL (or Buy Stop's/Buy Limit's entry) will be triggered when the Ask / OrderClosePrice reaches it. To trigger close at a specific Bid price, add the average spread.
                MODE_SPREAD (Paul) - MQL4 programming forum - Page 3 #25

    3. The charts show Bid prices only. Turn on the Ask line to see how big the spread is (Tools → Options (control+O) → charts → Show ask line.)

      Most brokers with variable spreads widen considerably at end of day (5 PM ET) ± 30 minutes.
      My GBPJPY shows average spread = 26 points, average maximum spread = 134.
      My EURCHF shows average spread = 18 points, average maximum spread = 106.
      (your broker will be similar).
                Is it reasonable to have such a huge spreads (20 PIP spreads) in EURCHF? - General - MQL5 programming forum (2022)