//+------------------------------------------------------------------+
//|                                               Arrow GannGrid.mq4 |
//+------------------------------------------------------------------+
#property strict

#property indicator_chart_window
#property indicator_buffers 2
#property indicator_color1 clrAqua    // Up arrow color
#property indicator_color2 clrMagenta      // Down arrow color
#property indicator_width1 1
#property indicator_width2 1
#property indicator_style1 DRAW_ARROW
#property indicator_style2 DRAW_ARROW

//---- external inputs
extern int   nBarsBack         = 120;      // Number of bars to process
extern int   Price_Mode        = 5;        // 0 = Close, 4 = Median, 5 = Typical
extern int   MainGrid_Intervals= 36;       // Used to compute gridStep (for trend calc)
extern double GannGrid_Interval= 8.0;        // Multiplier for reversal level
extern int   ATRPeriod         = 34;       // ATR period for dynamic arrow offset
extern double ArrowOffsetFactor= 0.4;        // Factor to multiply ATR for offset

//---- indicator buffers for arrows only
double upArrow[];
double dnArrow[];

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {
   IndicatorBuffers(2);
   SetIndexBuffer(0, upArrow);
   SetIndexStyle(0, DRAW_ARROW);
   // Use solid up arrow (Wingdings code 233 is typically a filled arrow)
   SetIndexArrow(0, 233);
   SetIndexEmptyValue(0, EMPTY_VALUE);
   
   SetIndexBuffer(1, dnArrow);
   SetIndexStyle(1, DRAW_ARROW);
   // Use solid down arrow (Wingdings code 234 is typically a filled arrow)
   SetIndexArrow(1, 234);
   SetIndexEmptyValue(1, EMPTY_VALUE);
   
   IndicatorShortName("Arrow GannGrid");
   return(0);
  }
  
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
  {
   return(0);
  }
  
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
  {
   // Ensure enough bars are available
   if(Bars <= nBarsBack) return(0);
   
   int counted_bars = IndicatorCounted();
   if(counted_bars < 0) return(-1);
   if(counted_bars > 0) counted_bars--;
   
   // Clear arrow buffers for all bars
   for(int i = 0; i < Bars; i++)
     {
      upArrow[i] = EMPTY_VALUE;
      dnArrow[i] = EMPTY_VALUE;
     }
   
   //--- Calculate the price range (max and min) over the sample period
   double maxPrice = CurrentPrice(Price_Mode, nBarsBack-1);
   double minPrice = maxPrice;
   for(int i = nBarsBack-1; i >= 1; i--)  // Process only closed bars (i>=1)
     {
      double priceVal = CurrentPrice(Price_Mode, i);
      if(priceVal > maxPrice) maxPrice = priceVal;
      if(priceVal < minPrice) minPrice = priceVal;
     }
   double gridStep = (maxPrice - minPrice) / MainGrid_Intervals;
   
   //--- Initialize trend using the oldest bar in the range
   int firstBar = nBarsBack - 1;
   double firstPriceValue = CurrentPrice(Price_Mode, firstBar);
   double prevPrice = CurrentPrice(Price_Mode, firstBar+1);
   int iTrend;
   double lineValue;
   if(firstPriceValue - prevPrice >= 0)
     {
      iTrend = 1;
      lineValue = firstPriceValue - GannGrid_Interval * gridStep;
     }
   else
     {
      iTrend = -1;
      lineValue = firstPriceValue + GannGrid_Interval * gridStep;
     }
   
   //--- Loop through bars (from older to most recent closed bar, i>=1)
   for(int i = nBarsBack - 2; i >= 1; i--)
     {
      double currPrice = CurrentPrice(Price_Mode, i);
      
      if(iTrend == 1)
        {
         if(currPrice - GannGrid_Interval * gridStep > lineValue + gridStep)
            lineValue = currPrice - GannGrid_Interval * gridStep;
         else
            lineValue = lineValue + gridStep;
         if(currPrice < lineValue)
           {
            // Reversal: up to down. Place a down arrow with dynamic offset.
            iTrend = -1;
            lineValue = currPrice + GannGrid_Interval * gridStep;
            double atr = iATR(Symbol(),Period(),ATRPeriod,i);
            dnArrow[i] = High[i] + atr * ArrowOffsetFactor;
           }
        }
      else if(iTrend == -1)
        {
         if(currPrice + GannGrid_Interval * gridStep < lineValue - gridStep)
            lineValue = currPrice + GannGrid_Interval * gridStep;
         else
            lineValue = lineValue - gridStep;
         if(currPrice > lineValue)
           {
            // Reversal: down to up. Place an up arrow with dynamic offset.
            iTrend = 1;
            lineValue = currPrice - GannGrid_Interval * gridStep;
            double atr = iATR(Symbol(),Period(),ATRPeriod,i);
            upArrow[i] = Low[i] - atr * ArrowOffsetFactor;
           }
        }
     }
   return(0);
  }
  
//+------------------------------------------------------------------+
//| Return the selected price (Close, Median, or Typical)            |
//+------------------------------------------------------------------+
double CurrentPrice(int priceCode, int i)
  {
   double dPrice = 0;
   switch(priceCode)
     {
      case 0: // Close prices
         dPrice = Close[i];
         break;
      case 4: // Median prices
         dPrice = (High[i] + Low[i]) / 2.0;
         break;
      case 5: // Typical prices
         dPrice = (High[i] + Low[i] + Close[i]) / 3.0;
         break;
      default:
         dPrice = Close[i];
         break;
     }
   return(dPrice);
  }
//+------------------------------------------------------------------+
