//+----------------------------------------------------------------------------------+
//|                                                  UniEMA (colored candles) BT.mq5 |
//|                                                      Copyright 2026, Igor Durkin |
//|                                               Converted from TradingView Pine v6 |
//+----------------------------------------------------------------------------------+
#property copyright   "Copyright 2026, Igor Durkin / PineCoders"
#property link        "https://forex-station.com/post1295586863.html#p1295586863"
#property version     "1.00"
#property description "TASC Issue: November 2026"
#property description "Article: The UniEMA - A Universal Exponential Moving Average"
#property description "Article By: Igor Durkin"
#property description "Converted from TradingView's Pine Script v6 to MQL5"
#property description "Enhanced with On/Off Toggle Button & Colored Candlesticks"

//--- Indicator plot properties
#property indicator_chart_window
#property indicator_buffers 7
#property indicator_plots   2

//--- Plot 1: Colored Candlesticks (Plot 0, drawn first so line renders cleanly on top)
#property indicator_label1  "Colored Candles"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrLime, clrRed, clrGray
#property indicator_width1  1

//--- Plot 2: UniEMA Line (Plot 1)
#property indicator_label2  "UniEMA"
#property indicator_type2   DRAW_COLOR_LINE
#property indicator_color2  clrDodgerBlue, clrRed
#property indicator_style2  STYLE_SOLID
#property indicator_width2  2

//+------------------------------------------------------------------+
//| Candlestick Color Mode Enum                                      |
//+------------------------------------------------------------------+
enum ENUM_CANDLE_MODE
{
   CANDLE_ON_SLOPE = 0, // Slope of UniEMA (Rising = Up, Falling = Down)
   CANDLE_ON_PRICE = 1  // Price vs UniEMA (Close > UniEMA = Up, Close < UniEMA = Down)
};

//+------------------------------------------------------------------+
//| Input Parameters                                                 |
//+------------------------------------------------------------------+
input group "=== UniEMA Settings ==="
input ENUM_APPLIED_PRICE InpAppliedPrice        = PRICE_CLOSE;       // Source
input int                InpLength               = 14;                // Length
input int                InpPoles                = 2;                 // Poles (1-EMA, 2-DEMA, 3-TEMA, 4-QEMA)
input int                InpOrder                = 3;                 // Smoothing Order (min. 1)
input double             InpWeightFactor         = 0.5;               // Weight Factor (Wilder=1, EMA=2)
input double             InpDampingFactor        = 0.5;               // Damping Factor (ex. 0.7)

input group "=== Candlestick Colors ==="
input bool               InpColorCandles         = true;              // Color Candlesticks
input ENUM_CANDLE_MODE   InpCandleMode           = CANDLE_ON_SLOPE;   // Candlestick Color Mode
input color              InpCandleColorUp        = clrLime;           // Candlestick Uptrend Color
input color              InpCandleColorDn        = clrRed;            // Candlestick Downtrend Color
input color              InpCandleColorNeutral   = clrGray;           // Candlestick Neutral Color

input group "=== Line Styles & Colors ==="
input bool               InpShow2Col             = true;              // Show Multi-Color Line
input color              InpUpColor              = clrDodgerBlue;     // Color 1 (Uptrend)
input color              InpDownColor            = clrRed;            // Color 2 (Downtrend)
input int                InpLineWidth            = 2;                 // Line Width
input ENUM_LINE_STYLE    InpLineStyle            = STYLE_SOLID;       // Line Style

