//+------------------------------------------------------------------+
//|                                                  Value chart.mq4 |
//|                                                                  |
//| Helweg/Stendahl value charts                                     |
//+------------------------------------------------------------------+
#property copyright "mladen"
#property link      "mladenfx@gmail.com"

#property indicator_separate_window
#property indicator_buffers    5
#property indicator_color3     Gold
#property indicator_color4     PaleVioletRed
#property indicator_color5     DeepSkyBlue
#property indicator_width4     1
#property indicator_width5     1
#property indicator_level1     -8.0
#property indicator_level2     -4.0
#property indicator_level4     4.0
#property indicator_level5     8.0
#property indicator_levelcolor MediumOrchid

//
//
//
//
//

extern int    magicNumber      = 1;
extern int    numberOfBars     = 70;
extern bool   showLineChart    = false;
extern string _                = "Bars settings";
extern color  colorBarDown     = Red;
extern color  colorBarUp       = LimeGreen;
extern color  colorBarNeutral  = Snow;
extern color  colorWickUp      = LimeGreen;
extern color  colorWickDown    = Red;
extern color  colorWickNeutral = Snow;
extern int    widthWick        = 1;
extern int    widthBody        = 3;
extern string __               = "Line chart settings";
extern int    linePrice        = PRICE_CLOSE;
extern string ___              = "Oma averages settings";
extern int    OmaLength1       = 20;
extern double OmaSpeed1        = 8.0;
extern bool   OmaAdaptive1     = true;
extern int    OmaLength2       = 50;
extern double OmaSpeed2        = 8.0;
extern bool   OmaAdaptive2     = true;

//
//
//
//
//

extern bool   alertsOnOmasCross = true;
extern bool   alertsOn          = true;
extern bool   alertsOnCurrent   = true;
extern bool   alertsMessage     = true;
extern bool   alertsSound       = false;
extern bool   alertsEmail       = false;

//
//
//
//
//

string name;
int    window;
double hiBuffer[];
double loBuffer[];
double prBuffer[];
double paBuffer[];
double pbBuffer[];
double maBuffer[];
double mbBuffer[];
double vaBuffer[];
double trend[];

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

int init()
{
   IndicatorBuffers(8);
      SetIndexBuffer(0,hiBuffer); SetIndexStyle(0,DRAW_NONE);
      SetIndexBuffer(1,loBuffer); SetIndexStyle(1,DRAW_NONE);
      SetIndexBuffer(2,prBuffer);
      SetIndexBuffer(3,maBuffer); SetIndexDrawBegin(3,OmaLength1);
      SetIndexBuffer(4,mbBuffer); SetIndexDrawBegin(4,OmaLength2);
         if (showLineChart)
               SetIndexStyle(2,DRAW_LINE);
         else  SetIndexStyle(2,DRAW_NONE);
         if (OmaLength1>1)
               SetIndexStyle(3,DRAW_LINE);
         else  SetIndexStyle(3,DRAW_NONE);
         if (OmaLength2>1)
               SetIndexStyle(4,DRAW_LINE);
         else  SetIndexStyle(4,DRAW_NONE);
      SetIndexBuffer(5,paBuffer);
      SetIndexBuffer(6,pbBuffer);
      SetIndexBuffer(7,vaBuffer);
      
      OmaLength1 = MathMax(OmaLength1,1);
      OmaLength2 = MathMax(OmaLength2,1);
      OmaSpeed1  = MathMax(OmaSpeed1,-1.5);
      OmaSpeed2  = MathMax(OmaSpeed2,-1.5);

   //
   //
   //
   //
   //
   
   numberOfBars = MathMax(numberOfBars,5);
   name = "Value chart Oma ("+numberOfBars+","+magicNumber+")";   
      IndicatorShortName(name);
   return (0);
}

int deinit()
{
   string oName;
   for (int i = ObjectsTotal() - 1; i >= 0; i--)
   {
      oName = ObjectName(i); if (StringFind(oName, name, 0) >= 0) ObjectDelete(oName);
   }
   return (0);
}

