//+------------------------------------------------------------------+
//|                                           MACD LR Histogram.mq5  |
//|  Linear-regression MACD histogram (cross-to-cross segments)      |
//+------------------------------------------------------------------+
#property description "Histogram of (LR MACD - LR Signal)."
#property description "MACD and Signal are each linearly regressed over every segment"
#property description "running from one MACD/Signal crossover to the next."
#property indicator_separate_window
#property indicator_buffers 2
#property indicator_plots   1

#property indicator_type1   DRAW_COLOR_HISTOGRAM
#property indicator_width1  3
#property indicator_label1  "LR Hist"
#property indicator_level1  0.0
#property indicator_levelcolor clrDimGray
#property indicator_levelstyle STYLE_DOT

//------------------------------------------------------------------
// Enums & Inputs
//------------------------------------------------------------------
enum enPrices
{
   pr_close,      // Close
   pr_open,       // Open
   pr_high,       // High
   pr_low,        // Low
   pr_median,     // Median
   pr_typical,    // Typical
   pr_weighted,   // Weighted
   pr_average,    // Average (high+low+open+close)/4
   pr_medianb,    // Average median body (open+close)/2
   pr_tbiased,    // Trend biased price
   pr_haclose,    // Heiken ashi close
   pr_haopen ,    // Heiken ashi open
   pr_hahigh,     // Heiken ashi high
   pr_halow,      // Heiken ashi low
   pr_hamedian,   // Heiken ashi median
   pr_hatypical,  // Heiken ashi typical
   pr_haweighted, // Heiken ashi weighted
   pr_haaverage,  // Heiken ashi average
   pr_hamedianb,  // Heiken ashi median body
   pr_hatbiased   // Heiken ashi trend biased price
};

input group "--- MTF Settings ---"
input ENUM_TIMEFRAMES InpTimeframe    = PERIOD_CURRENT; // Indicator Timeframe

input group "--- MACD Settings ---"
input int             InpFastEMA      = 12;       // Macd fast period
input int             InpSlowEMA      = 26;       // Macd slow period
input int             InpSignalEMA    = 9;        // Signal period
input enPrices        InpAppliedPrice = pr_close; // Price to use

input group "--- Histogram Colors ---"
input color           InpPosRising    = C'96,170,204';  // Above 0, growing
input color           InpPosFalling   = C'35,100,130';  // Above 0, shrinking
input color           InpNegFalling   = C'255,140,255'; // Below 0, growing (more negative)
input color           InpNegRising    = C'180,70,180';  // Below 0, shrinking

//------------------------------------------------------------------
// Buffers
//------------------------------------------------------------------
double HistBuffer[];
double HistColors[];

// HTF working arrays (index 0 = oldest)
double htf_ema_f[];
double htf_ema_s[];
double htf_ema_sig[];
double htf_macd[];
double htf_sig[];
double htf_lr_macd[];
double htf_lr_sig[];
double htf_hist[];

//------------------------------------------------------------------
// Initialization
//------------------------------------------------------------------
int OnInit()
{
   SetIndexBuffer(0, HistBuffer, INDICATOR_DATA);
   SetIndexBuffer(1, HistColors, INDICATOR_COLOR_INDEX);

   PlotIndexSetInteger(0, PLOT_COLOR_INDEXES, 4);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, 0, InpPosRising);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, 1, InpPosFalling);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, 2, InpNegFalling);
   PlotIndexSetInteger(0, PLOT_LINE_COLOR, 3, InpNegRising);
   PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE);

   string tfString = (InpTimeframe == PERIOD_CURRENT) ? StringSubstr(EnumToString(_Period), 7)
                                                      : StringSubstr(EnumToString(InpTimeframe), 7);
   IndicatorSetString(INDICATOR_SHORTNAME, "MACD LR Hist (" + tfString + "," + string(InpFastEMA) + "," +
                      string(InpSlowEMA) + "," + string(InpSignalEMA) + ")");
   IndicatorSetInteger(INDICATOR_DIGITS, _Digits + 2);
   return INIT_SUCCEEDED;
}

