//+------------------------------------------------------------------+
//|                                                MathUtilities.mqh |
//| Financial utility functions under test.                          |
//| These functions are the production code being validated by       |
//| the unit testing framework.                                      |
//+------------------------------------------------------------------+
#ifndef MATHUTILITIES_MQH
#define MATHUTILITIES_MQH

//+------------------------------------------------------------------+
//| NormalizeLot                                                     |
//| Purpose: Rounds a computed lot size to the nearest valid lot step|
//|          and clamps the output within min/max volume boundaries. |
//+------------------------------------------------------------------+
double NormalizeLot(double lot, double lot_step, double min_lot, double max_lot)
  {
//--- Prevent division by zero validation failures
   if(lot_step <= 0.0)
      return(min_lot);

//--- Perform mathematical half-up rounding to nearest step volume
   double normalized = MathFloor(lot / lot_step + 0.5) * lot_step;

//--- Enforce structural protective boundary conditions
   if(normalized < min_lot)
      normalized = min_lot;
   if(normalized > max_lot)
      normalized = max_lot;

   return(NormalizeDouble(normalized, 2));
  }

//+------------------------------------------------------------------+
//| CalcLotSize                                                      |
//| Purpose: Computes position volume sizing based on account equity,|
//|          risk parameters, and absolute point evaluations.        |
//+------------------------------------------------------------------+
double CalcLotSize(double equity, double risk_fraction,
                   double sl_points, double point_value,
                   double lot_step, double min_lot, double max_lot,
                   bool &error_flag)
  {
   error_flag = false;

//--- Validate operational parameter boundaries to prevent arithmetic faults
   if(equity <= 0.0 || risk_fraction <= 0.0 || sl_points <= 0.0 || point_value <= 0.0)
     {
      error_flag = true;
      return(0.0);
     }

//--- Derive final normalized risk-adjusted trading lot size
   double risk_amount = equity * risk_fraction;
   double raw_lot     = risk_amount / (sl_points * point_value);

   return(NormalizeLot(raw_lot, lot_step, min_lot, max_lot));
  }

//+------------------------------------------------------------------+
//| ScaleATR                                                         |
//| Purpose: Multiplies an ATR value by a scaling factor and bounds  |
//|          the output to exact fractional point increments.        |
//+------------------------------------------------------------------+
double ScaleATR(double atr_value, double multiplier, double point)
  {
   if(atr_value <= 0.0 || multiplier <= 0.0 || point <= 0.0)
      return(0.0);

   double raw    = atr_value * multiplier;
   long   points = (long)MathRound(raw / point);

   return((double)points * point);
  }

//+------------------------------------------------------------------+
//| NormalizeSpread                                                  |
//| Purpose: Converts raw terminal spread integer units into matching|
//|          points adjusting for pricing precision matrices.        |
//+------------------------------------------------------------------+
double NormalizeSpread(long raw_spread, int symbol_digits)
  {
//--- For five-digit and three-digit pairs, raw spread maps directly to points
   if(symbol_digits == 5 || symbol_digits == 3)
      return((double)raw_spread);

//--- Normalize traditional two-digit symbols (e.g., standard indices/crypto)
   if(symbol_digits == 2)
      return((double)raw_spread * 10.0);

   return((double)raw_spread);
  }

//+------------------------------------------------------------------+
//| CalcPipValue                                                     |
//| Purpose: Computes the asset-relative monetary value of a single  |
//|          pip calculation unit per standard lot.                  |
//+------------------------------------------------------------------+
double CalcPipValue(double pip_size, double quote_price, bool is_direct_quote)
  {
   if(pip_size <= 0.0 || quote_price <= 0.0)
      return(0.0);

//--- Route computation base conditional on quotation mechanics
   if(is_direct_quote)
      return(pip_size);

   return(pip_size / quote_price);
  }

//+------------------------------------------------------------------+
//| RoundLotToStep                                                   |
//| Purpose: Floor-truncates a trading volume calculation directly   |
//|          down to its nearest valid transaction step increment.   |
//+------------------------------------------------------------------+
double RoundLotToStep(double lot, double lot_step)
  {
   if(lot_step <= 0.0)
      return(lot);

   return(NormalizeDouble(MathFloor(lot / lot_step) * lot_step, 2));
  }

#endif // MATHUTILITIES_MQH
//+------------------------------------------------------------------+