//+------------------------------------------------------------------+
//|                                        DualFiboGoldenWave.mq4    |
//|                        Copyright 2024, MetaQuotes Software Corp. |
//|                                              https://www.mql5.com |
//+------------------------------------------------------------------+
#property strict
#property indicator_chart_window
#property indicator_buffers 8
#property indicator_color1 Yellow
#property indicator_color2 Yellow
#property indicator_color3 Blue
#property indicator_color4 Blue
#property indicator_color5 Green
#property indicator_color6 Red
#property indicator_color7 Lime
#property indicator_color8 Magenta
#property indicator_width1 1
#property indicator_width2 1
#property indicator_width3 1
#property indicator_width4 1

// Price type enum
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_tbiased2,   // Trend biased (extreme) 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
   pr_hatbiased2, // Heiken ashi trend biased (extreme) price
   pr_habclose,   // Heiken ashi (better formula) close
   pr_habopen,    // Heiken ashi (better formula) open
   pr_habhigh,    // Heiken ashi (better formula) high
   pr_hablow,     // Heiken ashi (better formula) low
   pr_habmedian,  // Heiken ashi (better formula) median
   pr_habtypical, // Heiken ashi (better formula) typical
   pr_habweighted,// Heiken ashi (better formula) weighted
   pr_habaverage, // Heiken ashi (better formula) average
   pr_habmedianb, // Heiken ashi (better formula) median body
   pr_habtbiased, // Heiken ashi (better formula) trend biased price
   pr_habtbiased2 // Heiken ashi (better formula) trend biased (extreme) price
  };

// Input parameters
input int      shortPeriod = 20;
input int      longPeriod = 100;
input double   fibArea1 = 0.5;
input double   fibArea2 = 0.618;
input color    shortFibColor = clrYellow;
input color    longFibColor = clrBlue;
input color    buySignalColor = clrLime;
input color    sellSignalColor = clrMagenta;
input bool     alertsOn = false;
input enPrices priceType = pr_close; // Price type for calculations

// Indicator buffers
double shortFib05Buffer[];
double shortFib061Buffer[];
double longFib05Buffer[];
double longFib061Buffer[];
double buySignalBuffer[];
double sellSignalBuffer[];
double shortTrendBuffer[];
double longTrendBuffer[];

// Price calculation variables
#define _priceInstances     1
#define _priceInstancesSize 4
double  _priceWorkHa[][_priceInstances*_priceInstancesSize];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit()
  {
   IndicatorBuffers(8);
   SetIndexBuffer(0, shortFib05Buffer);
   SetIndexBuffer(1, shortFib061Buffer);
   SetIndexBuffer(2, longFib05Buffer);
   SetIndexBuffer(3, longFib061Buffer);
   SetIndexBuffer(4, buySignalBuffer);
   SetIndexBuffer(5, sellSignalBuffer);
   SetIndexBuffer(6, shortTrendBuffer);
   SetIndexBuffer(7, longTrendBuffer);

   SetIndexStyle(0, DRAW_LINE, STYLE_SOLID, 1, shortFibColor);
   SetIndexStyle(1, DRAW_LINE, STYLE_SOLID, 1, shortFibColor);
   SetIndexStyle(2, DRAW_LINE, STYLE_SOLID, 1, longFibColor);
   SetIndexStyle(3, DRAW_LINE, STYLE_SOLID, 1, longFibColor);
   SetIndexStyle(4, DRAW_ARROW, STYLE_SOLID, 1, buySignalColor);
   SetIndexStyle(5, DRAW_ARROW, STYLE_SOLID, 1, sellSignalColor);

   SetIndexArrow(4, 225); // Wingdings code for up arrow
   SetIndexArrow(5, 226); // Wingdings code for down arrow

   return(INIT_SUCCEEDED);
  }