//
//
//
//
//

int start()
{
   int    counted_bars=IndicatorCounted();
   int    i,k,r,limit;

   if(counted_bars<0) return(-1);
   if(counted_bars>0) counted_bars--;
         limit  = MathMin(Bars-counted_bars,Bars-2);
         window = WindowFind(name);

         if (counted_bars == 0)
         {
            for (i=0;i<OmaLength1;i++) { paBuffer[Bars-i-1] = 0; maBuffer[Bars-i-1] = 0; }
            for (i=0;i<OmaLength2;i++) { pbBuffer[Bars-i-1] = 0; mbBuffer[Bars-i-1] = 0; }
         }
         if (ArraySize(trend)!=Bars) ArrayResize(trend,Bars);
         
   //
   //
   //
   //
   //

      int    nVarP = MathRound(numberOfBars/5.0);
      double nVar0,nVarA,nVarB,nVarC,nVarD,nVarE;
      double nVarR1,nVarR2,nVarR3,nVarR4,nVarR5;
      double nLRange;
      for (i=limit, r=Bars-i-1; i>=0; i--,r++)
      {
         //
         //
         //
         //
         //
         
         if (numberOfBars<7)
         {            
               if (MathAbs(Close[i]-Close[i+1]) >= (High[i]-Low[i]))
                     nVarA = MathAbs(Close[i]-Close[i+1]);
               else  nVarA = (High[i]-Low[i]);
               vaBuffer[i] = nVarA;
               
               //
               //
               //
               //
               //
               
               double average=0;
               for (k=0; k<5; k++) average += vaBuffer[i];
                                   average /= 5;
               nLRange = average*0.2;
         }
         else
         {            
            nVarA = iHighesti(nVarP,i)-iLowesti(nVarP,i);
                        if  (nVarA == 0 && nVarP == 1)
                             nVarR1 = MathAbs(Close[i]-Close[i+nVarP]);
                        else nVarR1 = nVarA;                      
                     
            nVarB = iHighesti(nVarP,i+nVarP+1)-iLowesti(nVarP,i+nVarP);
                        if (nVarB == 0 && nVarP == 1)
                             nVarR2 = MathAbs(Close[i+nVarP]-Close[i+nVarP*2]);
                        else nVarR2 = nVarB;

            nVarC = iHighesti(nVarP,i+nVarP*2)-iLowesti(nVarP,i+nVarP*2);
                        if (nVarC == 0 && nVarP == 1)
                             nVarR3 = MathAbs(Close[i+nVarP*2]-Close[i+nVarP*3]);
                        else nVarR3 = nVarC;

            nVarD = iHighesti(nVarP,i+nVarP*3)-iLowesti(nVarP,i+nVarP*3);
                        if (nVarD == 0 && nVarP == 1)
                             nVarR4 = MathAbs(Close[i+nVarP*3]-Close[i+nVarP*4]);
                        else nVarR4 = nVarD;

            nVarE = iHighesti(nVarP,i+nVarP*4)-iLowesti(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 (k=0; k<numberOfBars; k++) hlAverage += (High[i+k]+Low[i+k])/2.0;
                                              hlAverage /= numberOfBars;
            
               double nOpen  = (Open[i]  - hlAverage) / nLRange;
               double nHigh  = (High[i]  - hlAverage) / nLRange; hiBuffer[i] = nHigh;
	            double nLow   = (Low[i]   - hlAverage) / nLRange; loBuffer[i] = nLow;
	            double nClose = (Close[i] - hlAverage) / nLRange;	

            //
            //
            //
            //
            //	         

            if (showLineChart==false)
            {	         
               color  theBarColor  = colorBarNeutral;
               color  theWickColor = colorWickNeutral;
                  if (nClose<nOpen) { theBarColor = colorBarDown; theWickColor = colorWickDown; }
                  if (nClose>nOpen) { theBarColor = colorBarUp;   theWickColor = colorWickUp;   }
               
                  drawBar(Time[i],nHigh,nLow,nOpen,nClose,theBarColor,theWickColor);
            }
            else prBuffer[i] = getPrice(linePrice,nHigh,nLow,nOpen,nClose);
            if (OmaLength1>1) paBuffer[i] = getPrice(0,nHigh,nLow,nOpen,nClose);
            if (OmaLength2>1) pbBuffer[i] = getPrice(0,nHigh,nLow,nOpen,nClose);

            //
            //
            //
            //
            //
            
            if (!alertsOnOmasCross)
            {
               trend[r] = trend[r-1];
                  if (nHigh> 8)           trend[r] =  1;
                  if (nLow <-8)           trend[r] = -1;
                  if (nLow>-8 && nHigh<8) trend[r] =  0;
            }                  
      }
      
      if (OmaLength1>1) for (i=limit; i>=0; i--) maBuffer[i] = iSmooth(paBuffer[i],OmaLength1,OmaSpeed1,OmaAdaptive1,i,0);
      
      for(i=limit,r=Bars-i-1; i>=0; i--,r++) 
      {
      if (OmaLength2>1) mbBuffer[i] = iSmooth(pbBuffer[i],OmaLength2,OmaSpeed2,OmaAdaptive2,i,7); 
          
          
          if (alertsOnOmasCross)
          {
             trend[r] = trend[r-1];
               if (maBuffer[i]>mbBuffer[i]) trend[r] =  1;
               if (maBuffer[i]<mbBuffer[i]) trend[r] = -1;
         }                  
      }       
   
   //
   //
   //
   //
   //
   
   if (alertsOn)
   {
      int forBar = (alertsOnCurrent != true);
    
         //
         //
         //
         //
         //
         
         r = Bars-forBar-1;
         if (trend[r] != trend[r-1])
         {
            if (alertsOnOmasCross)
            {
              Comment(trend[r]);
              if (trend[r] == 1)
                     doAlert("oma "+OmaLength1+" crossed oma "+OmaLength2+" up");
               else  doAlert("oma "+OmaLength1+" crossed oma "+OmaLength2+" down");             
            }            
            else
            {
              if (trend[r] == 1)
                     doAlert("broke level 8 up");
               else  doAlert("broke level -8 down");             
            }               
         }               
   }
   return(0);
}



//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void doAlert(string doWhat)
{
   static string   previousAlert="nothing";
   static datetime previousTime;
   string message;
   
   if (previousAlert != doWhat || previousTime != Time[0]) {
       previousAlert  = doWhat;
       previousTime   = Time[0];

       //
       //
       //
       //
       //

       message =  StringConcatenate(Symbol()," at ",TimeToStr(TimeLocal(),TIME_SECONDS)," value chart ",doWhat);
          if (alertsMessage) Alert(message);
          if (alertsEmail)   SendMail(StringConcatenate(Symbol(),"value chart "),message);
          if (alertsSound)   PlaySound("alert2.wav");
   }
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

double iHighesti(int period, int i)
{
   if (i>=Bars) return(0);
   double max  = High[i];
         for (int k=1;(i+k)<Bars && k<period; k++) if (max<High[i+k]) max = High[i+k];
   return(max);
}
double iLowesti(int period, int i)
{
   if (i>=Bars) return(0);
   double min  = Low[i];
         for (int k=1;(i+k)<Bars && k<period; k++) if (min>Low[i+k]) min = Low[i+k];
   return(min);
}

//
//
//
//
//

double getPrice(int priceType, double high, double low, double open, double close)
{
   double price = 0;
   switch (priceType)
   {
      case PRICE_CLOSE    : price = close; break;
      case PRICE_OPEN     : price = open;  break;
      case PRICE_HIGH     : price = high;  break;
      case PRICE_LOW      : price = low;   break;
      case PRICE_MEDIAN   : price = (high+low)/2.0; break;
      case PRICE_TYPICAL  : price = (high+low+close)/3.0; break;
      case PRICE_WEIGHTED : price = (high+low+close+close)/4.0;
   }               
   return(price);
}


//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//

void drawBar(int bTime, double prHigh, double prLow, double prOpen, double prClose, color barColor, color wickColor)
{
   string oName;
          oName = name+TimeToStr(bTime)+"w";
            if (ObjectFind(oName) < 0) ObjectCreate(oName,OBJ_TREND,window,bTime,0,bTime,0);
                 ObjectSet(oName, OBJPROP_PRICE1, prHigh);
                 ObjectSet(oName, OBJPROP_PRICE2, prLow);
                 ObjectSet(oName, OBJPROP_COLOR, wickColor);
                 ObjectSet(oName, OBJPROP_WIDTH, widthWick);
                 ObjectSet(oName, OBJPROP_RAY, false);
                 ObjectSet(oName, OBJPROP_BACK, true);
           
         oName = name+TimeToStr(bTime)+"b";
            if (ObjectFind(oName) < 0)ObjectCreate(oName,OBJ_TREND,window,bTime,0,bTime,0);
                 ObjectSet(oName, OBJPROP_PRICE1, prOpen);
                 ObjectSet(oName, OBJPROP_PRICE2, prClose);
                 ObjectSet(oName, OBJPROP_COLOR, barColor);
                 ObjectSet(oName, OBJPROP_WIDTH, widthBody);
                 ObjectSet(oName, OBJPROP_RAY, false);
                 ObjectSet(oName, OBJPROP_BACK, true);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
//
//
//
//
//
              
double stored[][14];
#define E1  0
#define E2  1
#define E3  2
#define E4  3
#define E5  4
#define E6  5
#define res 6

//
//
//
//
//

double iSmooth(double price, double averagePeriod, double const, bool adaptive, int i, int ashift=0)
{
   if (ArrayRange(stored,0) != Bars) ArrayResize(stored,Bars);
   if (averagePeriod <=1) return(price);
   int r = Bars-i-1; 
   
   double e1=stored[r-1][E1+ashift];  double e2=stored[r-1][E2+ashift];
   double e3=stored[r-1][E3+ashift];  double e4=stored[r-1][E4+ashift];
   double e5=stored[r-1][E5+ashift];  double e6=stored[r-1][E6+ashift];

   //
   //
   //
   //
   //

      if (adaptive && (averagePeriod > 1))
      {
         double minPeriod = averagePeriod/2.0;
         double maxPeriod = minPeriod*5.0;
         int    endPeriod = MathCeil(maxPeriod);
         double signal    = MathAbs((price-stored[r-endPeriod][res+ashift]));
         double noise     = 0.00000000001;

            for(int k=1; k<endPeriod; k++) noise=noise+MathAbs(price-stored[r-k][res+ashift]);

         averagePeriod = ((signal/noise)*(maxPeriod-minPeriod))+minPeriod;
      }
      
      //
      //
      //
      //
      //
      
      double alpha = (2.0+const)/(1.0+const+averagePeriod);

      e1 = e1 + alpha*(price-e1); e2 = e2 + alpha*(e1-e2); double v1 = 1.5 * e1 - 0.5 * e2;
      e3 = e3 + alpha*(v1   -e3); e4 = e4 + alpha*(e3-e4); double v2 = 1.5 * e3 - 0.5 * e4;
      e5 = e5 + alpha*(v2   -e5); e6 = e6 + alpha*(e5-e6); double v3 = 1.5 * e5 - 0.5 * e6;

   //
   //
   //
   //
   //

   stored[r][E1+ashift]  = e1;  stored[r][E2+ashift] = e2;
   stored[r][E3+ashift]  = e3;  stored[r][E4+ashift] = e4;
   stored[r][E5+ashift]  = e5;  stored[r][E6+ashift] = e6;
   stored[r][res+ashift] = price;
   return(v3);
}


