//+------------------------------------------------------------------+
//|                                             MarketProfileDWM.mq4 |
//+------------------------------------------------------------------+

#property indicator_chart_window



//---extern vars
extern int        LookBack                = 6;
extern string     ProfileTimeframeInfo    = "use W, M or D";
extern string     ProfileTimeframe        = "D";
extern int        HistoHeight             = 2;
extern color      HistoColor1             = C'15,15,15';
extern color      HistoColor2             = C'25,25,25';
extern bool       ShowOpenCloseArrow      = True;
extern color      OpenColor               = White;
extern color      CloseColor              = White;
extern color      POCColor                = Peru;
extern color      VAColor                 = Black;
extern double     VATPOPercent            = 70.0;
extern int        ExtendedPocLines        = 6;

extern string     ProfileData             = "............";
extern int        DailyProfileDataTf      = 30;
extern int        WeeklyProfileDataTf     = 60;
extern int        MonthlyProfileDataTf    = 240;

//---global vars
string            gsPref                  = "MarketProfile";
double            fpoint
                , gdOneTick
                , gdHistoRange;
int               fdigits
                , giStep
                , giProfileTf             = PERIOD_D1
                , giDataTf                = 0;

//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int init()
  {       
      
   giDataTf = Period(); //default
   
   if (Point == 0.001 || Point == 0.00001) 
   { fpoint = Point*10; fdigits = Digits - 1; }
   else 
   { fpoint = Point; fdigits = Digits; }     
   
   //---- 
   if (ProfileTimeframe == "M" )   
   {
      gsPref      = gsPref + "2.0." + ProfileTimeframe + "."; 
      giProfileTf = PERIOD_MN1;     
      HistoHeight = MathMax(HistoHeight, 8);
      giDataTf    = MonthlyProfileDataTf;
   }   
   else if (ProfileTimeframe == "W" )
   {
      gsPref      = gsPref + "3.0." + ProfileTimeframe + "."; 
      giProfileTf = PERIOD_W1;     
      HistoHeight = MathMax(HistoHeight, 3);
      giDataTf    = WeeklyProfileDataTf;

   }
   else //default D1
   {
      gsPref      = gsPref + "4.0." + ProfileTimeframe + "."; 
      giProfileTf = PERIOD_D1;
      HistoHeight = MathMax(HistoHeight, 1);
      giDataTf    = DailyProfileDataTf;
   }
   //----
   
   gdOneTick    = 1/(MathPow(10,fdigits));
   gdHistoRange = HistoHeight/(MathPow(10,fdigits)); 
   giStep       = HistoHeight;
   
//----
   return(0);
  }
//+------------------------------------------------------------------+
//| Custom indicator deinitialization function                       |
//+------------------------------------------------------------------+
int deinit()
{
//----
   delObjs();
//----
   return(0);
}