//+------------------------------------------------------------------+
//| 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[])
  {
   int limit = rates_total - prev_calculated;
   if(prev_calculated > 0)
      limit++;

   for(int i = limit - 1; i >= 0; i--)
     {
      double price = getPrice(priceType, open, close, high, low, i, rates_total);

      // Calculate short-term Fibonacci levels
      double shortPivotHigh = high[iHighest(NULL, 0, MODE_HIGH, shortPeriod, i)];
      double shortPivotLow = low[iLowest(NULL, 0, MODE_LOW, shortPeriod, i)];
      double shortDifference = shortPivotHigh - shortPivotLow;
      shortFib05Buffer[i] = shortPivotHigh - (shortDifference * fibArea1);
      shortFib061Buffer[i] = shortPivotHigh - (shortDifference * fibArea2);

      // Calculate long-term Fibonacci levels
      double longPivotHigh = high[iHighest(NULL, 0, MODE_HIGH, longPeriod, i)];
      double longPivotLow = low[iLowest(NULL, 0, MODE_LOW, longPeriod, i)];
      double longDifference = longPivotHigh - longPivotLow;
      longFib05Buffer[i] = longPivotHigh - (longDifference * fibArea1);
      longFib061Buffer[i] = longPivotHigh - (longDifference * fibArea2);

      // Determine trends
      shortTrendBuffer[i] = (price > shortFib05Buffer[i]) ? 1 : -1;
      longTrendBuffer[i] = (price > longFib05Buffer[i]) ? 1 : -1;

      buySignalBuffer[i] = EMPTY_VALUE;
      sellSignalBuffer[i] = EMPTY_VALUE;

      if(i < rates_total - 1)  // Ensure we're not at the last candle
        {
         // Buy signal: Main trend up, short trend changes to up
         if(longTrendBuffer[i] == 1 && shortTrendBuffer[i] == 1 && shortTrendBuffer[i+1] == -1)
           {
            buySignalBuffer[i] = low[i];
            if(alertsOn)
              {
               Alert("Buy signal: Main trend up, short trend turned up");
               PlaySound("alert.wav");
              }
           }

         // Sell signal: Main trend down, short trend changes to down
         if(longTrendBuffer[i] == -1 && shortTrendBuffer[i] == -1 && shortTrendBuffer[i+1] == 1)
           {
            sellSignalBuffer[i] = high[i];
            if(alertsOn)
              {
               Alert("Sell signal: Main trend down, short trend turned down");
               PlaySound("alert.wav");
              }
           }
        }
     }

   return(rates_total);
  }

//+------------------------------------------------------------------+
//| Price calculation function                                       |
//+------------------------------------------------------------------+
double getPrice(int tprice, const double& open[], const double& close[], const double& high[], const double& low[], int i, int bars, int instanceNo=0)
  {
   if(tprice>=pr_haclose)
     {
      if(ArrayRange(_priceWorkHa,0)!= bars)
         ArrayResize(_priceWorkHa,bars);
      instanceNo*=_priceInstancesSize;
#ifdef __MQL4__
      int r = bars-i-1;
#else
      int r=i;
#endif

      double haOpen  = (r>0) ? (_priceWorkHa[r-1][instanceNo+2] + _priceWorkHa[r-1][instanceNo+3])/2.0 : (open[i]+close[i])/2;;
      double haClose = (open[i]+high[i]+low[i]+close[i]) / 4.0;
      if(tprice>=pr_habclose)
         if(high[i]!=low[i])
            haClose = (open[i]+close[i])/2.0+(((close[i]-open[i])/(high[i]-low[i]))*fabs((close[i]-open[i])/2.0));
         else
            haClose = (open[i]+close[i])/2.0;
      double haHigh  = fmax(high[i], fmax(haOpen,haClose));
      double haLow   = fmin(low[i], fmin(haOpen,haClose));

      if(haOpen<haClose)
        {
         _priceWorkHa[r][instanceNo+0] = haLow;
         _priceWorkHa[r][instanceNo+1] = haHigh;
        }
      else
        {
         _priceWorkHa[r][instanceNo+0] = haHigh;
         _priceWorkHa[r][instanceNo+1] = haLow;
        }
      _priceWorkHa[r][instanceNo+2] = haOpen;
      _priceWorkHa[r][instanceNo+3] = haClose;

      switch(tprice)
        {
         case pr_haclose:
         case pr_habclose:
            return(haClose);
         case pr_haopen:
         case pr_habopen:
            return(haOpen);
         case pr_hahigh:
         case pr_habhigh:
            return(haHigh);
         case pr_halow:
         case pr_hablow:
            return(haLow);
         case pr_hamedian:
         case pr_habmedian:
            return((haHigh+haLow)/2.0);
         case pr_hamedianb:
         case pr_habmedianb:
            return((haOpen+haClose)/2.0);
         case pr_hatypical:
         case pr_habtypical:
            return((haHigh+haLow+haClose)/3.0);
         case pr_haweighted:
         case pr_habweighted:
            return((haHigh+haLow+haClose+haClose)/4.0);
         case pr_haaverage:
         case pr_habaverage:
            return((haHigh+haLow+haClose+haOpen)/4.0);
         case pr_hatbiased:
         case pr_habtbiased:
            if(haClose>haOpen)
               return((haHigh+haClose)/2.0);
            else
               return((haLow+haClose)/2.0);
         case pr_hatbiased2:
         case pr_habtbiased2:
            if(haClose>haOpen)
               return(haHigh);
            if(haClose<haOpen)
               return(haLow);
            return(haClose);
        }
     }

   switch(tprice)
     {
      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);
      case pr_tbiased2:
         if(close[i]>open[i])
            return(high[i]);
         if(close[i]<open[i])
            return(low[i]);
         return(close[i]);
     }
   return(0);
  }

//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
  {
// Cleanup code if needed
  }
//+------------------------------------------------------------------+
