/*
   Dynamic Bias MA v2.1 - MT4 Version
   Converted from MQL5
   Original: IonOne 2026
*/

#property copyright "IonOne 2026"
#property description "forex-station.com"
#property version   "2.20"

#property indicator_chart_window
#property indicator_buffers 1
#property indicator_color1   Yellow
#property indicator_width1   2

//=========================================================
// ENUMS
//=========================================================
enum ENUM_PRICE_SOURCE
{
   SRC_CLOSE=0,
   SRC_TYPICAL,
   SRC_MEDIAN,
   SRC_WEIGHTED
};

enum ENUM_BIAS_SOURCE
{
   BIAS_CURRENT=0,
   BIAS_AVERAGE_WINDOW,
   BIAS_EMA_WINDOW
};

enum ENUM_bias_FUNCTION
{
   bias_LINEAR=0,
   bias_SQUARED,
   bias_TANH
};

enum ENUM_MA_TYPE
{
   MA_EMA = 0,
   MA_SMA = 1
};

enum ENUM_ADAPT_MODE
{
   ADAPT_OFF = 0,
   ADAPT_RUBBER = 1,
   ADAPT_ALPHA = 2
};

enum ENUM_LIMIT_MODE
{
   LIMIT_FIXED = 0,
   LIMIT_ATR = 1
};

enum ENUM_BIAS_MODE
{
   BIAS_OFF = 0,
   BIAS_RESIDUAL = 1,
   BIAS_SLOPE = 2
};

//=========================================================
// INPUTS
//=========================================================

input ENUM_MA_TYPE      InpMAType          = MA_EMA;
input int               MAPeriod           = 200;

input ENUM_ADAPT_MODE   AdaptMode          = ADAPT_RUBBER;

input ENUM_LIMIT_MODE   LimitMode          = LIMIT_ATR;

input int               ATRPeriod          = 14;
input double            ATRMultiplier      = 5.0;

input double            FixedDistancePts   = 500;

input double            AdaptStrength      = 0.80;

input double            MaxAlpha           = 1.0;

//---------------------------------------------------------
// Bias
//---------------------------------------------------------
input ENUM_BIAS_MODE    BiasMode           = BIAS_SLOPE;
input int               BiasPeriod         = 2;
input double            BiasStrength       = 1.00;
input double            MaxbiasATR        = 1.00;
input ENUM_PRICE_SOURCE BiasPriceSource    = SRC_CLOSE;
input ENUM_BIAS_SOURCE  BiasSource         = BIAS_CURRENT;
input ENUM_bias_FUNCTION biasFunction    = bias_LINEAR;
input bool              SmoothTrendbias   = true;
input bool              UseAdaptivebiasLimit = true;
input int               ResidualAveragePeriod = 10;

int                     DrawBegin          = 50;

//=========================================================
// BUFFERS
//=========================================================
double OutBuffer[];
double BaseMA[];
double ATRBuffer[];
double BiasEMA[];
double TrendState[];
double ResidualBuffer[];

//=========================================================
// GLOBAL VARIABLES
//=========================================================
double Alpha;
double BiasAlpha;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   IndicatorBuffers(6);
   
   SetIndexBuffer(0, OutBuffer);
   SetIndexStyle(0, DRAW_LINE);
   SetIndexLabel(0, "Adaptive MA");
   
   SetIndexBuffer(1, BaseMA);
   SetIndexBuffer(2, ATRBuffer);
   SetIndexBuffer(3, BiasEMA);
   SetIndexBuffer(4, TrendState);
   SetIndexBuffer(5, ResidualBuffer);
   
   IndicatorShortName("Kuku MA v2.1");
   
   Alpha = 2.0 / (MAPeriod + 1.0);
   BiasAlpha = 2.0 / (BiasPeriod + 1.0);
   
   SetIndexDrawBegin(0, DrawBegin);
   
   return(0);
}

//+------------------------------------------------------------------+
//| Clamp function                                                   |
//+------------------------------------------------------------------+
double Clamp(double value, double low, double high)
{
   if(value < low)  return low;
   if(value > high) return high;
   return value;
}

//+------------------------------------------------------------------+
//| SMA                                                              |
//+------------------------------------------------------------------+
double CalcSMA(int bar, int period)
{
   if(bar < period-1) return Close[bar];
   
   double sum = 0.0;
   for(int i = 0; i < period; i++)
      sum += Close[bar + i];
   
   return sum / period;
}

//+------------------------------------------------------------------+
//| EMA                                                              |
//+------------------------------------------------------------------+
double CalcEMA(double previous, double price)
{
   return previous + Alpha * (price - previous);
}

//+------------------------------------------------------------------+
//| MathSign                                                         |
//+------------------------------------------------------------------+
double MathSign(double v)
{
   if(v < 0) return -1;
   if(v > 0) return 1;
   return 0;
}
double MathTanh(double x)
{
   if(x > 10.0)  return(1.0);
   if(x < -10.0) return(-1.0);
   
   double e2x = MathExp(2.0 * x);
   return((e2x - 1.0) / (e2x + 1.0));
}