//Forex-Station copy & paste code; Button code start 11
input group "=== Toggle Button Settings ==="
input string             button_note1          = "------------------------------"; // ------------------------------
input int                btn_Subwindow         = 0;                  // What window to put the button on
input ENUM_BASE_CORNER   btn_corner            = CORNER_LEFT_UPPER;  // chart btn_corner for anchoring
input string             btn_text              = "UniEMA: ON";       // Display label
input string             btn_Font              = "Arial";            // button font name
input int                btn_FontSize          = 9;                  // btn__font size
input color              btn_text_ON_color     = clrLime;            // ON color when the button is turned on
input color              btn_text_OFF_color    = clrRed;             // OFF color when the button is turned off
input color              btn_background_color  = clrDimGray;         // background color of the button
input color              btn_border_color      = clrBlack;           // border color the button
input int                button_x              = 20;                 // Horizontal location
input int                button_y              = 60;                 // Vertical location
input int                btn_Width             = 80;                 // btn__width
input int                btn_Height            = 20;                 // btn__height
input string             UniqueButtonID        = "UniEMA_CC_BT";     // Unique Button ID                      
input string             button_note2          = "------------------------------"; // ------------------------------
//Forex-Station copy & paste code; Button code end 11

//+------------------------------------------------------------------+
//| Indicator Buffers (7 buffers total)                              |
//+------------------------------------------------------------------+
// Buffers 0-4: Colored Candles (Plot 0)
double CandleOpen[];
double CandleHigh[];
double CandleLow[];
double CandleClose[];
double CandleColor[];

// Buffers 5-6: UniEMA Line (Plot 1)
double BufferUniEma[];
double BufferUniEmaColor[];

//+------------------------------------------------------------------+
//| Internal State History Buffer                                    |
//+------------------------------------------------------------------+
double g_u[]; // Flattened array of size [rates_total * (poles * order)]

//+------------------------------------------------------------------+
//| Button State Tracking Variables                                  |
//+------------------------------------------------------------------+
bool    IndicatorEnabled = true;
bool    IndicatorHidden  = false; // True when indicator is turned OFF via toggle button
string  ToggleButtonName;
string  g_gvHidden;
string  g_prefix;
string  IndicatorName, IndicatorObjPrefix, buttonId;

//+------------------------------------------------------------------+
//| Forward Declarations                                             |
//+------------------------------------------------------------------+
string GetButtonText(bool isEnabled);
void   CreateToggleButton();
void   UpdateToggleButton();
void   SetCandlesVisibility(const bool visible);
void   ApplyPlotsStyle();
void   ToggleIndicatorVisibility();
void   BringButtonsToFront();

//+------------------------------------------------------------------+
//| Helper to resolve button text based on state                     |
//+------------------------------------------------------------------+
string GetButtonText(bool isEnabled)
{
   string t = btn_text;
   if(isEnabled)
   {
      if(StringFind(t, "OFF") >= 0) StringReplace(t, "OFF", "ON");
      else if(StringFind(t, "Off") >= 0) StringReplace(t, "Off", "On");
      else if(StringFind(t, "off") >= 0) StringReplace(t, "off", "on");
   }
   else
   {
      if(StringFind(t, "ON") >= 0) StringReplace(t, "ON", "OFF");
      else if(StringFind(t, "On") >= 0) StringReplace(t, "On", "Off");
      else if(StringFind(t, "on") >= 0) StringReplace(t, "on", "off");
   }
   return t;
}

