//+------------------------------------------------------------------+
//|                                            AlphaBetaFilter.mqh   |
//| Include para uso do Alpha-Beta Filter MTF em outros indicadores  |
//| ou Expert Advisors. Encapsula o cálculo em uma classe reutilizável|
//+------------------------------------------------------------------+
#ifndef __ALPHABETAFILTER_MQH__
#define __ALPHABETAFILTER_MQH__

//+------------------------------------------------------------------+
//| Enumeração de slope para consumo do sinal                        |
//+------------------------------------------------------------------+
enum ENUM_AB_SLOPE
{
   AB_SLOPE_NONE = 0,   // Sem inclinação definida (barras insuficientes)
   AB_SLOPE_UP   = 1,   // Inclinação para cima (compra)
   AB_SLOPE_DOWN = -1   // Inclinação para baixo (venda)
};

//+------------------------------------------------------------------+
//| Classe CAlphaBetaFilter                                          |
//| Encapsula o cálculo do filtro Alpha-Beta com suporte a MTF.      |
//+------------------------------------------------------------------+
class CAlphaBetaFilter
{
private:
   //--- Parâmetros
   double            m_alpha;
   double            m_beta;
   ENUM_TIMEFRAMES   m_timeframe;
   int               m_slope_period;
   string            m_symbol;

   //--- Estado interno para cálculo no TF atual (opcional, para uso incremental)
   double            m_state_ab;
   double            m_state_velocity;
   bool              m_state_initialized;

   //--- Helper: timeframe efetivo
   int               EffectiveTimeFrame() const
   {
      if(m_timeframe <= 0 || m_timeframe == PERIOD_CURRENT)
         return((int)Period());
      return((int)m_timeframe);
   }

public:
   //--- Construtor
                     CAlphaBetaFilter(void) :
                        m_alpha(0.2),
                        m_beta(0.1),
                        m_timeframe(PERIOD_CURRENT),
                        m_slope_period(1),
                        m_symbol(""),
                        m_state_ab(0.0),
                        m_state_velocity(0.0),
                        m_state_initialized(false)
                     {}

   //--- Setup
   void              Init(const double alpha,
                          const double beta,
                          const ENUM_TIMEFRAMES tf = PERIOD_CURRENT,
                          const int slope_period = 1,
                          const string symbol = NULL)
   {
      m_alpha = alpha;
      m_beta  = beta;
      m_timeframe = tf;
      m_slope_period = (slope_period < 1 ? 1 : slope_period);
      m_symbol = (symbol == NULL ? _Symbol : symbol);
      m_state_initialized = false;
   }

   //--- Getters dos parâmetros
   double            Alpha()        const { return(m_alpha); }
   double            Beta()         const { return(m_beta); }
   int               SlopePeriod()  const { return(m_slope_period); }
   ENUM_TIMEFRAMES   TimeFrame()    const { return(m_timeframe); }

   //+---------------------------------------------------------------+
   //| Calcula o filtro Alpha-Beta sobre um array de preços.         |
   //| price[] deve estar como série (índice 0 = mais recente).      |
   //| ab_out[] recebe o valor do filtro em cada barra.              |
   //| Use quando TimeFrame == atual ou para qualquer série custom.  |
   //+---------------------------------------------------------------+
   void              CalculateOnArray(const int bars,
                                      const double &price[],
                                      double &ab_out[])
   {
      if(bars <= 0) return;
      if(ArraySize(ab_out) < bars) ArrayResize(ab_out, bars);

      double ab = 0.0;
      double velocity = 0.0;

      for(int i = bars - 1; i >= 0; i--)
      {
         if(i == bars - 1)
         {
            ab = price[i];
            velocity = 0.0;
         }
         else
         {
            ab = ab + velocity;
            ab = ab + m_alpha * (price[i] - ab);
            velocity = velocity + m_beta * (price[i] - ab);
         }
         ab_out[i] = ab;
      }
   }

   //+---------------------------------------------------------------+
   //| Versão MTF com interpolação linear no tempo.                  |
   //| Mapeia o filtro calculado no TF superior para o gráfico atual.|
   //| time[] é o array de tempos do gráfico atual (série).          |
   //+---------------------------------------------------------------+
   void              CalculateMTF(const int rates_total,
                                  const datetime &time[],
                                  double &ab_out[])
   {
      if(rates_total <= 0) return;
      if(ArraySize(ab_out) < rates_total) ArrayResize(ab_out, rates_total);

      int tf = EffectiveTimeFrame();
      int tf_bars = iBars(m_symbol, (ENUM_TIMEFRAMES)tf);
      if(tf_bars <= 1) return;

      //--- Calcula filtro no TF superior
      double htf_ab[];
      ArrayResize(htf_ab, tf_bars);

      double ab = 0.0;
      double velocity = 0.0;
      for(int h = tf_bars - 1; h >= 0; h--)
      {
         double price = iClose(m_symbol, (ENUM_TIMEFRAMES)tf, h);
         if(h == tf_bars - 1)
         {
            ab = price;
            velocity = 0.0;
         }
         else
         {
            ab = ab + velocity;
            ab = ab + m_alpha * (price - ab);
            velocity = velocity + m_beta * (price - ab);
         }
         htf_ab[h] = ab;
      }

      //--- Mapeia para o gráfico atual com interpolação linear
      for(int i = rates_total - 1; i >= 0; i--)
      {
         datetime t = time[i];
         int idx1 = iBarShift(m_symbol, (ENUM_TIMEFRAMES)tf, t, true);
         int idx2 = idx1 - 1;
         if(idx1 < 0)
         {
            ab_out[i] = EMPTY_VALUE;
            continue;
         }

         double val = htf_ab[idx1];
         if(idx2 >= 0)
         {
            datetime t1 = iTime(m_symbol, (ENUM_TIMEFRAMES)tf, idx1);
            datetime t2 = iTime(m_symbol, (ENUM_TIMEFRAMES)tf, idx2);
            double   v2 = htf_ab[idx2];
            if(t1 != t2)
               val = v2 + (val - v2) * (double)(t - t2) / (double)(t1 - t2);
         }
         ab_out[i] = val;
      }
   }

