//+------------------------------------------------------------------
//| STEP ATR MA + DEMA
//+------------------------------------------------------------------
#property indicator_chart_window
#property indicator_buffers 3
#property indicator_color1 clrNONE
#property indicator_color2 clrLime
#property indicator_width2 2
#property indicator_color3 clrRed
#property indicator_width3 2

//--- MA Types Enum
enum ENUM_MA_TYPE
{
   MA_TYPE_SMA,    // Simple Moving Average
   MA_TYPE_EMA,    // Exponential Moving Average
   MA_TYPE_SMMA,   // Smoothed Moving Average
   MA_TYPE_LWMA,   // Linear Weighted Moving Average
   MA_TYPE_DEMA    // Double Exponential Moving Average
};

//--- Price Types Enum
enum ENUM_PRICE_TYPE
{
   PRICE_TYPE_CLOSE,     // Close price
   PRICE_TYPE_OPEN,      // Open price
   PRICE_TYPE_HIGH,      // High price
   PRICE_TYPE_LOW,       // Low price
   PRICE_TYPE_MEDIAN,    // Median price
   PRICE_TYPE_TYPICAL,   // Typical price
   PRICE_TYPE_WEIGHTED   // Weighted price
};

//--- Input Parameters
input int             inpMaPeriod = 14;
input ENUM_MA_TYPE    inpMaType = MA_TYPE_DEMA;
input ENUM_PRICE_TYPE inpPrice = PRICE_TYPE_CLOSE;
input int             inpAtrPeriod = 5;
input double          inpStepSize = 100;

//--- Buffers
double val[], val_up[], val_dn[], average[], atr[];
double _stepSize;
int _maPeriod, _atrPeriod;

// For DEMA calculation
double lastEMA, lastEMA_of_EMA;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
{
   // Initialize parameters
   _stepSize = inpStepSize/100.0;
   _maPeriod = MathMax(inpMaPeriod,1);
   _atrPeriod = (inpAtrPeriod>1 ? inpAtrPeriod : _maPeriod);
   
   // Initialize DEMA variables
   lastEMA = 0;
   lastEMA_of_EMA = 0;

   // Initialize buffers
   IndicatorBuffers(5);
   SetIndexBuffer(0, val);
   SetIndexStyle(0, DRAW_NONE);
   
   SetIndexBuffer(1, val_up);
   SetIndexStyle(1, DRAW_LINE, EMPTY);
   SetIndexLabel(1, "Step Average Up");
   
   SetIndexBuffer(2, val_dn);
   SetIndexStyle(2, DRAW_LINE, EMPTY);
   SetIndexLabel(2, "Step Average Down");
   
   SetIndexBuffer(3, average);
   SetIndexStyle(3, DRAW_NONE);
   SetIndexBuffer(4, atr);
   SetIndexStyle(4, DRAW_NONE);

   SetIndexEmptyValue(0, EMPTY_VALUE);
   SetIndexEmptyValue(1, EMPTY_VALUE);
   SetIndexEmptyValue(2, EMPTY_VALUE);
   
   IndicatorShortName("Step average ("+_maPeriod+","+inpStepSize+")");
   return(0);
}