//+------------------------------------------------------------------+
//| Create Toggle Button                                             |
//+------------------------------------------------------------------+
void CreateToggleButton()
{
   if(ObjectFind(0, buttonId) < 0)
   {
      int y_pos = button_y;
      int obj_total = ObjectsTotal(0, -1, OBJ_BUTTON);
      bool collision = true;
      while(collision)
      {
         collision = false;
         for(int i = 0; i < obj_total; i++)
         {
            string name = ObjectName(0, i, -1, OBJ_BUTTON);
            if(name == buttonId) continue;
            if((ENUM_BASE_CORNER)ObjectGetInteger(0, name, OBJPROP_CORNER) != btn_corner) continue;
            if(ObjectGetInteger(0, name, OBJPROP_XDISTANCE) == button_x &&
               ObjectGetInteger(0, name, OBJPROP_YDISTANCE) == y_pos)
            {
               y_pos += btn_Height + 4;
               collision = true;
               break;
            }
         }
      }

      ObjectCreate(0, buttonId, OBJ_BUTTON, btn_Subwindow, 0, 0);

      ObjectSetInteger(0, buttonId, OBJPROP_CORNER,       btn_corner);
      ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE,    button_x);
      ObjectSetInteger(0, buttonId, OBJPROP_YDISTANCE,    y_pos);
      ObjectSetInteger(0, buttonId, OBJPROP_XSIZE,        btn_Width);
      ObjectSetInteger(0, buttonId, OBJPROP_YSIZE,        btn_Height);
      ObjectSetString (0, buttonId, OBJPROP_FONT,         btn_Font);
      ObjectSetInteger(0, buttonId, OBJPROP_FONTSIZE,     btn_FontSize);
      ObjectSetInteger(0, buttonId, OBJPROP_BGCOLOR,      btn_background_color);
      ObjectSetInteger(0, buttonId, OBJPROP_BORDER_COLOR, btn_border_color);
      ObjectSetInteger(0, buttonId, OBJPROP_BORDER_TYPE,  BORDER_RAISED);
      ObjectSetInteger(0, buttonId, OBJPROP_SELECTABLE,   false);
      ObjectSetInteger(0, buttonId, OBJPROP_HIDDEN,       true);
      ObjectSetInteger(0, buttonId, OBJPROP_TIMEFRAMES,   OBJ_ALL_PERIODS);
      ObjectSetInteger(0, buttonId, OBJPROP_ZORDER,       100);
   }

   UpdateToggleButton();
}

//+------------------------------------------------------------------+
//| Update Toggle Button Appearance                                  |
//+------------------------------------------------------------------+
void UpdateToggleButton()
{
   if(ObjectFind(0, buttonId) < 0) return;

   IndicatorEnabled = !IndicatorHidden;

   string labelText  = GetButtonText(IndicatorEnabled);
   color  textColor  = IndicatorEnabled ? btn_text_ON_color : btn_text_OFF_color;

   ObjectSetString (0, buttonId, OBJPROP_TEXT,         labelText);
   ObjectSetString (0, buttonId, OBJPROP_FONT,         btn_Font);
   ObjectSetInteger(0, buttonId, OBJPROP_FONTSIZE,     btn_FontSize);
   ObjectSetInteger(0, buttonId, OBJPROP_COLOR,        textColor);
   ObjectSetInteger(0, buttonId, OBJPROP_BGCOLOR,      btn_background_color);
   ObjectSetInteger(0, buttonId, OBJPROP_BORDER_COLOR, btn_border_color);
   ObjectSetInteger(0, buttonId, OBJPROP_CORNER,       btn_corner);
   ObjectSetInteger(0, buttonId, OBJPROP_XDISTANCE,    button_x);
   ObjectSetInteger(0, buttonId, OBJPROP_XSIZE,        btn_Width);
   ObjectSetInteger(0, buttonId, OBJPROP_YSIZE,        btn_Height);
   ObjectSetInteger(0, buttonId, OBJPROP_STATE,        IndicatorEnabled);
   ObjectSetInteger(0, buttonId, OBJPROP_ZORDER,       100);

   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Set Candlesticks Visibility and Colors                           |
//+------------------------------------------------------------------+
void SetCandlesVisibility(const bool visible)
{
   if(visible)
   {
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, InpCandleColorUp);
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, InpCandleColorDn);
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 2, InpCandleColorNeutral);
      PlotIndexSetInteger(0, PLOT_SHOW_DATA, true);
   }
   else
   {
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, clrNONE);
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, clrNONE);
      PlotIndexSetInteger(0, PLOT_LINE_COLOR, 2, clrNONE);
      PlotIndexSetInteger(0, PLOT_SHOW_DATA, false);
   }
}

