//+------------------------------------------------------------------+
//|                      Donchian Channels - Generalized version.mq4 |
//|                         Copyright © 2005, Luis Guilherme Damiani |
//|                                      http://www.damianifx.com.br |
//+------------------------------------------------------------------+
#property copyright "Copyright © 2005, Luis Guilherme Damiani"
#property link      "http://www.damianifx.com.br"
//---- indicator version number
#property version   "1.00"
//---- drawing the indicator in the main window
#property indicator_chart_window
//---- number of indicator buffers
#property indicator_buffers 3
//---- 3 plots are used
#property indicator_plots   3
//+-----------------------------------+
//|  parameters of indicator drawing  |
//+-----------------------------------+
//---- drawing of the indicator as a line
#property indicator_type1   DRAW_LINE
//---- use olive color for the indicator line
#property indicator_color1 OliveDrab
//---- indicator line is a solid curve
#property indicator_style1  STYLE_SOLID
//---- indicator line width is equal to 1
#property indicator_width1  1
//---- indicator label display
#property indicator_label1  "Upper Donchian"

//---- drawing of the indicator as a line
#property indicator_type2   DRAW_LINE
//---- use gray color for the indicator line
#property indicator_color2 Gray
//---- indicator line is a solid curve
#property indicator_style2  STYLE_SOLID
//---- indicator line width is equal to 1
#property indicator_width2  1
//---- indicator label display
#property indicator_label2  "Middle Donchian"

//---- drawing of the indicator as a line
#property indicator_type3   DRAW_LINE
//---- use pale violet red color for the indicator line
#property indicator_color3 PaleVioletRed
//---- indicator line is a solid curve
#property indicator_style3  STYLE_SOLID
//---- indicator line width is equal to 1
#property indicator_width3  1
//---- indicator label display
#property indicator_label3  "Lower Donchian"
//+-----------------------------------+
//|  Enumeration declaration          |
//+-----------------------------------+
enum Applied_Extrem //Type of extreme points
  {
   HIGH_LOW,
   HIGH_LOW_OPEN,
   HIGH_LOW_CLOSE,
   OPEN_HIGH_LOW,
   CLOSE_HIGH_LOW
  };
//+-----------------------------------+
//|  INPUT PARAMETERS OF THE INDICATOR|
//+-----------------------------------+
input int DonchianPeriod=20;            //Period of averaging
input Applied_Extrem Extremes=HIGH_LOW; //Type of extreme points
input int Margins=-2;
input int Shift=0;                      //Horizontal shift of the indicator in bars
//+-----------------------------------+
//---- indicator buffers
double UpperBuffer[];
double MiddleBuffer[];
double LowerBuffer[];

#include <AZ-INVEST/CustomBarConfig.mqh>

//+------------------------------------------------------------------+
//|  searching index of the highest bar                              |
//+------------------------------------------------------------------+
int iHighest(
             const double &array[],   // array for searching for maximum element index
             int count,               // the number of the array elements (from a current bar to the index descending),
                                      // along which the searching must be performed.
             int startPos             // the initial bar index (shift relative to a current bar),
                                      // the search for the greatest value begins from
             )
  {
//----
   int index=startPos;

//----checking correctness of the initial index
   if(startPos<0)
     {
      Print("Bad value in the function iHighest, startPos = ",startPos);
      return(0);
     }
//---- checking correctness of startPos value
   if(startPos-count<0)
      count=startPos;

   double max=array[startPos];
//---- searching for an index
   for(int i=startPos; i>startPos-count; i--)
     {
      if(array[i]>max)
        {
         index=i;
         max=array[i];
        }
     }
//---- returning of the greatest bar index
   return(index);
  }
//+------------------------------------------------------------------+
//|  searching index of the lowest bar                               |
//+------------------------------------------------------------------+
int iLowest(
            const double &array[],// array for searching for minimum element index
            int count,// the number of the array elements (from a current bar to the index descending),
            // along which the searching must be performed.
            int startPos //the initial bar index (shift relative to a current bar),
            // the search for the lowest value begins from
            )
  {
//----
   int index=startPos;

//----checking correctness of the initial index
   if(startPos<0)
     {
      Print("Bad value in the function iLowest, startPos = ",startPos);
      return(0);
     }

//---- checking correctness of startPos value
   if(startPos-count<0)
      count=startPos;

   double min=array[startPos];

//---- searching for an index
   for(int i=startPos; i>startPos-count; i--)
     {
      if(array[i]<min)
        {
         index=i;
         min=array[i];
        }
     }
//---- returning of the lowest bar index
   return(index);
  }