//
//+------------------------------------------------------------------+
//| Main calculation function                                        |
//+------------------------------------------------------------------+
int start()
{
   int counted_bars = IndicatorCounted();
   int limit = Bars - counted_bars;
   
   if(counted_bars == 0) 
      limit = Bars - MAPeriod - 5;
   
   if(Bars < MAPeriod + 5) 
      return(0);

   for(int i = limit - 1; i >= 0; i--)
   {
      // ATR
      if(i < Bars - 1)
      {
         double tr1 = High[i] - Low[i];
         double tr2 = MathAbs(High[i] - Close[i+1]);
         double tr3 = MathAbs(Low[i] - Close[i+1]);
         double tr = MathMax(tr1, MathMax(tr2, tr3));
         
         ATRBuffer[i] = ATRBuffer[i+1] + (tr - ATRBuffer[i+1]) / ATRPeriod;
      }
      else
         ATRBuffer[i] = High[i] - Low[i];

      // Base MA
      if(i == Bars - 1)
         BaseMA[i] = Close[i];
      else
      {
         if(InpMAType == MA_EMA)
            BaseMA[i] = CalcEMA(BaseMA[i+1], Close[i]);
         else
            BaseMA[i] = CalcSMA(i, MAPeriod);
      }

      // Distance limit
      double limit_dist = (LimitMode == LIMIT_ATR) ? 
                         ATRBuffer[i] * ATRMultiplier : 
                         FixedDistancePts * Point;

      double adaptiveMA = BaseMA[i];
      double dist = Close[i] - adaptiveMA;

      // Rubber Band Adaptation
      if(AdaptMode == ADAPT_RUBBER)
      {
         if(MathAbs(dist) > limit_dist)
         {
            double excess = MathAbs(dist) - limit_dist;
            double correction = excess * AdaptStrength;
            adaptiveMA += (dist > 0) ? correction : -correction;
         }
      }
      // Adaptive Alpha
      else if(AdaptMode == ADAPT_ALPHA)
      {
         if(i < Bars - 1)
         {
            double alpha = Alpha;
            if(MathAbs(dist) > limit_dist)
            {
               double gain = 1.0 + AdaptStrength * (MathAbs(dist) - limit_dist) / 
                            MathMax(limit_dist, Point);
               alpha = Clamp(alpha * gain, Alpha, MaxAlpha);
            }
            adaptiveMA = OutBuffer[i+1] + alpha * (Close[i] - OutBuffer[i+1]);
         }
      }

      BaseMA[i] = adaptiveMA;

      // Bias Calculation
      double bias = 0.0;

      if(BiasMode == BIAS_RESIDUAL)
      {
         double sourcePrice = 0.0;
         switch(BiasPriceSource)
         {
            case SRC_CLOSE:    sourcePrice = Close[i]; break;
            case SRC_TYPICAL:  sourcePrice = (High[i]+Low[i]+Close[i])/3.0; break;
            case SRC_MEDIAN:   sourcePrice = (High[i]+Low[i])/2.0; break;
            case SRC_WEIGHTED: sourcePrice = (High[i]+Low[i]+2*Close[i])/4.0; break;
         }

         double residual = 0.0;

         if(BiasSource == BIAS_CURRENT)
         {
            residual = sourcePrice - adaptiveMA;
         }
         else
         {
            int count = MathMin(ResidualAveragePeriod, Bars - i);
            for(int j = 0; j < count; j++)
            {
               double p = 0.0;
               int idx = i + j;
               switch(BiasPriceSource)
               {
                  case SRC_CLOSE:    p = Close[idx]; break;
                  case SRC_TYPICAL:  p = (High[idx]+Low[idx]+Close[idx])/3.0; break;
                  case SRC_MEDIAN:   p = (High[idx]+Low[idx])/2.0; break;
                  case SRC_WEIGHTED: p = (High[idx]+Low[idx]+2*Close[idx])/4.0; break;
               }
               residual += p - BaseMA[idx];
            }
            residual /= count;
         }

         if(BiasSource == BIAS_EMA_WINDOW)
         {
            if(i == Bars - 1)
               BiasEMA[i] = residual;
            else
               BiasEMA[i] = BiasEMA[i+1] + BiasAlpha * (residual - BiasEMA[i+1]);
            residual = BiasEMA[i];
         }

         bias = residual * BiasStrength;

         // bias Function
         if(biasFunction == bias_SQUARED)
         {
            bias = MathSign(bias) * bias * MathAbs(bias) / (ATRBuffer[i] + Point);
         }
         else if(biasFunction == bias_TANH)
         {
            bias = ATRBuffer[i] * MathTanh(bias / (ATRBuffer[i] + Point));
         }
      }
      else if(BiasMode == BIAS_SLOPE)
      {
         double slope = (i < Bars - 1) ? (adaptiveMA - BaseMA[i+1]) : 0.0;
         double norm = (ATRBuffer[i] > Point) ? ATRBuffer[i] : Point;
         double trend = slope / norm;
         trend = trend / (1.0 + MathAbs(trend));

         if(i == Bars - 1)
            TrendState[i] = trend;
         else
            TrendState[i] = TrendState[i+1] + BiasAlpha * (trend - TrendState[i+1]);

         bias = TrendState[i] * ATRBuffer[i] * BiasStrength;
      }

      // bias Limiter
      double maxbias = ATRBuffer[i] * MaxbiasATR;
      if(UseAdaptivebiasLimit)
         maxbias *= (0.5 + MathAbs(TrendState[i]));

      bias = Clamp(bias, -maxbias, maxbias);

      // Final Output
      OutBuffer[i] = adaptiveMA + bias;
   }

   return(0);
}