//+------------------------------------------------------------------+
//| Apply Plot Styles based on Button Toggle State                   |
//+------------------------------------------------------------------+
void ApplyPlotsStyle()
{
   bool showIndicator = !IndicatorHidden;
   bool showCandles   = showIndicator && InpColorCandles;

   // 1. Plot 0: Colored Candles
   SetCandlesVisibility(showCandles);

   // 2. Plot 1: UniEMA Line
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, showIndicator ? DRAW_COLOR_LINE : DRAW_NONE);
   PlotIndexSetInteger(1, PLOT_COLOR_INDEXES, 2);

   if(showIndicator)
   {
      string short_name = StringFormat("UniEMA(%d, %d, %d, %.2f, %.2f)", 
                                       InpLength, InpPoles, InpOrder, InpWeightFactor, InpDampingFactor);
      PlotIndexSetString (1, PLOT_LABEL, short_name);
      PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, InpUpColor);
      PlotIndexSetInteger(1, PLOT_LINE_COLOR, 1, InpShow2Col ? InpDownColor : InpUpColor);
      PlotIndexSetInteger(1, PLOT_LINE_WIDTH, InpLineWidth);
      PlotIndexSetInteger(1, PLOT_LINE_STYLE, InpLineStyle);
      PlotIndexSetInteger(1, PLOT_SHOW_DATA, true);
   }
   else
   {
      PlotIndexSetString (1, PLOT_LABEL, "");
      PlotIndexSetInteger(1, PLOT_LINE_COLOR, 0, clrNONE);
      PlotIndexSetInteger(1, PLOT_LINE_COLOR, 1, clrNONE);
      PlotIndexSetInteger(1, PLOT_SHOW_DATA, false);
   }
   PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE);
}

//+------------------------------------------------------------------+
//| Toggle Indicator Visibility                                      |
//+------------------------------------------------------------------+
void ToggleIndicatorVisibility()
{
   IndicatorHidden  = !IndicatorHidden;
   IndicatorEnabled = !IndicatorHidden;

   if(g_gvHidden != "")
      GlobalVariableSet(g_gvHidden, IndicatorHidden ? 1.0 : 0.0);

   ApplyPlotsStyle();
   UpdateToggleButton();

   bool showCandles = !IndicatorHidden && InpColorCandles;
   int sz = ArraySize(CandleOpen);
   if(sz > 0)
   {
      if(!showCandles)
      {
         for(int i = 0; i < sz; i++)
         {
            CandleOpen[i]  = EMPTY_VALUE;
            CandleHigh[i]  = EMPTY_VALUE;
            CandleLow[i]   = EMPTY_VALUE;
            CandleClose[i] = EMPTY_VALUE;
         }
      }
      else
      {
         MqlRates rates[];
         ArraySetAsSeries(rates, false);
         int copied = CopyRates(_Symbol, _Period, 0, sz, rates);
         if(copied > 0)
         {
            for(int i = 0; i < copied; i++)
            {
               CandleOpen[i]  = rates[i].open;
               CandleHigh[i]  = rates[i].high;
               CandleLow[i]   = rates[i].low;
               CandleClose[i] = rates[i].close;

               bool isUp = false;
               bool isDn = false;

               if(InpCandleMode == CANDLE_ON_SLOPE)
               {
                  if(i > 0)
                  {
                     if(BufferUniEma[i] > BufferUniEma[i - 1]) isUp = true;
                     else if(BufferUniEma[i] < BufferUniEma[i - 1]) isDn = true;
                  }
               }
               else // CANDLE_ON_PRICE
               {
                  if(rates[i].close > BufferUniEma[i]) isUp = true;
                  else if(rates[i].close < BufferUniEma[i]) isDn = true;
               }

               if(isUp)
                  CandleColor[i] = 0.0;
               else if(isDn)
                  CandleColor[i] = 1.0;
               else
                  CandleColor[i] = 2.0;
            }
         }
      }
   }

   BringButtonsToFront();
   ChartRedraw(0);
}

//+------------------------------------------------------------------+
//| Ensure all buttons on chart have higher ZORDER                   |
//+------------------------------------------------------------------+
void BringButtonsToFront()
{
   int total = ObjectsTotal(0, -1, OBJ_BUTTON);
   for(int i = 0; i < total; i++)
   {
      string name = ObjectName(0, i, -1, OBJ_BUTTON);
      if(name != "")
      {
         if(ObjectGetInteger(0, name, OBJPROP_ZORDER) < 100)
            ObjectSetInteger(0, name, OBJPROP_ZORDER, 100);
      }
   }
}