//+------------------------------------------------------------------+    
//| Donchian Channel indicator initialization function               |
//+------------------------------------------------------------------+  
void OnInit()
  {
//---- turning a dynamic array into an indicator buffer
   SetIndexBuffer(0,UpperBuffer,INDICATOR_DATA);
//---- shifting the indicator 1 horizontally by AroonShift
   PlotIndexSetInteger(0,PLOT_SHIFT,Shift);
//---- shifting the start of drawing of the indicator 1
   PlotIndexSetInteger(0,PLOT_DRAW_BEGIN,DonchianPeriod-1);
//--- create label to display in DataWindow
   PlotIndexSetString(0,PLOT_LABEL,"Upper Donchian");
//---- setting values of the indicator that won't be visible on the chart
   PlotIndexSetDouble(0,PLOT_EMPTY_VALUE,EMPTY_VALUE);

//---- turning a dynamic array into an indicator buffer
   SetIndexBuffer(1,MiddleBuffer,INDICATOR_DATA);
//---- shifting the indicator 2 horizontally
   PlotIndexSetInteger(1,PLOT_SHIFT,Shift);
//---- shifting the start of drawing of the indicator 2
   PlotIndexSetInteger(1,PLOT_DRAW_BEGIN,DonchianPeriod-1);
//--- create label to display in DataWindow
   PlotIndexSetString(1,PLOT_LABEL,"Middle Donchian");
//---- setting values of the indicator that won't be visible on the chart
   PlotIndexSetDouble(1,PLOT_EMPTY_VALUE,EMPTY_VALUE);

//---- turning a dynamic array into an indicator buffer
   SetIndexBuffer(2,LowerBuffer,INDICATOR_DATA);
//---- shifting the indicator 3 horizontally
   PlotIndexSetInteger(2,PLOT_SHIFT,Shift);
//---- shifting the start of drawing of the indicator 3
   PlotIndexSetInteger(2,PLOT_DRAW_BEGIN,DonchianPeriod-1);
//--- create label to display in DataWindow
   PlotIndexSetString(2,PLOT_LABEL,"Lower Donchian");
//---- setting values of the indicator that won't be visible on the chart
   PlotIndexSetDouble(2,PLOT_EMPTY_VALUE,EMPTY_VALUE);

//---- initialization of variable for indicator short name
   string shortname;
   StringConcatenate(shortname,"Donchian( DonchianPeriod = ",DonchianPeriod,")");
//--- creation of the name to be displayed in a separate sub-window and in a pop up help
   IndicatorSetString(INDICATOR_SHORTNAME,shortname);
//--- determination of accuracy of displaying of the indicator values
   IndicatorSetInteger(INDICATOR_DIGITS,_Digits+1);
//---- end of initialization
  }
//+------------------------------------------------------------------+  
//| Donchian Channel iteration function                              |
//+------------------------------------------------------------------+  
int OnCalculate(const int rates_total,    // amount of history in bars at the current tick
                const int prev_calculated,// amount of history in bars at the previous tick
                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[])
  {
//---- checking the number of bars to be enough for the calculation
   if(rates_total<DonchianPeriod+1) return(0);

//---- declaration of variables with a floating point  
   double smin,smax,SsMax=0,SsMin=0;
//---- declaration of integer variables
   int first,bar;

   if(!customChartIndicator.OnCalculate(rates_total,prev_calculated,time,close))
      return(0);
      
   if(!customChartIndicator.BufferSynchronizationCheck(close))
      return(0);
   
   int _prev_calculated = customChartIndicator.GetPrevCalculated();

//---- calculation of the starting number 'first' for the cycle of recalculation of bars
   if(_prev_calculated==0)      // checking for the first start of the indicator calculation
     {
      first=DonchianPeriod;    // starting number for calculation of all bars
     }
   else
     {
      first=_prev_calculated-1; // starting number for calculation of new bars
     }

//---- Main cycle of calculation of the channel
   for(bar=first; bar<rates_total; bar++)
     {
      switch(Extremes)
        {
         case HIGH_LOW:
            SsMax=customChartIndicator.High[iHighest(customChartIndicator.High,DonchianPeriod,bar)];
            SsMin=customChartIndicator.Low[iLowest(customChartIndicator.Low,DonchianPeriod,bar)];
            break;

         case HIGH_LOW_OPEN:
            SsMax=(customChartIndicator.Open[iHighest(customChartIndicator.Open,DonchianPeriod,bar)]+customChartIndicator.High[iHighest(customChartIndicator.High,DonchianPeriod,bar)])/2;
            SsMin=(customChartIndicator.Open[iLowest(customChartIndicator.Open,DonchianPeriod,bar)]+customChartIndicator.Low[iLowest(customChartIndicator.Low,DonchianPeriod,bar)])/2;
            break;

         case HIGH_LOW_CLOSE:
            SsMax=(customChartIndicator.Close[iHighest(customChartIndicator.Close,DonchianPeriod,bar)]+customChartIndicator.High[iHighest(customChartIndicator.High,DonchianPeriod,bar)])/2;
            SsMin=(customChartIndicator.Close[iLowest(customChartIndicator.Close,DonchianPeriod,bar)]+customChartIndicator.Low[iLowest(customChartIndicator.Low,DonchianPeriod,bar)])/2;
            break;

         case OPEN_HIGH_LOW:
            SsMax=customChartIndicator.Open[iHighest(customChartIndicator.Open,DonchianPeriod,bar)];
            SsMin=customChartIndicator.Open[iLowest(customChartIndicator.Open,DonchianPeriod,bar)];
            break;

         case CLOSE_HIGH_LOW:
            SsMax=customChartIndicator.Close[iHighest(customChartIndicator.Close,DonchianPeriod,bar)];
            SsMin=customChartIndicator.Close[iLowest(customChartIndicator.Close,DonchianPeriod,bar)];
            break;
        }

      smin=SsMin+(SsMax-SsMin)*Margins/100;
      smax=SsMax-(SsMax-SsMin)*Margins/100;
      UpperBuffer[bar]=smax;
      LowerBuffer[bar]=smin;
      MiddleBuffer[bar]=(smax+smin)/2.0;
     }
//----    
   return(rates_total);
  }
//+------------------------------------------------------------------+
