//+------------------------------------------------------------------+
//|                                                  value chart.mq5 |
//|                                                                  |
//| Helweg/Stendahl value charts                                     |
//+------------------------------------------------------------------+

#property copyright "mladen"
#property link      "mladenfx@gmail.com"
#property version   "1.00"

#property indicator_separate_window
#property indicator_buffers   16
#property indicator_plots     7

//
//
//
//
//

#property indicator_label1  "price"
#property indicator_type1   DRAW_LINE
#property indicator_color1  Lime
#property indicator_width1  1
#property indicator_label2  "ma 1"
#property indicator_type2   DRAW_LINE
#property indicator_color2  Lime
#property indicator_width2  1
#property indicator_label3  "ma 2"
#property indicator_type3   DRAW_LINE
#property indicator_color3  Red
#property indicator_width3  1
#property indicator_label4  "upper zone"
#property indicator_type4   DRAW_FILLING
#property indicator_color4  C'41,42,43'
#property indicator_style4  STYLE_SOLID
#property indicator_width4  1
#property indicator_label5  "lower zone"
#property indicator_type5   DRAW_FILLING
#property indicator_color5  C'41,42,43'
#property indicator_style5  STYLE_SOLID
#property indicator_width5  1
#property indicator_label6  "High;Low;Open;Close"
#property indicator_type6   DRAW_BARS
#property indicator_color6  DimGray
#property indicator_width6  0
#property indicator_label7  "Open;Close"
#property indicator_type7   DRAW_COLOR_HISTOGRAM2
#property indicator_color7  Green,Red,DimGray
#property indicator_width7  1

//
//
//
//
//

enum chartTypes
{
   chtBars,  // bars
   chtLine   // line
};

input int                inpBars      = 10;          // Number of bars
input chartTypes         inpChartType = chtBars;     // Show chart as :
input string             _1           = "";          // Only valid for line chart type
input ENUM_APPLIED_PRICE inpLinePrice = PRICE_CLOSE; // Price for line chart type
input string             _2           = "";          // Moving average 1
input int                inpMa1Period = 12;          // Moving average 1 period
input ENUM_MA_METHOD     inpMa1Method = MODE_EMA;    // Method for moving average 1
input ENUM_APPLIED_PRICE inpMa1Price  = PRICE_CLOSE; // Price for moving average1
input string             _3           = "";          // Moving average 2
input int                inpMa2Period = 26;          // Moving average 2 period
input ENUM_MA_METHOD     inpMa2Method = MODE_EMA;    // Method for moving average 2
input ENUM_APPLIED_PRICE inpMa2Price  = PRICE_CLOSE; // Price for moving average2

//
//
//
//
//