//------------------------------------------------------------------
// Main Calculation
//------------------------------------------------------------------
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[])
{
   int htf_total = iBars(_Symbol, InpTimeframe);
   if(htf_total <= 0) return(0);

   double   htf_Open[], htf_High[], htf_Low[], htf_Close[];
   datetime htf_Time[];
   if(CopyTime (_Symbol, InpTimeframe, 0, htf_total, htf_Time)  <= 0) return(0);
   if(CopyOpen (_Symbol, InpTimeframe, 0, htf_total, htf_Open)  <= 0) return(0);
   if(CopyHigh (_Symbol, InpTimeframe, 0, htf_total, htf_High)  <= 0) return(0);
   if(CopyLow  (_Symbol, InpTimeframe, 0, htf_total, htf_Low)   <= 0) return(0);
   if(CopyClose(_Symbol, InpTimeframe, 0, htf_total, htf_Close) <= 0) return(0);

   // Full reset when history changed underneath us
   static int      htf_prev  = 0;
   static datetime htf_first = 0;
   if(prev_calculated == 0 || htf_total < htf_prev || htf_Time[0] != htf_first)
   {
      htf_prev  = 0;
      htf_first = htf_Time[0];
   }

   if(ArraySize(htf_macd) != htf_total)
   {
      ArrayResize(htf_ema_f,   htf_total);
      ArrayResize(htf_ema_s,   htf_total);
      ArrayResize(htf_ema_sig, htf_total);
      ArrayResize(htf_macd,    htf_total);
      ArrayResize(htf_sig,     htf_total);
      ArrayResize(htf_lr_macd, htf_total);
      ArrayResize(htf_lr_sig,  htf_total);
      ArrayResize(htf_hist,    htf_total);
   }

   // --- HTF: MACD + Signal ---
   int    htf_start = (htf_prev > 0) ? htf_prev - 1 : 0;
   double aF   = 2.0 / (1.0 + InpFastEMA);
   double aS   = 2.0 / (1.0 + InpSlowEMA);
   double aSig = 2.0 / (1.0 + InpSignalEMA);

   for(int i = htf_start; i < htf_total; i++)
   {
      double price = getPrice(InpAppliedPrice, htf_Open, htf_Close, htf_High, htf_Low, htf_total, i);
      if(i == 0)
      {
         htf_ema_f[0] = price;
         htf_ema_s[0] = price;
      }
      else
      {
         htf_ema_f[i] = htf_ema_f[i-1] + aF * (price - htf_ema_f[i-1]);
         htf_ema_s[i] = htf_ema_s[i-1] + aS * (price - htf_ema_s[i-1]);
      }
      htf_macd[i] = htf_ema_f[i] - htf_ema_s[i];

      if(i == 0) htf_ema_sig[0] = htf_macd[0];
      else       htf_ema_sig[i] = htf_ema_sig[i-1] + aSig * (htf_macd[i] - htf_ema_sig[i-1]);
      htf_sig[i] = htf_ema_sig[i];
   }

   // --- HTF: linear regression between crossovers -> histogram ---
   int changedFrom = ComputeLRSegments(htf_total, htf_start);
   htf_prev = htf_total;

   // --- Map to chart timeframe ---
   // The newest segment is re-fitted on every bar, so also redraw the chart bars
   // that belong to the segment(s) that changed, not just the newest bar.
   int ltf_start = (prev_calculated > 0) ? prev_calculated - 1 : 0;
   if(ltf_start > 0)
   {
      datetime tChanged = htf_Time[changedFrom];
      while(ltf_start > 0 && time[ltf_start-1] >= tChanged) ltf_start--;
   }

   int htf_idx = 0;
   for(int i = ltf_start; i < rates_total; i++)
   {
      while(htf_idx < htf_total - 1 && htf_Time[htf_idx + 1] <= time[i]) htf_idx++;

      double h = htf_hist[htf_idx];
      if(h == EMPTY_VALUE)
      {
         HistBuffer[i] = EMPTY_VALUE;
         HistColors[i] = 0;
         continue;
      }

      double hp = (htf_idx > 0) ? htf_hist[htf_idx-1] : EMPTY_VALUE;
      bool growing = (hp == EMPTY_VALUE) ? true : (MathAbs(h) >= MathAbs(hp));

      int col;
      if(h >= 0.0) col = growing ? 0 : 1;
      else         col = growing ? 2 : 3;

      HistBuffer[i] = h;
      HistColors[i] = col;
   }

   return(rates_total);
}

//------------------------------------------------------------------
// Linear-regression helpers
//------------------------------------------------------------------

// Same crossover convention as the original indicator's exit logic
bool IsMacdCross(int i)
{
   if(i < 1) return false;
   double d0 = htf_macd[i-1] - htf_sig[i-1];
   double d1 = htf_macd[i]   - htf_sig[i];
   return ((d0 <= 0.0 && d1 > 0.0) || (d0 >= 0.0 && d1 < 0.0));
}