void getPriceTPO
(
     int    ibar.proftf
   , int    &dt.proftf
   , int    &startbar
   , double &aprice.step[][2]
   , int    &countps
   , int    &amax.tpo[2]
   , int    &totaltpo
)
{

   dt.proftf                      = iTime     (NULL, giProfileTf, ibar.proftf);
   startbar                       = iBarShift (NULL, giDataTf, dt.proftf);
   double     hh                  = iHigh     (NULL, giProfileTf, ibar.proftf)
            , ll                  = iLow      (NULL, giProfileTf, ibar.proftf)   
            , profile.range       = hh - ll
            , mid.profile.price   = hh - (0.5 * profile.range);
   totaltpo                       = 0.0;
   
   //if (nhh < hh) nhh += gdHistoRange; 
   // fix ibarshift on month\week change
   if ( iTime(NULL, giDataTf, startbar) < dt.proftf ) startbar--;
 
   //--- populate price level     
   countps  = 0;          
   while (hh >= ll)
   {   
      ArrayResize(aprice.step, countps+1);   
      aprice.step [countps][0] = hh;
      aprice.step [countps][1] = 0;
      
      //nhh -= gdHistoRange;
      hh -= gdOneTick;
      countps  ++;

   } // end while (nhh >= nll)
   
   //--- end populate price level                
   
   //--- Counting tpo     
   int      i, j
          , maxtpo=0, maxtpoidx=0;
   double   price.step;
   for (i=0; i<countps; i++)
   {
      price.step = aprice.step[i][0];
      j = startbar;
      while ( iBarShift( NULL, giProfileTf, iTime(NULL, giDataTf, j) ) == ibar.proftf )
      {            
         if (  price.step >= iLow (NULL, giDataTf, j) 
            && price.step <= iHigh(NULL, giDataTf, j) )   
         {
            aprice.step[i][1] += 1;  
            totaltpo += 1; 
            // save maxtpo
            if (aprice.step[i][1] > maxtpo) 
            {
               maxtpo    = aprice.step[i][1];
               maxtpoidx = i;               
            }
            
            if (aprice.step[i][1] == maxtpo) 
            {
               // take the closes to the middle of profile range
               if (   MathAbs(mid.profile.price - aprice.step[i][0]) 
                    < MathAbs(mid.profile.price - aprice.step[maxtpoidx][0]) 
                   )
               {
                  maxtpo    = aprice.step[i][1];
                  maxtpoidx = i;
               }
            }
            // end save maxtpo
                 
         } // end if (price-gdHistoRange <= High[j] && price >= Low[j])   
         
         j--;
      } // end while ( iBarShift(NULL, giProfileTf, Time[j]) == itfsource )
      
      //----end Counting tpo     
      
   }// end for (i=0; i<countps; i++)   
   
   amax.tpo[0] = maxtpoidx;
   amax.tpo[1] = maxtpo;

}


