//+------------------------------------------------------------------+
//|                                       FXP_HeikinAshiSmoothed.mq5 |
//|                                                     FXPIPS ONLINE |
//+------------------------------------------------------------------+
#property copyright "FXPIPS ONLINE"
#property link      "https://fxpips.online/mt5-indicators/heikin-ashi-smoothed/"
#property version   "1.00"
#property description "Smoothed Heikin-Ashi candles drawn on the price chart."
#property description "Prices are smoothed before and after the Heikin-Ashi step."
/*
   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 13
#property indicator_plots   3

#property indicator_label1  "HA Open;HA High;HA Low;HA Close"
#property indicator_type1   DRAW_COLOR_CANDLES
#property indicator_color1  clrDodgerBlue,clrOrangeRed
#property indicator_width1  1

#property indicator_label2  "Turn bullish"
#property indicator_type2   DRAW_ARROW
#property indicator_color2  clrDodgerBlue
#property indicator_width2  2

#property indicator_label3  "Turn bearish"
#property indicator_type3   DRAW_ARROW
#property indicator_color3  clrOrangeRed
#property indicator_width3  2

enum ENUM_FXP_MA
{
   FXP_MA_SMA = 0,   // Simple
   FXP_MA_EMA = 1,   // Exponential
   FXP_MA_SMMA = 2,  // Smoothed (Wilder)
   FXP_MA_LWMA = 3   // Linear weighted
};

input group "Smoothing"
input ENUM_FXP_MA InpPreType   = FXP_MA_EMA; // Pre-smoothing method (on OHLC)
input int         InpPreLen    = 6;          // Pre-smoothing length
input ENUM_FXP_MA InpPostType  = FXP_MA_EMA; // Post-smoothing method (on HA open/close)
input int         InpPostLen   = 3;          // Post-smoothing length (1 = off)
input group "Signals"
input bool        InpArrows    = true;       // Arrows on colour changes
input group "Alerts"
input bool        InpAlerts    = false;      // Pop-up alert on new colour change
input bool        InpPush      = false;      // Push notification on new colour change

double g_o[], g_h[], g_l[], g_c[], g_clr[], g_buy[], g_sell[];
double g_so[], g_sh[], g_sl[], g_sc[], g_hao[], g_hac[];
int g_pre, g_post, g_begin;
datetime g_lastAlert = 0;

//+------------------------------------------------------------------+
int OnInit()
{
   g_pre  = MathMax(1, InpPreLen);
   g_post = MathMax(1, InpPostLen);
   g_begin = g_pre + g_post + 1;

   SetIndexBuffer(0,  g_o,    INDICATOR_DATA);
   SetIndexBuffer(1,  g_h,    INDICATOR_DATA);
   SetIndexBuffer(2,  g_l,    INDICATOR_DATA);
   SetIndexBuffer(3,  g_c,    INDICATOR_DATA);
   SetIndexBuffer(4,  g_clr,  INDICATOR_COLOR_INDEX);
   SetIndexBuffer(5,  g_buy,  INDICATOR_DATA);
   SetIndexBuffer(6,  g_sell, INDICATOR_DATA);
   SetIndexBuffer(7,  g_so,   INDICATOR_CALCULATIONS);
   SetIndexBuffer(8,  g_sh,   INDICATOR_CALCULATIONS);
   SetIndexBuffer(9,  g_sl,   INDICATOR_CALCULATIONS);
   SetIndexBuffer(10, g_sc,   INDICATOR_CALCULATIONS);
   SetIndexBuffer(11, g_hao,  INDICATOR_CALCULATIONS);
   SetIndexBuffer(12, g_hac,  INDICATOR_CALCULATIONS);

   ArraySetAsSeries(g_o, false);   ArraySetAsSeries(g_h, false);
   ArraySetAsSeries(g_l, false);   ArraySetAsSeries(g_c, false);
   ArraySetAsSeries(g_clr, false); ArraySetAsSeries(g_buy, false);
   ArraySetAsSeries(g_sell, false);ArraySetAsSeries(g_so, false);
   ArraySetAsSeries(g_sh, false);  ArraySetAsSeries(g_sl, false);
   ArraySetAsSeries(g_sc, false);  ArraySetAsSeries(g_hao, false);
   ArraySetAsSeries(g_hac, false);

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

   IndicatorSetString(INDICATOR_SHORTNAME,
      StringFormat("FXP Heikin-Ashi Smoothed (%d, %d)", g_pre, g_post));
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits);
   return(INIT_SUCCEEDED);
}

// Moving average of a non-series array at bar i; prev is the previous MA output (for recursive types)
double Ma(const ENUM_FXP_MA type, const double &src[], const int i, const int len, const double prev)
{
   if(len <= 1) return(src[i]);
   switch(type)
   {
      case FXP_MA_EMA:
         if(i < len) return(src[i]);
         return(prev + (2.0 / (len + 1.0)) * (src[i] - prev));
      case FXP_MA_SMMA:
         if(i < len) return(src[i]);
         return((prev * (len - 1) + src[i]) / len);
      case FXP_MA_LWMA:
      {
         int n = MathMin(len, i + 1);
         double num = 0.0, den = 0.0;
         for(int k = 0; k < n; k++) { double w = (double)(n - k); num += src[i - k] * w; den += w; }
         return(num / den);
      }
      default:
      {
         int n = MathMin(len, i + 1);
         double s = 0.0;
         for(int k = 0; k < n; k++) s += src[i - k];
         return(s / n);
      }
   }
}

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_begin + 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;

      // 1) Pre-smooth raw OHLC
      g_so[i] = Ma(InpPreType, open,  i, g_pre, i > 0 ? g_so[i - 1] : open[i]);
      g_sh[i] = Ma(InpPreType, high,  i, g_pre, i > 0 ? g_sh[i - 1] : high[i]);
      g_sl[i] = Ma(InpPreType, low,   i, g_pre, i > 0 ? g_sl[i - 1] : low[i]);
      g_sc[i] = Ma(InpPreType, close, i, g_pre, i > 0 ? g_sc[i - 1] : close[i]);

      // 2) Heikin-Ashi on the smoothed prices
      double haClose = (g_so[i] + g_sh[i] + g_sl[i] + g_sc[i]) / 4.0;
      double haOpen  = (i == 0) ? (g_so[i] + g_sc[i]) / 2.0 : (g_hao[i - 1] + g_hac[i - 1]) / 2.0;
      g_hao[i] = haOpen;
      g_hac[i] = haClose;

      if(i < g_begin)
      {
         g_o[i] = EMPTY_VALUE; g_h[i] = EMPTY_VALUE; g_l[i] = EMPTY_VALUE; g_c[i] = EMPTY_VALUE;
         g_clr[i] = 0;
         continue;
      }

      // 3) Post-smooth HA open and close (seeded from the raw HA values)
      double po = (i == g_begin) ? g_hao[i - 1] : g_o[i - 1];
      double pc = (i == g_begin) ? g_hac[i - 1] : g_c[i - 1];
      double so = Ma(InpPostType, g_hao, i, g_post, po);
      double sc = Ma(InpPostType, g_hac, i, g_post, pc);

      g_o[i] = so;
      g_c[i] = sc;
      g_h[i] = MathMax(g_sh[i], MathMax(so, sc));
      g_l[i] = MathMin(g_sl[i], MathMin(so, sc));
      g_clr[i] = (sc >= so) ? 0 : 1;

      if(InpArrows && i > g_begin && i < rates_total - 1 && g_clr[i] != g_clr[i - 1])
      {
         double off = (high[i] - low[i]) * 0.6;
         if(g_clr[i] == 0) g_buy[i]  = low[i] - off;
         else              g_sell[i] = high[i] + off;
      }
   }

   if(prev_calculated > 0 && (InpAlerts || InpPush))
   {
      int b = rates_total - 2;
      if(b > g_begin && g_clr[b] != g_clr[b - 1])
         Notify(time[b], g_clr[b] == 0 ? "Smoothed Heikin-Ashi turned bullish"
                                       : "Smoothed Heikin-Ashi turned bearish");
   }
   return(rates_total);
}

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