double vcboBuffer[];
double vcbhBuffer[];
double vcblBuffer[];
double vcbcBuffer[];
double vchoBuffer[];
double vchcBuffer[];
double colsBuffer[];
double prcsBuffer[];
double fluaBuffer[];
double flubBuffer[];
double fldaBuffer[];
double fldbBuffer[];
double mav1Buffer[];
double mav2Buffer[];
double cal1Buffer[];
double cal2Buffer[];
int    nVarP;
int    nBars;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int OnInit()
{
   SetIndexBuffer( 0,prcsBuffer,INDICATOR_DATA);        ArraySetAsSeries(prcsBuffer,true);
   SetIndexBuffer( 1,mav1Buffer,INDICATOR_DATA);        ArraySetAsSeries(mav1Buffer,true);
   SetIndexBuffer( 2,mav2Buffer,INDICATOR_DATA);        ArraySetAsSeries(mav2Buffer,true);
   SetIndexBuffer( 3,fluaBuffer,INDICATOR_DATA);        ArraySetAsSeries(fluaBuffer,true);
   SetIndexBuffer( 4,flubBuffer,INDICATOR_DATA);        ArraySetAsSeries(flubBuffer,true);
   SetIndexBuffer( 5,fldaBuffer,INDICATOR_DATA);        ArraySetAsSeries(fldaBuffer,true);
   SetIndexBuffer( 6,fldbBuffer,INDICATOR_DATA);        ArraySetAsSeries(fldbBuffer,true);
   SetIndexBuffer( 7,vcboBuffer,INDICATOR_DATA);        ArraySetAsSeries(vcboBuffer,true);
   SetIndexBuffer( 8,vcbhBuffer,INDICATOR_DATA);        ArraySetAsSeries(vcbhBuffer,true);
   SetIndexBuffer( 9,vcblBuffer,INDICATOR_DATA);        ArraySetAsSeries(vcblBuffer,true);
   SetIndexBuffer(10,vcbcBuffer,INDICATOR_DATA);        ArraySetAsSeries(vcbcBuffer,true);
   SetIndexBuffer(11,vchoBuffer,INDICATOR_DATA);        ArraySetAsSeries(vchoBuffer,true);
   SetIndexBuffer(12,vchcBuffer,INDICATOR_DATA);        ArraySetAsSeries(vchcBuffer,true);
   SetIndexBuffer(13,colsBuffer,INDICATOR_COLOR_INDEX); ArraySetAsSeries(colsBuffer,true);
   SetIndexBuffer(14,cal1Buffer,INDICATOR_CALCULATIONS);ArraySetAsSeries(cal1Buffer,true);
   SetIndexBuffer(15,cal2Buffer,INDICATOR_CALCULATIONS);ArraySetAsSeries(cal2Buffer,true);
   

   string PriceType = "";

      if (inpChartType == chtLine)
      {
         PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_LINE);
         PlotIndexSetInteger(5,PLOT_DRAW_TYPE,DRAW_NONE);
         PlotIndexSetInteger(6,PLOT_DRAW_TYPE,DRAW_NONE);
         switch(inpLinePrice)
         {
            case PRICE_CLOSE:    PriceType = ",Close";    break;  // 0
            case PRICE_OPEN:     PriceType = ",Open";     break;  // 1
            case PRICE_HIGH:     PriceType = ",High";     break;  // 2
            case PRICE_LOW:      PriceType = ",Low";      break;  // 3
            case PRICE_MEDIAN:   PriceType = ",Median";   break;  // 4
            case PRICE_TYPICAL:  PriceType = ",Typical";  break;  // 5
            case PRICE_WEIGHTED: PriceType = ",Weighted"; break;  // 6
         }      
      }
   else
      {
         PlotIndexSetInteger(0,PLOT_DRAW_TYPE,DRAW_NONE);
         PlotIndexSetInteger(5,PLOT_DRAW_TYPE,DRAW_BARS);
         PlotIndexSetInteger(6,PLOT_DRAW_TYPE,DRAW_COLOR_HISTOGRAM2);
      }
   string maAdd1=""; if (inpMa1Period>1) maAdd1 = ","+(string)inpMa1Period;
   string maAdd2=""; if (inpMa2Period>1) maAdd2 = ","+(string)inpMa2Period;
   
   
   //
   //
   //
   //
   //
   
   nBars = inpBars>7 ? inpBars : 8;
   nVarP = (int)MathRound(nBars/5.0);
      IndicatorSetString(INDICATOR_SHORTNAME,"Value chart ("+(string)inpBars+maAdd1+maAdd2+PriceType+")");
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

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++;
         if (prev_calculated ==0)
         {
            int last = (inpBars>5) ? inpBars : 5;
            limit -= last;
         }
         if (!ArrayGetAsSeries(high))  ArraySetAsSeries(high ,true);
         if (!ArrayGetAsSeries(low))   ArraySetAsSeries(low  ,true);
         if (!ArrayGetAsSeries(open))  ArraySetAsSeries(open ,true);
         if (!ArrayGetAsSeries(close)) ArraySetAsSeries(close,true);
 
            int highSize  = ArraySize(high);
            int lowSize   = ArraySize(low);
            int openSize  = ArraySize(open);
            int closeSize = ArraySize(close);

      //
      //
      //
      //
      //

      double nVar0,nVarA,nVarB,nVarC,nVarD,nVarE;
      double nVarR1,nVarR2,nVarR3,nVarR4,nVarR5;
      double nLRange;
         for (int i=limit; i>=0; i--)
         {
         
            //
            //
            //
            //
            //
            
            nVarA = iHighest(high,highSize,nVarP,i)-iLowest(low,lowSize,nVarP,i);
                        if  (nVarA == 0 && nVarP == 1)
                             nVarR1 = MathAbs(close[i]-close[i+nVarP]);
                        else nVarR1 = nVarA;                      
                     
            nVarB = iHighest(high,highSize,nVarP,i+nVarP)-iLowest(low,lowSize,nVarP,i+nVarP);
                        if (nVarB == 0 && nVarP == 1)
                             nVarR2 = MathAbs(close[i+nVarP]-close[i+nVarP*2]);
                        else nVarR2 = nVarB;

            nVarC = iHighest(high,highSize,nVarP,i+nVarP*2)-iLowest(low,lowSize,nVarP,i+nVarP*2);
                        if (nVarC == 0 && nVarP == 1)
                             nVarR3 = MathAbs(close[i+nVarP*2]-close[i+nVarP*3]);
                        else nVarR3 = nVarC;

            nVarD = iHighest(high,highSize,nVarP,i+nVarP*3)-iLowest(low,lowSize,nVarP,i+nVarP*3);
                        if (nVarD == 0 && nVarP == 1)
                             nVarR4 = MathAbs(close[i+nVarP*3]-close[i+nVarP*4]);
                        else nVarR4 = nVarD;

            nVarE = iHighest(high,highSize,nVarP,i+nVarP*4)-iLowest(low,lowSize,nVarP,i+nVarP*4);
                        if (nVarE == 0 && nVarP == 1)
                             nVarR5 = MathAbs(close[i+nVarP*4]-close[i+nVarP*5]);
                        else nVarR5 = nVarE;

            nLRange = ((nVarR1+nVarR2+nVarR3+nVarR4+nVarR5)/5.0)*0.2;
   	         
            //
            //
            //
            //
            //
               	         
	         if ( nLRange <= 0 ) continue;
            double hlAverage = 0;
               for (int k=0;(i+k)<highSize && k<nBars; k++) hlAverage += (high[i+k]+low[i+k])/2.0;
                                                            hlAverage /= nBars;
            
            double nOpen  = (open[i]  - hlAverage) / nLRange;
	         double nHigh  = (high[i]  - hlAverage) / nLRange;
	         double nLow   = (low[i]   - hlAverage) / nLRange;
	         double nClose = (close[i] - hlAverage) / nLRange;	

                  vcbhBuffer[i] = nHigh;
                  vcblBuffer[i] = nLow;
                  vcboBuffer[i] = nOpen;
                  vcbcBuffer[i] = nClose;
                  vchoBuffer[i] = nOpen;
                  vchcBuffer[i] = nClose;
            if (nOpen <nClose) colsBuffer[i] = 0;
            if (nOpen >nClose) colsBuffer[i] = 1;
            if (nOpen==nClose) colsBuffer[i] = 2;
            
               fluaBuffer[i] =  8;
               flubBuffer[i] =  4;
               fldaBuffer[i] = -4;
               fldbBuffer[i] = -8;
      
         //
         //
         //
         //
         //
         
         if (inpChartType==chtLine) prcsBuffer[i] = getPrice(i,inpLinePrice);
         if (inpMa1Period>1)        cal1Buffer[i] = getPrice(i,inpMa1Price);
         if (inpMa2Period>1)        cal2Buffer[i] = getPrice(i,inpMa2Price);
   }
   if (inpMa1Period>1) doiMAOnArray(mav1Buffer,cal1Buffer,inpMa1Period,inpMa1Method,rates_total,prev_calculated,limit);
   if (inpMa2Period>1) doiMAOnArray(mav2Buffer,cal2Buffer,inpMa2Period,inpMa2Method,rates_total,prev_calculated,limit);
   
   //
   //
   //
   //
   //
   
   return(rates_total);
}



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iHighest(const double& array[], int size, int period, int i)
{
   if (i>=size) return(0);
   double max  = array[i];
         for (int k=1;(i+k)<size && k<period; k++) if (max<array[i+k]) max = array[i+k];
   return(max);
}
double iLowest(const double& array[],int size, int period, int i)
{
   if (i>=size) return(0);
   double min  = array[i];
         for (int k=1;(i+k)<size && k<period; k++) if (min>array[i+k]) min = array[i+k];
   return(min);
}
double getPrice(int i, ENUM_APPLIED_PRICE forPrice)
{
   double price = 0;
   switch (forPrice)
   {
      case PRICE_CLOSE    : price = vcbcBuffer[i]; break;
      case PRICE_OPEN     : price = vcboBuffer[i];  break;
      case PRICE_HIGH     : price = vcbhBuffer[i];  break;
      case PRICE_LOW      : price = vcblBuffer[i];   break;
      case PRICE_MEDIAN   : price = (vcbhBuffer[i]+vcblBuffer[i])/2.0; break;
      case PRICE_TYPICAL  : price = (vcbhBuffer[i]+vcblBuffer[i]+vcbcBuffer[i])/3.0; break;
      case PRICE_WEIGHTED : price = (vcbhBuffer[i]+vcblBuffer[i]+vcbcBuffer[i]+vcbcBuffer[i])/4.0; break;
   }
   return(price);             
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void doiMAOnArray(double& targetArray[], double& sourceArray[], int period, int mode, int total, int prev,int limit)
{
   switch(mode)
   {
      case MODE_SMA  : iSMAOnBuffer (total,limit,period,sourceArray,targetArray); break;
      case MODE_EMA  : iEMAOnBuffer (total,limit,period,sourceArray,targetArray); break;
      case MODE_SMMA : iSMMAOnBuffer(total,limit,period,sourceArray,targetArray); break;
      case MODE_LWMA : iLWMAOnBuffer(total,limit,period,sourceArray,targetArray); break;
   }
}

//
//
//
//
//

void iSMAOnBuffer(const int total, const int limit, const int period, const double& price[], double& buffer[])
{
   bool as_series_price  = ArrayGetAsSeries(price);
   bool as_series_buffer = ArrayGetAsSeries(buffer);

   if(!as_series_price)  ArraySetAsSeries(price, true);
   if(!as_series_buffer) ArraySetAsSeries(buffer,true);

   //
   //
   //
   //
   //

   for (int i=limit; i>=0; i--)
   {
      if (i>(total-period)) buffer[i] = price[i];
      else
      {
         double sum = 0;
            for (int l=0; l<period; l++) sum += price[i+l];
         buffer[i] = (sum/period);
      }
   }
   ArraySetAsSeries(buffer, as_series_buffer);   
   ArraySetAsSeries(price,  as_series_price);   
}

//
//
//
//
//

void iEMAOnBuffer(const int total, const int limit, const int period, const double& price[], double& buffer[])
{
   bool as_series_price  = ArrayGetAsSeries(price);
   bool as_series_buffer = ArrayGetAsSeries(buffer);

   if(!as_series_price)  ArraySetAsSeries(price, true);
   if(!as_series_buffer) ArraySetAsSeries(buffer,true);

   //
   //
   //
   //
   //

   double alpha = 2.0/(1.0+period);
   for (int i=limit; i>=0; i--)
   {
      if (i>(total-period)) buffer[i] = price[i];
      else                  buffer[i] = buffer[i+1]+alpha*(price[i]-buffer[i+1]);
   }
   ArraySetAsSeries(buffer, as_series_buffer);   
   ArraySetAsSeries(price,  as_series_price);   
}

//
//
//
//
//

void iSMMAOnBuffer(const int total, const int limit, const int period, const double& price[], double& buffer[])
{
   bool as_series_price  = ArrayGetAsSeries(price);
   bool as_series_buffer = ArrayGetAsSeries(buffer);

   if(!as_series_price)  ArraySetAsSeries(price, true);
   if(!as_series_buffer) ArraySetAsSeries(buffer,true);

   //
   //
   //
   //
   //

   for (int i=limit; i>=0; i--)
   {
      if (i>(total-period))
      {
         int l=0;
         double sum = 0; for(;(i+l)<total;l++) sum += price[i+l];
         if (l>0)
               buffer[i] = sum/l;
         else  buffer[i] = price[i];
      }
      else buffer[i] = (buffer[i+1]*(period-1)+price[i])/period;
   }
   ArraySetAsSeries(buffer, as_series_buffer);   
   ArraySetAsSeries(price,  as_series_price);   
}

//
//
//
//
//

void iLWMAOnBuffer(const int total, const int limit, const int period, const double& price[], double& buffer[])
{
   bool as_series_price  = ArrayGetAsSeries(price);
   bool as_series_buffer = ArrayGetAsSeries(buffer);

   if(!as_series_price)  ArraySetAsSeries(price, true);
   if(!as_series_buffer) ArraySetAsSeries(buffer,true);

   //
   //
   //
   //
   //

   for (int i=limit; i>=0; i--)
   {
      if (i>(total-period)) buffer[i] = price[i];
      else
      {
         double sum    = 0;
         double weight = 0;
            for (int l=0,k=period; l<period; l++,k--)
            {
               weight += k; sum += price[i+l]*k;
            }
            if (weight !=0)
                 buffer[i] = (sum/weight);
            else buffer[i] = 0.0;
      }         
   }
   ArraySetAsSeries(buffer, as_series_buffer);   
   ArraySetAsSeries(price,  as_series_price);   
}
