//+------------------------------------------------------------------+
//|                                        PriceNormalization.mqh    |
//|   Normalisation correcte des prix : pas de cotation ET decimales |
//|   Corrige le retcode 10016 (invalid stops) sur les symboles ou   |
//|   SYMBOL_TRADE_TICK_SIZE differe de SYMBOL_POINT.                |
//+------------------------------------------------------------------+
#property copyright "Article MQL5 - Defauts silencieux"
#property version   "1.00"

//+------------------------------------------------------------------+
//| Normalise un prix au pas de cotation ET aux decimales            |
//| A utiliser pour TOUT prix envoye au broker (entry, SL, TP)       |
//+------------------------------------------------------------------+
double NormalizePriceToTick(const string symbol, const double price)
{
   double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickSize <= 0.0)
      tickSize = SymbolInfoDouble(symbol, SYMBOL_POINT);

   int digits = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);

   if(tickSize > 0.0)
      return NormalizeDouble(MathRound(price / tickSize) * tickSize, digits);

   return NormalizeDouble(price, digits);
}

//+------------------------------------------------------------------+
//| Verifie qu'un prix est bien aligne sur le pas de cotation        |
//| Utile en diagnostic avant envoi                                  |
//+------------------------------------------------------------------+
bool IsPriceAlignedToTick(const string symbol, const double price)
{
   double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
   if(tickSize <= 0.0)
      return true;

   double ratio = price / tickSize;
   return (MathAbs(ratio - MathRound(ratio)) < 1e-8);
}

//+------------------------------------------------------------------+
//| Ajuste SL et TP pour respecter la distance minimale du broker    |
//| ET le pas de cotation. Retourne false si impossible.             |
//+------------------------------------------------------------------+
bool EnsureValidStops(const string symbol,
                      const bool   isBuy,
                      const double entryPrice,
                      double &sl,
                      double &tp)
{
   long stopsLevel = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
   double point    = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double minDist  = stopsLevel * point;

   MqlTick tick;
   if(!SymbolInfoTick(symbol, tick))
      return false;

   double ref = (isBuy ? tick.ask : tick.bid);

   if(isBuy)
   {
      if(sl > 0.0 && (ref - sl) < minDist)
         sl = ref - minDist;
      if(tp > 0.0 && (tp - ref) < minDist)
         tp = ref + minDist;
   }
   else
   {
      if(sl > 0.0 && (sl - ref) < minDist)
         sl = ref + minDist;
      if(tp > 0.0 && (ref - tp) < minDist)
         tp = ref - minDist;
   }

   // NORMALISATION FINALE : apres tous les ajustements
   if(sl > 0.0) sl = NormalizePriceToTick(symbol, sl);
   if(tp > 0.0) tp = NormalizePriceToTick(symbol, tp);

   // verification de coherence directionnelle
   if(isBuy)
   {
      if(sl > 0.0 && sl >= ref) return false;
      if(tp > 0.0 && tp <= ref) return false;
   }
   else
   {
      if(sl > 0.0 && sl <= ref) return false;
      if(tp > 0.0 && tp >= ref) return false;
   }

   return true;
}

//+------------------------------------------------------------------+
//| Affiche les contraintes de cotation d'un symbole                 |
//| A appeler une fois au demarrage pour reperer les pieges          |
//+------------------------------------------------------------------+
void PrintSymbolConstraints(const string symbol)
{
   double point    = SymbolInfoDouble(symbol, SYMBOL_POINT);
   double tickSize = SymbolInfoDouble(symbol, SYMBOL_TRADE_TICK_SIZE);
   int    digits   = (int)SymbolInfoInteger(symbol, SYMBOL_DIGITS);
   long   stops    = SymbolInfoInteger(symbol, SYMBOL_TRADE_STOPS_LEVEL);
   long   freeze   = SymbolInfoInteger(symbol, SYMBOL_TRADE_FREEZE_LEVEL);

   PrintFormat("[SYMBOL] %s | point=%.8f tick_size=%.8f digits=%d "
               "stops_level=%d freeze_level=%d",
               symbol, point, tickSize, digits, (int)stops, (int)freeze);

   if(tickSize > 0.0 && MathAbs(tickSize - point) > 1e-12)
      PrintFormat("[SYMBOL] %s : ATTENTION tick_size (%.8f) != point (%.8f) "
                  "-> NormalizeDouble(digits) seul est INSUFFISANT",
                  symbol, tickSize, point);
}
//+------------------------------------------------------------------+