//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
int start()
{
   if (Period() > PERIOD_H4 || Period() < PERIOD_M5) return (-1); 
   
   int ibar.proftf = 0, endbar.proftf = 0;
      
   //---create all profile on startup/new tfsrc bar
   //---and then only update the last tfsrc profile
   if ( newBarProfileTf() ) { endbar.proftf = LookBack-1; } 
   
   //---delay 20 seconds
   if (!secondDiff(20)) return(0);
   
   //---main loop --> day by day, week by week, month by month...
   double     aprice.step[][2];            // [ 2-->{price,    count tpo} ]
   int        amax.tpo[2]   ;              // [ 2-->{priceidx, count tpo} ]
   double     vah                          // Value Area High
            , val;                         // Value Area Low

   datetime   dt.proftf;

   int        startbar                     // startbar on giDataTf
            , countps
            , totaltpo
            , vahidx
            , validx;   
         
   for (ibar.proftf = endbar.proftf; ibar.proftf >= 0; ibar.proftf--)      
   {
      //--skip sunday bar on Daily Profile
      if (giProfileTf == PERIOD_D1 && TimeDayOfWeek(iTime(NULL, giProfileTf, ibar.proftf)) == 0 ) continue;
                          
      getPriceTPO 
         ( ibar.proftf, dt.proftf, startbar, aprice.step, countps, amax.tpo, totaltpo );                         
      
      drawPriceHistoAndPOCLines 
         ( startbar, ibar.proftf, countps, aprice.step, amax.tpo );
         
      Sleep(100);
      
      getValueArea 
         ( countps, aprice.step, amax.tpo, totaltpo, vah, vahidx, val, validx );
      
      drawValueArea 
         ( startbar, ibar.proftf, countps, aprice.step, amax.tpo[0], vah, vahidx, val, validx );
 
   }//end for (ibartf = endbartf; ibartf >= 0; ibartf--)    
   
   
   if (!newBar()) return(0);
   //update time ExtendedPocLines
   for (int i=1; i<=ExtendedPocLines; i++)
   {
      ObjectSet(gsPref + "#" + i +".1.1.poc",       OBJPROP_TIME2, Time[0] + 10*Period()*60 );     
      ObjectSet(gsPref + "#" + i +".1.0.poc.price", OBJPROP_TIME1, Time[0] + 13*Period()*60 );
   }   
      
}
//+------------------------------------------------------------------+
//| drawPriceHistoAndPOCLines                                        |
//+------------------------------------------------------------------+
void drawPriceHistoAndPOCLines
(
     int    startbar
   , int    ibar.proftf
   , int    countps
   , double aprice.step[][]
   , int    amax.tpo[]
)
{
   double   price1, price2, dstep;
   int      numtpo; 
   int      step, i;
   int      chart.startbar = iBarShift( NULL, 0, iTime(NULL, giDataTf, startbar) );
   color    clr; 
   datetime t1 = Time[chart.startbar], t2;
   string   strdt.proftf = TimeToStr (iTime (NULL, giProfileTf, ibar.proftf), TIME_DATE);
   double   lprice = aprice.step[countps-1][0];
   
   //--- draw price histo
   if (ibar.proftf == 0) 
      delObjs( gsPref + "#" + ibar.proftf + ".histo.");  
       
   for (step=0; step<countps; step += giStep)      
   {
      price1   = aprice.step[step][0];
      price2   = aprice.step[step+(giStep)][0];
      numtpo   = 0;//aprice.step[step][1];
      
      for (i=step; i < step+giStep; i++)
         numtpo = MathMax( numtpo, aprice.step[i][1] );
      
      if (MathCeil(dstep/2) == dstep/2) clr = HistoColor1;
      else clr = HistoColor2;
      
      double x2 = ((giDataTf/1.0)/Period()) * numtpo;
            
      numtpo = MathCeil( x2 ) ;
            
      t2 = Time[chart.startbar - numtpo] ;
      //Time[ iBarShift(NULL, 0, iTime(NULL, giDataTf, startbar - numtpo)) ];
                  
      if (t2<=t1) t2 = t1 + (Period()*60);         
      
      createRect( "#" + ibar.proftf + ".histo." + DoubleToStr(price1,fdigits)
         , price1,                  t1
         , MathMax(price2, lprice), t2
         , clr ); 
         
      dstep += 1.0;                                        
   }      

   //--- draw poc lines
   t2 = iTime(NULL, giProfileTf, ibar.proftf-2);
      
   if (ibar.proftf <= ExtendedPocLines || ibar.proftf == 0) 
   {
      t2 = Time[0] + 10*Period()*60;
      createText( "#" + ibar.proftf +".1.0.poc.price"   
         , t2 + (3 * Period() * 60)
         , aprice.step[ amax.tpo[0] ][0]
         , addStr(
            ProfileTimeframe + "#" + ibar.proftf + ".POC "
            + StringSubstr( strdt.proftf, 2, 8 )+" "
            + DoubleToStr( aprice.step[ amax.tpo[0] ][0], fdigits )
            , " ", 0
            )
         , 8, "Arial Narrow", POCColor
         );                       
   } 
   
   createTl("#" + ibar.proftf + ".1.1.poc"
      , t1, aprice.step[ amax.tpo[0] ][0]
      , t2, aprice.step[ amax.tpo[0] ][0]
      , POCColor, STYLE_SOLID, 1
      );   
      
   //--- draw open and close
   if (!ShowOpenCloseArrow) return;
   double dopen  = iOpen (NULL, giProfileTf, ibar.proftf);
   double dclose = iClose(NULL, giProfileTf, ibar.proftf);
   createArw("#0.0.0.0" + ibar.proftf + ".open", dopen
      , Time[chart.startbar], 3, OpenColor);
   
   createArw("#0.0.0.0" + ibar.proftf + ".close", dclose
      , Time[chart.startbar], 3, CloseColor);
         
}
//+------------------------------------------------------------------+
//| getValueArea                                                     |
//+------------------------------------------------------------------+
void getValueArea
(
     int      countps
   , double   aprice.step[][]
   , int      amax.tpo[]
   , double   totaltpo
   , double   &vah
   , int      &vahidx
   , double   &val
   , int      &validx
)
{

   double vatpo  = (VATPOPercent/100) * totaltpo;      
   double tpo    = aprice.step[ amax.tpo[0] ][1];   
   double tpo2upper, tpo2lower;
   int    maxtpoidx = amax.tpo[0];
   int    upperidx = 1, lastupperidx = 1; 
   int    loweridx = 1, lastloweridx = 1; 
   
   while (tpo <= vatpo)
   {

      double utpo1 = aprice.step[ maxtpoidx - upperidx ][1];
      double ltpo1 = aprice.step[ maxtpoidx + loweridx ][1];
      double utpo2 = aprice.step[ maxtpoidx - (upperidx+1) ][1];
      double ltpo2 = aprice.step[ maxtpoidx + (loweridx+1) ][1];
      
      //check if vatpo reached by a single step                  
      if ( utpo1 >= ltpo1 && tpo + utpo1 >= vatpo )
      {
         lastupperidx = upperidx;
         tpo += utpo1;
         break;
      }
      else 
      if ( ltpo1 > utpo1 && tpo + ltpo1 >= vatpo )
      {
         lastloweridx = loweridx;
         tpo += ltpo1;
         break;         
      }
      
      //2 step price
      tpo2upper = utpo1 + utpo2;
      tpo2lower = ltpo1 + ltpo2;
                        
      if (tpo2upper >= tpo2lower)
      {
         lastupperidx = upperidx+1;
         if (maxtpoidx-lastupperidx < 0) lastupperidx--;
         upperidx +=2;
         tpo += tpo2upper;
      }
      else
      {
         lastloweridx = loweridx+1;
         if (maxtpoidx+lastloweridx > countps-1) lastloweridx--;
         loweridx +=2;
         tpo += tpo2lower;        
      }         
   }//end  while (tpo <= vatpo)
   
   vahidx = maxtpoidx - lastupperidx;
   validx = maxtpoidx + lastloweridx;
   vah    = aprice.step[ maxtpoidx-(lastupperidx) ][0];
   val    = aprice.step[ maxtpoidx+(lastloweridx) ][0];

}
//+------------------------------------------------------------------+
//| drawValueArea                                                    |
//+------------------------------------------------------------------+
void drawValueArea
(
     int    startbar
   , int    ibar.proftf
   , int    countps
   , double aprice.step[][]
   , int    maxtpoidx
   , double vah
   , int    vahidx
   , double val
   , int    validx
)
{
   int      chart.startbar = iBarShift( NULL, 0, iTime(NULL, giDataTf, startbar) );
   int      numtpo;
   int      step;
   datetime dtva1          = Time[chart.startbar];
   datetime dtva2          = iTime(NULL, giProfileTf, ibar.proftf-1); 
   double   lprice = aprice.step[countps-1][0];     

   //fix weekly open time on lower time frame...
   if ( Time[ iBarShift(NULL,0,dtva2) ] < dtva2 )             
      if (ibar.proftf != 0) dtva2 = Time[ iBarShift(NULL,0,dtva2) - 1];
   
   int      argb[3];
   intToRGB(VAColor, argb);      

   if (ibar.proftf == 0) 
   {
      delObjs( gsPref + "#" + ibar.proftf + ".0.0.0.va.");
      ObjectDelete( gsPref + "#" + ibar.proftf + ".1.0.vah");
      ObjectDelete( gsPref + "#" + ibar.proftf + ".1.0.val");
   }

   int      iclr, i, shiftx;   
   double   price1, price2;
   int      midvaidx    = vahidx + MathCeil((validx-vahidx)/2);   
   int      vaidx       = 0;      

   for (step=0; step<countps; step += giStep)      
   {

      if ( step > validx ) break;
      if ( step < vahidx ) continue;
      
      price1   = aprice.step[ step ][0];
      price2   = aprice.step[ step+giStep ][0];

      dtva1  = ObjectGet(gsPref + "#" + ibar.proftf + ".histo." + DoubleToStr(price1, fdigits), OBJPROP_TIME2 ); 
      //dtva1 += (Period() * 60);
      
      if ( dtva1 < Time[chart.startbar] ) dtva1 = Time[0] + Period()*60;         
      if ( dtva2 < dtva1 ) dtva2  = Time[0] + 10*Period()*60;
      
      //rect @vah
      if (vah < price1 && vah >= price2) price1 = vah;
      
      createRect("#" + ibar.proftf + ".0.0.0.va." + vaidx
         , price1, dtva1 
         , MathMax(price2, val), dtva2
         , RGB(argb[0]+iclr, argb[1]+iclr, argb[2]+iclr)
         );  
      
      if (step <= midvaidx)
      {       
         if (giProfileTf == PERIOD_MN1) iclr +=1;
         if (giProfileTf == PERIOD_W1)  iclr +=2;
         else iclr += 3;
      }
      else
      {
         if (giProfileTf == PERIOD_MN1) iclr -=1;
         if (giProfileTf == PERIOD_W1)  iclr -=2;
         else iclr -= 3;      
      }
      
      vaidx ++;                                       
   }             
 
}
  