//+------------------------------------------------------------------+
//| Calculate DEMA                                                   |
//+------------------------------------------------------------------+
double CalculateDEMA(int period, int shift)
{
   double weight = 2.0/(1.0+period);
   double price;
   
   // Get price based on selected price type
   switch(inpPrice)
   {
      case PRICE_TYPE_OPEN:    price = Open[shift]; break;
      case PRICE_TYPE_HIGH:    price = High[shift]; break;
      case PRICE_TYPE_LOW:     price = Low[shift]; break;
      case PRICE_TYPE_MEDIAN:  price = (High[shift]+Low[shift])/2; break;
      case PRICE_TYPE_TYPICAL: price = (High[shift]+Low[shift]+Close[shift])/3; break;
      case PRICE_TYPE_WEIGHTED:price = (High[shift]+Low[shift]+Close[shift]+Close[shift])/4; break;
      default:                price = Close[shift]; // PRICE_TYPE_CLOSE
   }
   
   if(shift == Bars-1)
   {
      lastEMA = price;
      lastEMA_of_EMA = price;
      return price;
   }
   
   lastEMA = weight * price + (1.0-weight) * lastEMA;
   lastEMA_of_EMA = weight * lastEMA + (1.0-weight) * lastEMA_of_EMA;
   
   return 2.0 * lastEMA - lastEMA_of_EMA;
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   int rates_total = Bars;
   int prev_calculated = IndicatorCounted();
   if(prev_calculated < 0) return(-1);
   
   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0) limit++;
   
   // Convert enums to MQL4 constants (except for DEMA)
   int maType;
   switch(inpMaType)
   {
      case MA_TYPE_SMA:  maType = MODE_SMA; break;
      case MA_TYPE_EMA:  maType = MODE_EMA; break;
      case MA_TYPE_SMMA: maType = MODE_SMMA; break;
      case MA_TYPE_LWMA: maType = MODE_LWMA; break;
      case MA_TYPE_DEMA: maType = -1; break; // Special case for DEMA
   }
   
   int priceType;
   switch(inpPrice)
   {
      case PRICE_TYPE_CLOSE:   priceType = PRICE_CLOSE; break;
      case PRICE_TYPE_OPEN:    priceType = PRICE_OPEN; break;
      case PRICE_TYPE_HIGH:    priceType = PRICE_HIGH; break;
      case PRICE_TYPE_LOW:     priceType = PRICE_LOW; break;
      case PRICE_TYPE_MEDIAN:  priceType = PRICE_MEDIAN; break;
      case PRICE_TYPE_TYPICAL: priceType = PRICE_TYPICAL; break;
      case PRICE_TYPE_WEIGHTED:priceType = PRICE_WEIGHTED; break;
   }

   int current_direction = 0;
   int prev_direction = 0;
   
   for(int i = limit-1; i >= 0; i--)
   {
      // Calculate MA
      if(inpMaType == MA_TYPE_DEMA)
      {
         average[i] = CalculateDEMA(_maPeriod, i);
      }
      else
      {
         average[i] = iMA(NULL, 0, _maPeriod, 0, maType, priceType, i);
      }
      
      // Calculate ATR
      atr[i] = iATR(NULL, 0, _atrPeriod, i);
      
      if(average[i] == EMPTY_VALUE) average[i] = Close[i];
      if(atr[i] == EMPTY_VALUE) atr[i] = High[i] - Low[i];
      
      val[i] = iStepVal(average[i], _stepSize*atr[i], rates_total - i - 1);
      
      if(i < rates_total-1)
      {
         if(val[i] > val[i+1]) current_direction = 1;
         else if(val[i] < val[i+1]) current_direction = -1;
         else current_direction = prev_direction;
      }
      else current_direction = 0;
      
      if(current_direction == 1)
      {
         val_up[i] = val[i];
         val_dn[i] = EMPTY_VALUE;
         if(prev_direction == -1 && i < rates_total-1) val_up[i+1] = val[i+1];
      }
      else if(current_direction == -1)
      {
         val_up[i] = EMPTY_VALUE;
         val_dn[i] = val[i];
         if(prev_direction == 1 && i < rates_total-1) val_dn[i+1] = val[i+1];
      }
      else
      {
         if(prev_direction == 1) { val_up[i] = val[i]; val_dn[i] = EMPTY_VALUE; }
         else if(prev_direction == -1) { val_up[i] = EMPTY_VALUE; val_dn[i] = val[i]; }
         else { val_up[i] = EMPTY_VALUE; val_dn[i] = EMPTY_VALUE; }
      }
      
      prev_direction = current_direction;
   }
   return(rates_total);
}

//+------------------------------------------------------------------+
//| Step calculation function                                        |
//+------------------------------------------------------------------+
#define _stepValInstances 1
#define _stepValInstancesSize 1
#define _stepValRingSize 300
double _stepValWork[_stepValRingSize][_stepValInstances*_stepValInstancesSize];

double iStepVal(double value, double stepSize, int i, int instance=0)
{
   int _indC = (i)%_stepValRingSize;
   int _inst = instance*_stepValInstancesSize;
   #define _steps _inst

   if(i > 0 && stepSize > 0)
   {
      int _indP = (i-1)%_stepValRingSize;
      double _diff = value-_stepValWork[_indP][_steps];
      _stepValWork[_indC][_steps] = _stepValWork[_indP][_steps]+((_diff<stepSize && _diff>-stepSize) ? 0 : (int)(_diff/stepSize)*stepSize); 
   }
   else _stepValWork[_indC][_steps] = (stepSize>0) ? MathRound(value/stepSize)*stepSize : value; 
   
   return(_stepValWork[_indC][_steps]);
   #undef _steps
}