   //+---------------------------------------------------------------+
   //| Wrapper inteligente: escolhe entre cálculo direto ou MTF.     |
   //| Use esta função se o indicador consumidor já tem time[] e     |
   //| close[] do OnCalculate.                                       |
   //+---------------------------------------------------------------+
   void              Calculate(const int rates_total,
                               const datetime &time[],
                               const double &close[],
                               double &ab_out[])
   {
      int tf = EffectiveTimeFrame();
      if(tf == (int)Period() && (m_symbol == _Symbol || m_symbol == ""))
         CalculateOnArray(rates_total, close, ab_out);
      else
         CalculateMTF(rates_total, time, ab_out);
   }

   //+---------------------------------------------------------------+
   //| Preenche buffers de slope (Up/Down) a partir do AB já          |
   //| calculado. Use após Calculate() ou CalculateOnArray().         |
   //+---------------------------------------------------------------+
   void              FillSlopeBuffers(const int bars,
                                      const double &ab_in[],
                                      double &up_out[],
                                      double &down_out[])
   {
      if(bars <= 0) return;
      if(ArraySize(up_out)   < bars) ArrayResize(up_out, bars);
      if(ArraySize(down_out) < bars) ArrayResize(down_out, bars);

      for(int i = 0; i < bars; i++)
      {
         if(i + m_slope_period < bars)
         {
            double diff = ab_in[i] - ab_in[i + m_slope_period];
            if(diff >= 0.0)
            {
               up_out[i]   = ab_in[i];
               down_out[i] = EMPTY_VALUE;
            }
            else
            {
               down_out[i] = ab_in[i];
               up_out[i]   = EMPTY_VALUE;
            }
         }
         else
         {
            up_out[i]   = EMPTY_VALUE;
            down_out[i] = EMPTY_VALUE;
         }
      }
   }

   //+---------------------------------------------------------------+
   //| Retorna o slope (direção) de uma barra específica.            |
   //| Útil quando o consumidor só precisa do sinal, não da curva.   |
   //+---------------------------------------------------------------+
   ENUM_AB_SLOPE     GetSlope(const int bar_index,
                              const int bars,
                              const double &ab_in[]) const
   {
      if(bar_index < 0 || bar_index + m_slope_period >= bars)
         return(AB_SLOPE_NONE);

      double diff = ab_in[bar_index] - ab_in[bar_index + m_slope_period];
      if(diff >= 0.0) return(AB_SLOPE_UP);
      return(AB_SLOPE_DOWN);
   }

   //+---------------------------------------------------------------+
   //| Função one-shot: retorna apenas o valor do AB na barra dada.  |
   //| Recalcula do zero — use só quando não precisar do array todo. |
   //| 'bar' = índice no TF efetivo (0 = barra atual).               |
   //+---------------------------------------------------------------+
   double            GetValueAt(const int bar)
   {
      int tf = EffectiveTimeFrame();
      int tf_bars = iBars(m_symbol, (ENUM_TIMEFRAMES)tf);
      if(tf_bars <= 1 || bar < 0 || bar >= tf_bars) return(EMPTY_VALUE);

      double ab = 0.0;
      double velocity = 0.0;
      for(int h = tf_bars - 1; h >= bar; h--)
      {
         double price = iClose(m_symbol, (ENUM_TIMEFRAMES)tf, h);
         if(h == tf_bars - 1)
         {
            ab = price;
            velocity = 0.0;
         }
         else
         {
            ab = ab + velocity;
            ab = ab + m_alpha * (price - ab);
            velocity = velocity + m_beta * (price - ab);
         }
      }
      return(ab);
   }
};

//+------------------------------------------------------------------+
//| Função procedural (alternativa à classe) para uso direto         |
//| sem instanciar objeto. Cálculo no TF atual sobre array de preços.|
//+------------------------------------------------------------------+
void AlphaBetaFilter_Calc(const int bars,
                          const double &price[],
                          double &ab_out[],
                          const double alpha,
                          const double beta)
{
   if(bars <= 0) return;
   if(ArraySize(ab_out) < bars) ArrayResize(ab_out, bars);

   double ab = 0.0;
   double velocity = 0.0;
   for(int i = bars - 1; i >= 0; i--)
   {
      if(i == bars - 1)
      {
         ab = price[i];
         velocity = 0.0;
      }
      else
      {
         ab = ab + velocity;
         ab = ab + alpha * (price[i] - ab);
         velocity = velocity + beta * (price[i] - ab);
      }
      ab_out[i] = ab;
   }
}

#endif // __ALPHABETAFILTER_MQH__
//+------------------------------------------------------------------+
