//+------------------------------------------------------------------+
//|                                          FXP_KamaTrendFilter.mq5 |
//|                                                     FXPIPS ONLINE |
//+------------------------------------------------------------------+
#property copyright "FXPIPS ONLINE"
#property link      "https://fxpips.online/mt5-indicators/kama-trend-filter/"
#property version   "1.00"
#property description "Kaufman adaptive moving average coloured by trend state."
#property description "Efficiency ratio separates trending from ranging conditions."
/*
   License: MIT. Copyright (c) 2026 FXPIPS ONLINE.
   Permission is hereby granted, free of charge, to any person obtaining a copy of this
   software, to use, copy, modify, merge, publish and distribute it, provided this notice
   is included. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.

   Educational tool - not financial advice. Trading involves substantial risk of loss.
*/
#property indicator_chart_window
#property indicator_buffers 6
#property indicator_plots   3

#property indicator_label1  "KAMA"
#property indicator_type1   DRAW_COLOR_LINE
#property indicator_color1  clrLimeGreen,clrTomato,clrDarkGray
#property indicator_width1  3

#property indicator_label2  "Uptrend start"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrLimeGreen
#property indicator_width2  2

#property indicator_label3  "Downtrend start"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrTomato
#property indicator_width3  2

input group "Kaufman adaptive MA"
input int    InpErLen      = 10;    // Efficiency ratio length
input int    InpFastLen    = 2;     // Fast smoothing length
input int    InpSlowLen    = 30;    // Slow smoothing length
input group "Trend filter"
input double InpErThresh   = 0.30;  // Minimum efficiency ratio to call a trend
input int    InpSlopeBars  = 3;     // Bars used to measure KAMA slope
input group "Alerts"
input bool   InpAlerts     = false; // Pop-up alert on new trend state
input bool   InpPush       = false; // Push notification on new trend state

double g_kama[], g_clr[], g_buy[], g_sell[], g_er[], g_state[];
int g_erLen, g_slope;
double g_fastSc, g_slowSc;
datetime g_lastAlert = 0;

//+------------------------------------------------------------------+
int OnInit()
{
   g_erLen  = MathMax(1, InpErLen);
   g_slope  = MathMax(1, InpSlopeBars);
   g_fastSc = 2.0 / (MathMax(1, InpFastLen) + 1.0);
   g_slowSc = 2.0 / (MathMax(2, InpSlowLen) + 1.0);

   SetIndexBuffer(0, g_kama,  INDICATOR_DATA);
   SetIndexBuffer(1, g_clr,   INDICATOR_COLOR_INDEX);
   SetIndexBuffer(2, g_buy,   INDICATOR_DATA);
   SetIndexBuffer(3, g_sell,  INDICATOR_DATA);
   SetIndexBuffer(4, g_er,    INDICATOR_CALCULATIONS);
   SetIndexBuffer(5, g_state, INDICATOR_CALCULATIONS);

   ArraySetAsSeries(g_kama, false); ArraySetAsSeries(g_clr, false);
   ArraySetAsSeries(g_buy, false);  ArraySetAsSeries(g_sell, false);
   ArraySetAsSeries(g_er, false);   ArraySetAsSeries(g_state, false);

   for(int p = 0; p < 3; p++)
      PlotIndexSetDouble(p, PLOT_EMPTY_VALUE, EMPTY_VALUE);
   PlotIndexSetInteger(0, PLOT_DRAW_BEGIN, g_erLen);
   PlotIndexSetInteger(1, PLOT_ARROW, 233);
   PlotIndexSetInteger(2, PLOT_ARROW, 234);

   IndicatorSetString(INDICATOR_SHORTNAME,
      StringFormat("FXP KAMA Trend Filter (%d, %d, %d)", g_erLen, InpFastLen, InpSlowLen));
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   return(INIT_SUCCEEDED);
}

void Notify(const datetime barTime, const string msg)
{
   if(barTime == g_lastAlert) return;
   g_lastAlert = barTime;
   string text = StringFormat("%s %s: %s", _Symbol, EnumToString((ENUM_TIMEFRAMES)_Period), msg);
   if(InpAlerts) Alert(text);
   if(InpPush)   SendNotification(text);
}

//+------------------------------------------------------------------+
int OnCalculate(const int rates_total,
                const int prev_calculated,
                const datetime &time[],
                const double &open[],
                const double &high[],
                const double &low[],
                const double &close[],
                const long &tick_volume[],
                const long &volume[],
                const int &spread[])
{
   if(rates_total <= g_erLen + g_slope + 2) return(0);

   int start = (prev_calculated > 0) ? prev_calculated - 1 : 0;

   for(int i = start; i < rates_total; i++)
   {
      g_buy[i] = EMPTY_VALUE; g_sell[i] = EMPTY_VALUE;

      if(i < g_erLen)
      {
         g_kama[i] = (i == g_erLen - 1) ? close[i] : EMPTY_VALUE;
         g_clr[i] = 2; g_er[i] = 0.0; g_state[i] = 0.0;
         continue;
      }

      // Efficiency ratio = net change / sum of absolute bar-to-bar changes
      double change = MathAbs(close[i] - close[i - g_erLen]);
      double noise = 0.0;
      for(int k = 0; k < g_erLen; k++) noise += MathAbs(close[i - k] - close[i - k - 1]);
      double er = (noise > 0.0) ? change / noise : 0.0;
      g_er[i] = er;

      double sc = er * (g_fastSc - g_slowSc) + g_slowSc;
      sc *= sc;
      g_kama[i] = g_kama[i - 1] + sc * (close[i] - g_kama[i - 1]);

      // Trend state: +1 up, -1 down, 0 range
      double state = 0.0;
      if(i >= g_erLen + g_slope && er >= InpErThresh)
      {
         double slope = g_kama[i] - g_kama[i - g_slope];
         if(slope > 0.0)      state = 1.0;
         else if(slope < 0.0) state = -1.0;
      }
      g_state[i] = state;
      g_clr[i] = (state > 0.0) ? 0 : (state < 0.0 ? 1 : 2);

      if(i < rates_total - 1 && state != g_state[i - 1])
      {
         double off = (high[i] - low[i]) * 0.5;
         if(state > 0.0) g_buy[i]  = low[i] - off;
         if(state < 0.0) g_sell[i] = high[i] + off;
      }
   }

   if(prev_calculated > 0 && (InpAlerts || InpPush))
   {
      int b = rates_total - 2;
      if(g_buy[b] != EMPTY_VALUE)  Notify(time[b], "KAMA trend filter turned bullish");
      if(g_sell[b] != EMPTY_VALUE) Notify(time[b], "KAMA trend filter turned bearish");
   }
   return(rates_total);
}

void OnDeinit(const int reason)
{
   // No handles or chart objects are created by this indicator.
}
//+------------------------------------------------------------------+