//+------------------------------------------------------------------+
//|add char at beginning or end of text                              |
//+------------------------------------------------------------------+
string addStr(string str, string char, int maxlength, bool atbeginning = true)
{
   int l = maxlength - StringLen(str);
   for (int i=0; i<l; i++)
   {
      if (atbeginning) str = char + str;
      else str = str + char;
   }
      
   return(str);
}  
//+------------------------------------------------------------------+
//| createRect                                                       |
//+------------------------------------------------------------------+   
void createRect(string objname, double p1, datetime t1, double p2, datetime t2, color clr, bool back=true)
{

   objname = gsPref + objname;
   if(ObjectFind(objname) != 0)    
      ObjectCreate(objname, OBJ_RECTANGLE, 0, 0, 0, 0, 0);
   
   ObjectSet(objname, OBJPROP_PRICE1, p1);
   ObjectSet(objname, OBJPROP_TIME1,  t1);
   ObjectSet(objname, OBJPROP_PRICE2, p2);
   ObjectSet(objname, OBJPROP_TIME2,  t2);
   ObjectSet(objname, OBJPROP_COLOR,  clr);
   ObjectSet(objname, OBJPROP_BACK,   back);
}  
//+------------------------------------------------------------------+
//| createArw                                                        |
//+------------------------------------------------------------------+ 
void createArw(string objname, double p1, datetime t1, int ac, 
   color clr)
{
   objname = gsPref + objname;
   if(ObjectFind(objname) != 0)    
      ObjectCreate(objname, OBJ_ARROW, 0, 0, 0, 0, 0);
   
   ObjectSet(objname, OBJPROP_PRICE1, p1);
   ObjectSet(objname, OBJPROP_TIME1,  t1);  
   ObjectSet(objname, OBJPROP_ARROWCODE,  ac);  
   ObjectSet(objname, OBJPROP_COLOR,  clr);  
   
}
//+------------------------------------------------------------------+
//| createText                                                       |
//+------------------------------------------------------------------+
void createText(string name, datetime t, double p, string text
   , int size=8, string font="Arial", color c=White)
{
   name = gsPref + name;
   
   if (ObjectFind(name) != 0)
      ObjectCreate (name,OBJ_TEXT,0,0,0);
   
   ObjectSet    (name,OBJPROP_TIME1, t);
   ObjectSet    (name,OBJPROP_PRICE1,p);
   ObjectSetText(name,text,size,font,c);

}
//+------------------------------------------------------------------+
//| createTl                                                         |
//+------------------------------------------------------------------+ 
void createTl(string tlname, datetime t1, double v1, datetime t2, double v2, 
            color tlColor, int style = STYLE_SOLID, int width = 1, string desc="")
{
   tlname = gsPref + tlname;
   if(ObjectFind(tlname) != 0)
   {
      ObjectCreate(
            tlname
            , OBJ_TREND
            , 0
            , t1
            , v1
            , t2
            , v2
            );      
   }else
   {
      ObjectMove(tlname, 0, t1, v1);   
      ObjectMove(tlname, 1, t2, v2);
   }
   ObjectSet(tlname, OBJPROP_COLOR, tlColor);
   ObjectSet(tlname, OBJPROP_RAY, false);
   ObjectSet(tlname, OBJPROP_STYLE, style);
   ObjectSet(tlname, OBJPROP_WIDTH, width);
   ObjectSetText(tlname, desc);
} 