//+------------------------------------------------------------------+
//| Helper to retrieve price by Applied Price enum                   |
//+------------------------------------------------------------------+
double GetAppliedPrice(const int index,
                       const double &open[],
                       const double &high[],
                       const double &low[],
                       const double &close[])
{
   switch(InpAppliedPrice)
   {
      case PRICE_OPEN:     return open[index];
      case PRICE_HIGH:     return high[index];
      case PRICE_LOW:      return low[index];
      case PRICE_MEDIAN:   return (high[index] + low[index]) * 0.5;
      case PRICE_TYPICAL:  return (high[index] + low[index] + close[index]) / 3.0;
      case PRICE_WEIGHTED: return (high[index] + low[index] + 2.0 * close[index]) * 0.25;
      case PRICE_CLOSE:
      default:             return close[index];
   }
}

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
{
   //--- Validate input parameters
   if(InpLength < 1)
   {
      PrintFormat("UniEMA (colored candles) BT: Length must be >= 1. Received: %d", InpLength);
      return(INIT_PARAMETERS_INCORRECT);
   }
   if(InpPoles < 1 || InpPoles > 4)
   {
      PrintFormat("UniEMA (colored candles) BT: Poles must be between 1 and 4. Received: %d", InpPoles);
      return(INIT_PARAMETERS_INCORRECT);
   }
   if(InpOrder < 1)
   {
      PrintFormat("UniEMA (colored candles) BT: Order must be >= 1. Received: %d", InpOrder);
      return(INIT_PARAMETERS_INCORRECT);
   }
   if(InpWeightFactor <= 0.0)
   {
      PrintFormat("UniEMA (colored candles) BT: Weight Factor must be > 0. Received: %.4f", InpWeightFactor);
      return(INIT_PARAMETERS_INCORRECT);
   }

   //--- Set up unique IDs and global variables for state tracking
   IndicatorObjPrefix = "__" + btn_text + "__";
   buttonId           = IndicatorObjPrefix + UniqueButtonID;
   ToggleButtonName   = buttonId;
   IndicatorName      = "UniEMA (colored candles) BT [" + UniqueButtonID + "]";
   g_prefix           = "UniEMA_CC_BT_" + UniqueButtonID + "_";
   g_gvHidden         = g_prefix + IntegerToString(ChartID()) + "_Hidden";

   // Clean up any old duplicate or phantom buttons
   int total_objs = ObjectsTotal(0, -1, OBJ_BUTTON);
   for(int i = total_objs - 1; i >= 0; i--)
   {
      string bName = ObjectName(0, i, -1, OBJ_BUTTON);
      if(StringFind(bName, UniqueButtonID) >= 0 || StringFind(bName, "UniEMA_CC_BT") >= 0)
      {
         if(bName != buttonId)
            ObjectDelete(0, bName);
      }
   }

   // When parameters are changed by user in properties dialog, delete old global variable and respect inputs strictly
   bool isParamChange = (UninitializeReason() == REASON_PARAMETERS);
   if(isParamChange && g_gvHidden != "")
      GlobalVariableDel(g_gvHidden);

   // Restore toggle state across timeframe/symbol changes
   bool hasSavedState = false;
   bool savedHidden   = false;

   if(!isParamChange)
   {
      if(ObjectFind(0, buttonId) >= 0)
      {
         string btnTxt = ObjectGetString(0, buttonId, OBJPROP_TEXT);
         long   btnSt  = ObjectGetInteger(0, buttonId, OBJPROP_STATE);

         if(StringFind(btnTxt, "OFF") >= 0 || StringFind(btnTxt, "Off") >= 0 || btnSt == 0)
            savedHidden = true;
         else if(StringFind(btnTxt, "ON") >= 0 || StringFind(btnTxt, "On") >= 0 || btnSt != 0)
            savedHidden = false;
         hasSavedState = true;
      }

      if(!hasSavedState && GlobalVariableCheck(g_gvHidden))
      {
         savedHidden   = (GlobalVariableGet(g_gvHidden) > 0.5);
         hasSavedState = true;
      }
   }

   if(hasSavedState)
      IndicatorHidden = savedHidden;
   else
      IndicatorHidden = false;

   IndicatorEnabled = !IndicatorHidden;

   if(g_gvHidden != "")
      GlobalVariableSet(g_gvHidden, IndicatorHidden ? 1.0 : 0.0);

   //--- Bind indicator buffers (7 buffers total)
   // Buffers 0-4: Colored Candles (Plot 0)
   SetIndexBuffer(0, CandleOpen,        INDICATOR_DATA);
   SetIndexBuffer(1, CandleHigh,        INDICATOR_DATA);
   SetIndexBuffer(2, CandleLow,         INDICATOR_DATA);
   SetIndexBuffer(3, CandleClose,       INDICATOR_DATA);
   SetIndexBuffer(4, CandleColor,       INDICATOR_COLOR_INDEX);

   // Buffers 5-6: UniEMA Line (Plot 1)
   SetIndexBuffer(5, BufferUniEma,      INDICATOR_DATA);
   SetIndexBuffer(6, BufferUniEmaColor, INDICATOR_COLOR_INDEX);

   //--- Chronological indexing (index 0 is oldest bar)
   ArraySetAsSeries(CandleOpen,        false);
   ArraySetAsSeries(CandleHigh,        false);
   ArraySetAsSeries(CandleLow,         false);
   ArraySetAsSeries(CandleClose,       false);
   ArraySetAsSeries(CandleColor,       false);
   ArraySetAsSeries(BufferUniEma,      false);
   ArraySetAsSeries(BufferUniEmaColor, false);

   //--- Configure plot styles
   // Plot 0: Colored Candles
   PlotIndexSetString (0, PLOT_LABEL, "Colored Candles");
   PlotIndexSetInteger(0, PLOT_DRAW_TYPE, DRAW_COLOR_CANDLES);
   PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 3);
   PlotIndexSetDouble (0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   // Plot 1: UniEMA Line
   PlotIndexSetString (1, PLOT_LABEL, "UniEMA");
   PlotIndexSetInteger(1, PLOT_DRAW_TYPE, DRAW_COLOR_LINE);
   PlotIndexSetInteger(1, PLOT_COLOR_INDEXES, 2);
   PlotIndexSetDouble (1, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   ApplyPlotsStyle();
   CreateToggleButton();
   BringButtonsToFront();

   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   IndicatorSetString(INDICATOR_SHORTNAME, IndicatorName);

   ArrayFree(g_u);

   return(INIT_SUCCEEDED);
}

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
   if(reason == REASON_REMOVE || reason == REASON_CHARTCLOSE || reason == REASON_PARAMETERS)
   {
      ObjectDelete(0, buttonId);
      ObjectDelete(0, ToggleButtonName);
      if(g_gvHidden != "")
         GlobalVariableDel(g_gvHidden);
   }

   ArrayFree(g_u);
}