// Least-squares line y = a + b*x (x = 0..n-1) over src[from..to];
// fitted values are written to dst[from..to].
void FitSegment(const double &src[], double &dst[], int from, int to)
{
   int n = to - from + 1;
   if(n <= 0) return;

   double sx = 0, sxx = 0, sy = 0, sxy = 0;
   for(int k = 0; k < n; k++)
   {
      double y = src[from + k];
      sx  += k;
      sxx += (double)k * k;
      sy  += y;
      sxy += k * y;
   }

   double den   = n * sxx - sx * sx;
   double slope = (n >= 2 && den != 0.0) ? (n * sxy - sx * sy) / den : 0.0;
   double icpt  = (sy - slope * sx) / n;

   for(int k = 0; k < n; k++)
      dst[from + k] = icpt + slope * k;
}

// Segment = [crossover bar ... bar before next crossover]; the open last segment
// runs to the newest bar. Bars before the first crossover stay EMPTY.
// Returns the first HTF bar whose value may have changed.
int ComputeLRSegments(int total, int from)
{
   int start = from;
   while(start > 0 && !IsMacdCross(start)) start--;

   int i = start;
   if(!IsMacdCross(start))
   {
      int k = start;
      while(k < total && !IsMacdCross(k))
      {
         htf_lr_macd[k] = EMPTY_VALUE;
         htf_lr_sig[k]  = EMPTY_VALUE;
         htf_hist[k]    = EMPTY_VALUE;
         k++;
      }
      i = k;
   }

   int prevSeg = MathMin(i, total - 1);
   int lastSeg = prevSeg;

   while(i < total)
   {
      int j = i + 1;
      while(j < total && !IsMacdCross(j)) j++;

      prevSeg = lastSeg;
      lastSeg = i;

      FitSegment(htf_macd, htf_lr_macd, i, j - 1);
      FitSegment(htf_sig,  htf_lr_sig,  i, j - 1);
      for(int k = i; k < j; k++) htf_hist[k] = htf_lr_macd[k] - htf_lr_sig[k];

      i = j;
   }
   return prevSeg;
}

//------------------------------------------------------------------
// Price helper (same as the main indicator)
//------------------------------------------------------------------
double workHa[][4];

double getPrice(int price, const double& open[], const double& close[], const double& high[], const double& low[], int bars, int i, int instanceNo=0)
{
   if(price >= pr_haclose)
   {
      if(ArrayRange(workHa,0) != bars) ArrayResize(workHa,bars);

      double haOpen;
      if(i > 0) haOpen = (workHa[i-1][instanceNo+2] + workHa[i-1][instanceNo+3]) / 2.0;
      else      haOpen = (open[i] + close[i]) / 2;

      double haClose = (open[i] + high[i] + low[i] + close[i]) / 4.0;
      double haHigh  = MathMax(high[i], MathMax(haOpen, haClose));
      double haLow   = MathMin(low[i],  MathMin(haOpen, haClose));

      if(haOpen < haClose)
      {
         workHa[i][instanceNo+0] = haLow;
         workHa[i][instanceNo+1] = haHigh;
      }
      else
      {
         workHa[i][instanceNo+0] = haHigh;
         workHa[i][instanceNo+1] = haLow;
      }
      workHa[i][instanceNo+2] = haOpen;
      workHa[i][instanceNo+3] = haClose;

      switch(price)
      {
         case pr_haclose:     return(haClose);
         case pr_haopen:      return(haOpen);
         case pr_hahigh:      return(haHigh);
         case pr_halow:       return(haLow);
         case pr_hamedian:    return((haHigh+haLow)/2.0);
         case pr_hamedianb:   return((haOpen+haClose)/2.0);
         case pr_hatypical:   return((haHigh+haLow+haClose)/3.0);
         case pr_haweighted:  return((haHigh+haLow+haClose+haClose)/4.0);
         case pr_haaverage:   return((haHigh+haLow+haClose+haOpen)/4.0);
         case pr_hatbiased:
            if(haClose > haOpen) return((haHigh+haClose)/2.0);
            else                 return((haLow+haClose)/2.0);
      }
   }

   switch(price)
   {
      case pr_close:     return(close[i]);
      case pr_open:      return(open[i]);
      case pr_high:      return(high[i]);
      case pr_low:       return(low[i]);
      case pr_median:    return((high[i]+low[i])/2.0);
      case pr_medianb:   return((open[i]+close[i])/2.0);
      case pr_typical:   return((high[i]+low[i]+close[i])/3.0);
      case pr_weighted:  return((high[i]+low[i]+close[i]+close[i])/4.0);
      case pr_average:   return((high[i]+low[i]+close[i]+open[i])/4.0);
      case pr_tbiased:
         if(close[i] > open[i]) return((high[i]+close[i])/2.0);
         else                   return((low[i]+close[i])/2.0);
   }
   return(0);
}