//+------------------------------------------------------------------+
//| newBarProfileTf                                                           |
//+------------------------------------------------------------------+
bool newBarProfileTf()
{
 static int Lasttfsource;
 int Currtfsource = iTime(NULL, giProfileTf, 0);//Timetfsource(Time[0]);
 
 if(Lasttfsource!=Currtfsource)
 {
   Lasttfsource=Currtfsource;
   return (true);
 }
   return(false);
   
 static datetime LastBar;
 int CurrBar = iTime(NULL, giProfileTf, 0);
 
 if(LastBar!=CurrBar)
 {
   LastBar=CurrBar;
   return (true);
 }
 
 return(false);     
   
}
//+------------------------------------------------------------------+
//| newBar                                                           |
//+------------------------------------------------------------------+   

bool newBar()
{
 static datetime LastBar;
 int CurrBar = Time[0];
 
 if(LastBar!=CurrBar)
 {
   LastBar=CurrBar;
   return (true);
 }
 
 return(false);    
}  

//+------------------------------------------------------------------+
//| secondDiff                                                       |
//+------------------------------------------------------------------+ 
bool secondDiff(int sec = 10)
{
   static datetime lasttime ;
   int diff = TimeCurrent() - lasttime ;

   if (diff > sec )
   {
      lasttime = TimeCurrent();
      return (true);
   }
   
   return (false);
   
}  

