
enum enPrices
{
   pr_close, pr_open, pr_high, pr_low,
   pr_median, pr_typical, pr_weighted, pr_average,
   pr_medianb, pr_tbiased, pr_tbiased2,
   pr_haclose, pr_haopen , pr_hahigh, pr_halow,
   pr_hamedian, pr_hatypical, pr_haweighted, pr_haaverage,
   pr_hamedianb, pr_hatbiased, pr_hatbiased2,
   pr_habclose, pr_habopen , pr_habhigh, pr_hablow,
   pr_habmedian, pr_habtypical, pr_habweighted, pr_habaverage,
   pr_habmedianb, pr_habtbiased, pr_habtbiased2
};

#define _prHABF(_prtype) (_prtype>=pr_habclose && _prtype<=pr_habtbiased2)
#define _priceInstances     1
#define _priceInstancesSize 4

double workHa[][_priceInstances*_priceInstancesSize];

extern enPrices PriceType = pr_close;

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(workHa,0)!= bars) ArrayResize(workHa,bars); instanceNo*=_priceInstancesSize; int r = bars-i-1;
         
         double haOpen  = (r>0) ? (workHa[r-1][instanceNo+2] + workHa[r-1][instanceNo+3])/2.0 : (open[i]+close[i])/2;;
         double haClose = (open[i]+high[i]+low[i]+close[i]) / 4.0;
         if (_prHABF(tprice))
               if (high[i]!=low[i])
                     haClose = (open[i]+close[i])/2.0+(((close[i]-open[i])/(high[i]-low[i]))*MathAbs((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) { workHa[r][instanceNo+0] = haLow;  workHa[r][instanceNo+1] = haHigh; } 
         else               { workHa[r][instanceNo+0] = haHigh; workHa[r][instanceNo+1] = haLow;  } 
                              workHa[r][instanceNo+2] = haOpen;
                              workHa[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);
}

//------------------------------------------------------------------
#property copyright "www.forex-station.com"
#property link      "www.forex-station.com"
//------------------------------------------------------------------
#property indicator_separate_window
#property indicator_buffers 8
#property indicator_color5  Red
#property indicator_color6  Blue
#property indicator_style5  STYLE_SOLID
#property indicator_style6  STYLE_SOLID
#property indicator_color7  Red
#property indicator_color8  Blue
#property indicator_style7  STYLE_SOLID
#property indicator_style8  STYLE_SOLID


extern int    DrawBars      = 800;
extern int    BBPeriod      = 27;
extern int    BBPrice       = 0;
extern double BBDeviations1 =  1.7;
extern double BBDeviations2  = 3.0;
extern color  WickColor     = Black;
extern color  BodyUpColor   = Chartreuse;
extern color  BodyDownColor = Red;
extern int    BodyWidth     = 3;
extern bool   DrawAsBack    = false;
extern string UniqueID      = "BB bars 1";
extern bool   TouchesVisible = true;
extern color  TouchUpColor1 = Aqua;
extern color  TouchUpColor2 = Aqua;
extern color  TouchDnColor1 = Aqua;
extern color  TouchDnColor2 = Aqua;
extern double TouchesTolerance = 8;
extern int    ArrowsSize       = 1;
extern int    ArrowsCode       = 3;

//
//
//
//
//

double open[];
double close[];
double high[];
double low[];
double bandUp[];
double bandDn[];
double bandUp2[];
double bandDn2[];
int    window;
double tolerance;

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

int init()
{
   for (int i=0; i<indicator_buffers; i++) SetIndexStyle(i,DRAW_LINE);
   SetIndexBuffer(0,open);  SetIndexStyle(0,DRAW_NONE);
   SetIndexBuffer(1,close); SetIndexStyle(1,DRAW_NONE);
   SetIndexBuffer(2,high);  SetIndexStyle(2,DRAW_NONE);
   SetIndexBuffer(3,low);   SetIndexStyle(3,DRAW_NONE);
   SetIndexBuffer(4,bandUp);
   SetIndexBuffer(5,bandDn);
   SetIndexBuffer(6,bandUp2);
   SetIndexBuffer(7,bandDn2);
	IndicatorShortName(UniqueID); 
	  tolerance = MathMax(TouchesTolerance,0);
   return(0);
}

//
//
//
//
//

int deinit()
{
   string lookFor       = UniqueID+":";
   int    lookForLength = StringLen(lookFor);
   for (int i=ObjectsTotal()-1; i>=0; i--)
   {
      double price = getPrice(PriceType, open, close, high, low, i, Bars);
   {
      string objectName = ObjectName(i); if (StringSubstr(objectName,0,lookForLength) == lookFor) ObjectDelete(objectName);
   }
   return(0);
}

//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//

int start()
{
   int countedBars = IndicatorCounted();
      if (countedBars<0) return(-1);
      if (countedBars>0) countedBars--;
         int drawBars = DrawBars; if (drawBars<1) drawBars = Bars;
         int limit = MathMin(MathMin(Bars-countedBars,Bars-1),drawBars);
            window = WindowFind(UniqueID);

   //
   //
   //
   //
   //
   
   for (int i=limit; i>=0; i--)
   {
      double price = getPrice(PriceType, open, close, high, low, i, Bars);
   {
   
   
   
      
      double udeviation = iStdDev(NULL,0,BBPeriod,0,0,0,i);
      double ddeviation = iStdDev(NULL,0,BBPeriod,0,0,0,i);
      
      double ma = iMA(NULL,0,BBPeriod,0,MODE_SMA,BBPrice,i);
         bandUp[i]  =  BBDeviations1*udeviation;
         bandDn[i]  = -BBDeviations1*ddeviation;
         bandUp2[i] =  BBDeviations2*udeviation;
         bandDn2[i] = -BBDeviations2*ddeviation;
         open[i]    = Open[i] -ma;
         close[i]   = Close[i]-ma;
         high[i]    = High[i] -ma;
         low[i]     = Low[i]  -ma;
         drawCandle(i);
         if (TouchesVisible) drawTouches(i);
   }
   return(0);
}

  
//------------------------------------------------------------------
//
//------------------------------------------------------------------
//
//
//
//
//

void drawTouches(int i)  
{
   datetime time = Time[i];
   string name = UniqueID+":"+time+":t";
      ObjectDelete(name+"u1");
      ObjectDelete(name+"u2");
      ObjectDelete(name+"d1");
      ObjectDelete(name+"d2");
         if (safeDivide(high[i],bandUp[i] ,tolerance)) drawDot(name+"u1",time,TouchUpColor1,bandUp[i]);
         if (safeDivide(high[i],bandUp2[i],tolerance)) drawDot(name+"u2",time,TouchUpColor2,bandUp2[i]);
         if (safeDivide(low[i] ,bandDn[i] ,tolerance)) drawDot(name+"d1",time,TouchDnColor1,bandDn[i]);
         if (safeDivide(low[i] ,bandDn2[i],tolerance)) drawDot(name+"d2",time,TouchDnColor2,bandDn2[i]);
}
bool safeDivide(double value1, double value2, double tol)
{
   if (value2!=0)
         return(100.0*(MathAbs(value1-value2)/MathAbs(value2))<=tol);
   else  return(false);    
}
void drawDot(string name, datetime time, color theColor, double atValue)
{
      ObjectCreate(name,OBJ_ARROW,window,0,0,0,0);
         ObjectSet(name,OBJPROP_COLOR,theColor);
         ObjectSet(name,OBJPROP_TIME1,time);
         ObjectSet(name,OBJPROP_PRICE1,atValue);
         ObjectSet(name,OBJPROP_BACK,DrawAsBack);
         ObjectSet(name,OBJPROP_ARROWCODE,ArrowsCode);
         ObjectSet(name,OBJPROP_WIDTH,ArrowsSize);
}

//
//
//
//
//

void drawCandle(int i)
{
   datetime time = Time[i];
   string   name = UniqueID+":"+time+":";
   
      ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
         ObjectSet(name,OBJPROP_COLOR,WickColor);
         ObjectSet(name,OBJPROP_TIME1,time);
         ObjectSet(name,OBJPROP_TIME2,time);
         ObjectSet(name,OBJPROP_PRICE1,MathMax(high[i],MathMin(close[i],open[i])));
         ObjectSet(name,OBJPROP_PRICE2,MathMin(low[i] ,MathMax(close[i],open[i])));
         ObjectSet(name,OBJPROP_RAY ,false);
         ObjectSet(name,OBJPROP_BACK,DrawAsBack);
      
   //
   //
   //
   //
   //
         
   name = name+"body";
      ObjectCreate(name,OBJ_TREND,window,0,0,0,0);
         ObjectSet(name,OBJPROP_TIME1,time);
         ObjectSet(name,OBJPROP_TIME2,time);
         ObjectSet(name,OBJPROP_PRICE1,open[i]);
         ObjectSet(name,OBJPROP_PRICE2,close[i]);
         ObjectSet(name,OBJPROP_WIDTH,BodyWidth);
         ObjectSet(name,OBJPROP_RAY  ,false);
         ObjectSet(name,OBJPROP_BACK,DrawAsBack);
         if (open[i]<close[i])
               ObjectSet(name,OBJPROP_COLOR,BodyUpColor);
         else  ObjectSet(name,OBJPROP_COLOR,BodyDownColor);
}