Validation failed [Invalid Volume]

 
When uploading an EA to the market, it fails validation due to invalid volume error: 

test on EURUSD,H1 (netting) strategy tester report 22 total trades test on XAUUSD,D1 (netting) log files size exceeded 2080 MB, test terminated 2022.04.11 15:39:28 failed market sell 0.15 XAUUSD, close #47 buy 0.15 XAUUSD 1945.32 [Invalid volume] 2022.04.11 15:39:28 failed market sell 0.15 XAUUSD, close #47 buy 0.15 XAUUSD 1945.32 [Invalid volume] 2022.04.11 15:39:29 failed market sell 0.15 XAUUSD, close #47 buy 0.15 XAUUSD 1945.32 [Invalid volume]


Question: How is the EA allowed to open a position with invalid volume if it is not allowed to close the position due to invalid volume? 

I do have checks in place to prevent invalid volume, I even hardcoded lotstep of 0.1 increments on XAUUSD/GOLD if the EA is not able to retrieve the actual properties form the broker/symbol. 

Can anyone see what im missing? 

//+------------------------------------------------------------------+
//| Validate and constrain lot size with all safety checks
//+------------------------------------------------------------------+
double ValidateAndConstrainLotSize(double requestedLotSize, bool checkTotalVolume = true)
  {
// Step 1: Normalize to broker's lot step
   double lotStep = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_STEP);
   
   // If SymbolInfoDouble fails, try ensuring symbol is selected first
   if(lotStep <= 0)
     {
      SymbolSelect(symbolTraded, true);  // Ensure symbol is in Market Watch
      Sleep(100);  // Give it a moment to load
      lotStep = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_STEP);
     }
   
   // Last resort: use symbol-aware fallback
   if(lotStep <= 0)
     {
      if(StringFind(symbolTraded, "XAU") >= 0 || 
         StringFind(symbolTraded, "GOLD") >= 0)
        {
         lotStep = 0.1;
         Print("WARNING: Could not retrieve lot step for ", symbolTraded, " - using Gold fallback of 0.1");
        }
      else
        {
         lotStep = 0.01;  // Forex default
         Print("WARNING: Could not retrieve lot step for ", symbolTraded, " - using Forex fallback of 0.01");
        }
     }
   
   // Round to NEAREST valid step (not floor)
   double constrainedLot = MathRound(requestedLotSize / lotStep) * lotStep;
   
   // DEFENSIVE CHECK: If lot step is 0.01 but result isn't valid for 0.1 step,
   if(lotStep == 0.01)
     {
      double remainder = MathMod(constrainedLot * 100, 10);  // Check if multiple of 0.1
      if(remainder > 0.01)  // Not a clean multiple of 0.1 
        {
         constrainedLot = MathRound(constrainedLot / 0.1) * 0.1;  // Round to 0.1
        }
     }

// Step 2: Apply all maximum constraints
   constrainedLot = ApplyMaximumLotConstraints(constrainedLot);

// Step 3: Check total volume limits if requested
   if(checkTotalVolume)
     {
      constrainedLot = CheckTotalVolumeLimit(constrainedLot);
     }

// Step 4: Apply minimum constraints
   constrainedLot = ApplyMinimumLotConstraints(constrainedLot);

// Step 5: Check SYMBOL_VOLUME_LIMIT (total volume across all positions - CRITICAL for netting accounts!)
   double volumeLimit = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_LIMIT);
   if(volumeLimit > 0)  // If limit exists 
     {
      double currentTotalVolume = getAllVolume();
      double availableVolume = volumeLimit - currentTotalVolume;
      
      if(availableVolume <= 0)
        {
         Print("Cannot open position - SYMBOL_VOLUME_LIMIT reached (", currentTotalVolume, "/", volumeLimit, ")");
         return 0;
        }
      
      if(constrainedLot > availableVolume)
        {
         Print("Reducing lot from ", constrainedLot, " to ", availableVolume, " due to SYMBOL_VOLUME_LIMIT");
         constrainedLot = availableVolume;
         
         // Re-normalize after reduction to maintain valid lot step
         constrainedLot = MathRound(constrainedLot / lotStep) * lotStep;
         
         // Re-apply defensive check for 0.1 multiples
         if(lotStep == 0.01)
           {
            double remainder = MathMod(constrainedLot * 100, 10);
            if(remainder > 0.01)
              {
               constrainedLot = MathRound(constrainedLot / 0.1) * 0.1;
              }
           }
         
         // Ensure it's still above minimum after all adjustments
         double minLot = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MIN);
         if(minLot <= 0) minLot = 0.01;
         if(constrainedLot < minLot)
           {
            Print("After SYMBOL_VOLUME_LIMIT adjustment, lot size too small - cannot open position");
            return 0;
           }
        }
     }

   return NormalizeDouble(constrainedLot, 2);
  }
//+------------------------------------------------------------------+
//| Apply all maximum lot size constraints
//+------------------------------------------------------------------+
double ApplyMaximumLotConstraints(double lotSize)
  {
// Your custom max lot function
   lotSize = MathMin(lotSize, MaxLotPer1000());

// Broker's symbol maximum
   double brokerMax = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MAX);
   lotSize = MathMin(lotSize, brokerMax);

// Hard safety cap (especially important for volatile symbols like gold)
   double hardCap = MathMin(brokerMax, 100.0);
   lotSize = MathMin(lotSize, hardCap);

   return lotSize;
  }

//+------------------------------------------------------------------+
//| Check if lot size would exceed total volume limits
//+------------------------------------------------------------------+
double CheckTotalVolumeLimit(double requestedLotSize)
  {
   double currentTotalVolume = getAllVolume();
   double maxTotalVolume = MathMin(SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MAX), 100.0);

   if(currentTotalVolume + requestedLotSize > maxTotalVolume)
     {
      double availableVolume = maxTotalVolume - currentTotalVolume;
      if(availableVolume <= 0)
        {
         Print("Cannot open position - total volume limit reached (",
               currentTotalVolume, "/", maxTotalVolume, ")");
         return 0; // Signal that no position can be opened
        }

      Print("Reducing lot size from ", requestedLotSize, " to ", availableVolume,
            " to stay within total volume limit");
      return availableVolume;
     }

   return requestedLotSize;
  }

//+------------------------------------------------------------------+
//| Apply minimum lot size constraints
//+------------------------------------------------------------------+
double ApplyMinimumLotConstraints(double lotSize)
  {
   double minLot = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MIN);
   if(minLot <= 0)
      minLot = 0.01;

   return MathMax(lotSize, minLot);
  }

//+------------------------------------------------------------------+
//| Check if lot size is valid for opening positions
//+------------------------------------------------------------------+
bool IsValidLotSize(double lotSize)
  {
   double minLot = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MIN);
   double maxLot = SymbolInfoDouble(symbolTraded, SYMBOL_VOLUME_MAX);

   if(lotSize <= 0)
     {
      Print("Invalid lot size: ", lotSize, " (must be positive)");
      return false;
     }

   if(lotSize < minLot)
     {
      Print("Lot size too small: ", lotSize, " (min: ", minLot, ")");
      return false;
     }

   if(lotSize > maxLot)
     {
      Print("Lot size too large: ", lotSize, " (max: ", maxLot, ")");
      return false;
     }

   return true;
  }