//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 < 2)
      return(0);

   if(prev_calculated <= 0)
   {
      CreateToggleButton();
      ApplyPlotsStyle();
   }

   bool showIndicator = !IndicatorHidden;
   bool showCandles   = showIndicator && InpColorCandles;

   int uni_size = InpPoles * InpOrder;
   if(uni_size < 1)
      return(0);

   //--- Ensure internal state storage is adequately sized
   if(ArraySize(g_u) < rates_total * uni_size)
   {
      if(ArrayResize(g_u, rates_total * uni_size, 1000 * uni_size) < 0)
      {
         Print("UniEMA (colored candles) BT: Failed to allocate memory for internal state buffer.");
         return(0);
      }
   }

   //--- Filter coefficient calculation
   double denom = (double)InpLength + InpWeightFactor * (double)InpOrder - 1.0;
   double alpha = (denom > 0.0) ? (InpWeightFactor * (double)InpOrder / denom) : 1.0;
   if(alpha < 0.0) alpha = 0.0;
   if(alpha > 1.0) alpha = 1.0;
   double bet = 1.0 - alpha;

   int start = prev_calculated - 1;
   if(start < 0)
      start = 0;

   for(int i = start; i < rates_total && !_StopFlag; i++)
   {
      double price = GetAppliedPrice(i, open, high, low, close);
      int base_offset = i * uni_size;

      if(i == 0)
      {
         BufferUniEma[0] = price;
         BufferUniEmaColor[0] = 0.0;
         for(int idx = 0; idx < uni_size; idx++)
            g_u[base_offset + idx] = price;
      }
      else if(i == 1)
      {
         BufferUniEma[1] = price;
         BufferUniEmaColor[1] = 0.0;
         for(int idx = 0; idx < uni_size; idx++)
            g_u[base_offset + idx] = price;
      }
      else
      {
         int prev_offset = (i - 1) * uni_size;
         double uniema = price;

         // Cascading stages (_k = 0 to order - 1) and poles (_j = 0 to poles - 1)
         for(int k = 0; k < InpOrder; k++)
         {
            int pole0_idx = InpPoles * k;

            for(int j = 0; j < InpPoles; j++)
            {
               int curr_idx = pole0_idx + j;
               double p_val = g_u[prev_offset + curr_idx];
               double u_val = bet * p_val + alpha * uniema;
               g_u[base_offset + curr_idx] = u_val;

               if(j > 0)
               {
                  double u_pole0 = g_u[base_offset + pole0_idx];
                  uniema = uniema + InpDampingFactor * (u_pole0 - u_val);
               }
               else
               {
                  uniema = u_val;
               }
            }
         }

         BufferUniEma[i] = uniema;

         // Multi-color line determination based on slope
         if(!InpShow2Col)
         {
            BufferUniEmaColor[i] = 0.0;
         }
         else
         {
            if(BufferUniEma[i] > BufferUniEma[i - 1])
               BufferUniEmaColor[i] = 0.0; // Color 1 (Uptrend)
            else if(BufferUniEma[i] < BufferUniEma[i - 1])
               BufferUniEmaColor[i] = 1.0; // Color 2 (Downtrend)
            else
               BufferUniEmaColor[i] = BufferUniEmaColor[i - 1]; // Flat / Unchanged
         }
      }

      //--- Candlestick coloring
      if(showCandles)
      {
         CandleOpen[i]  = open[i];
         CandleHigh[i]  = high[i];
         CandleLow[i]   = low[i];
         CandleClose[i] = close[i];

         bool isUp = false;
         bool isDn = false;

         if(InpCandleMode == CANDLE_ON_SLOPE)
         {
            if(i > 0)
            {
               if(BufferUniEma[i] > BufferUniEma[i - 1]) isUp = true;
               else if(BufferUniEma[i] < BufferUniEma[i - 1]) isDn = true;
            }
         }
         else // CANDLE_ON_PRICE
         {
            if(close[i] > BufferUniEma[i]) isUp = true;
            else if(close[i] < BufferUniEma[i]) isDn = true;
         }

         if(isUp)
            CandleColor[i] = 0.0; // Uptrend (Lime)
         else if(isDn)
            CandleColor[i] = 1.0; // Downtrend (Red)
         else
            CandleColor[i] = 2.0; // Neutral (Gray)
      }
      else
      {
         CandleOpen[i]  = EMPTY_VALUE;
         CandleHigh[i]  = EMPTY_VALUE;
         CandleLow[i]   = EMPTY_VALUE;
         CandleClose[i] = EMPTY_VALUE;
         CandleColor[i] = 2.0;
      }
   }

   return(rates_total);
}

//+------------------------------------------------------------------+
//| ChartEvent function                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id,
                  const long &lparam,
                  const double &dparam,
                  const string &sparam)
{
   if(id == CHARTEVENT_OBJECT_CLICK)
   {
      string altId = "_" + UniqueButtonID + IndicatorObjPrefix + "_BT_";
      if(sparam == buttonId || sparam == ToggleButtonName || sparam == altId)
      {
         ToggleIndicatorVisibility();
         return;
      }
   }

   if(id == CHARTEVENT_CHART_CHANGE)
   {
      BringButtonsToFront();
   }
}
//+------------------------------------------------------------------+