//+------------------------------------------------------------------+
//| delObjs function                                                 |
//+------------------------------------------------------------------+
void delObjs(string s="")
{
   int objs = ObjectsTotal();
   if (StringLen(s) == 0) s = gsPref;
   
   string name;
   for(int cnt=ObjectsTotal()-1;cnt>=0;cnt--)
   {
      name=ObjectName(cnt);
      if (StringSubstr(name,0,StringLen(s)) == s)       
         ObjectDelete(name); 
   }   
} 

//+------------------------------------------------------------------+
//| convert red, green and blue values to color                      |
//+------------------------------------------------------------------+
int RGB(int red_value,int green_value,int blue_value)
{
   //---- check parameters
   if(red_value<0)     red_value   = 0;
   if(red_value>255)   red_value   = 255;
   if(green_value<0)   green_value = 0;
   if(green_value>255) green_value = 255;
   if(blue_value<0)    blue_value  = 0;
   if(blue_value>255)  blue_value  = 255;
   //----

   green_value<<=8;
   blue_value<<=16;
   return(red_value+green_value+blue_value);
}
//+------------------------------------------------------------------+
//| convert color to red, green and blue values                      |
//+------------------------------------------------------------------+  
void intToRGB(int clr, int &argb[] )
{   
   int red   = (15);  
   int green = (15);  
   int blue  = (15);

   argb[0] = red;
   argb[1] = green;
   argb[2] = blue;
   
}  
//+------------------------------------------------------------------+


