#property copyright "Paradox"
#property link "http://www.forexfactory.com"

#property indicator_chart_window 

// input parameters
//You can change this either codes or number ie (H4 or 240)
extern string TimeFrame = "CURRENT";
extern int NumBars = 500;
//Histogram step size in Points. 10 is 1 pip on 5 digit broker.
extern int PricePrecision = 10;

extern bool ShowMarketProfile = true;
extern bool ShowFractal = true;

extern bool UseFullbarMarketProfile = true;
//Number of bars on each side of a bar to count as a Fractal.
extern int FractalSize = 2;

//This is where the histogram is divided absolute values. Use 0 to use HistogramThreshold logic instead
extern int MarketProfileThreshold = 50;
extern int FractalThreshold = 4;

extern color HighColor = Lime;
extern color LowColor = Red;
extern color ThresholdColor = Orange;
extern color BoxColor = LightBlue;
extern color StartLineColor = CLR_NONE;

extern int MovingShift = 0;
extern int HistogramSize = 5;
extern int HistogramStart = 4;
extern int HistogramSpace = 3;


//You can smooth the histogram like a moving average. 0 is the raw score data.
int HistogramSmoothRadius = 0;


//Number of bars to count both sides of a peak. Every peak counts but the more bars the more the score.Up to FractalLookBack
int FractalLookBack = 0;
bool ShowTdst = false;
int TdstCount = 9;
int TdstThreshold = 2;
bool UseVolume = false;

//This is where the histogram is divided (1.0 is the average).
double HistogramThreshold = 1.5;

//You can make a vertical line and name it ShiftLine.
string ShiftLine = "SHIFTLINE";
//Set this to true if you are running in strategy tester
bool VisualMode = false; 
//The lets you scroll back and hit F12 to simulate this running. The code no longer draws horizontal lines and the histogram is off to the right. 
//So this is no longer useful as a way to test.
bool UseChartDisplay = false;
bool DrawStartLine = true;


//DO NOT EDIT
int TimeFrameValue = 1440;
double StepSize;
int Shift = 0;
datetime LastUpdate;
double HighestHigh = 0;
double LowestLow = 999;
int NumPoints = 999;
int History = 0;
//----
string OBJECT_PREFIX = "LEVELS";
string SR_PREFIX = "SR_LEVEL";
int ObjectId = 0;

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int init()
{ 
   Shift = 0;
   OBJECT_PREFIX = TimeFrame + "SR" + OBJECT_PREFIX + DoubleToStr(HistogramSpace,0);
   SR_PREFIX = TimeFrame + "SR" + SR_PREFIX + DoubleToStr(HistogramSpace,0);
   StepSize = (1.0*Point*PricePrecision);
   TimeFrameValue = Text2Timeframe(TimeFrame);
   start();
   //ObDeleteObjectsByPrefix(ShiftLine);
   return(0);
}

//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
int deinit()
{
   ObDeleteObjectsByPrefix(OBJECT_PREFIX);
   ObDeleteObjectsByPrefix(SR_PREFIX);
   return(0);
}

datetime LastEndLineTime = NULL;
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void start()
{   
   datetime endLineTime = GetEndLine();
   
   if (endLineTime != LastEndLineTime)
   {
      int shift = GetTimeShift(endLineTime);
      //Print(" endLineTime ", TimeToStr(endLineTime));
      //Print("shift ", shift, " Shift ", Shift);
      LastEndLineTime = endLineTime;
      if (Shift != shift)
      {
         Shift = shift;
      }
      else
      {  
         if(TimeFrameValue != Period()) return (0);
      }
   }
   else
   {
      return (0);
   }   
   
   
   //Print ("UPDATING shift ", shift);
   ObDeleteObjectsByPrefix(OBJECT_PREFIX);
   ObDeleteObjectsByPrefix(SR_PREFIX);
   
   int pivotHistogram[];
   int smoothHistogram[];
   InitVariables(shift);
   int histogramOffset = HistogramStart;   
   
   if (DrawStartLine) 
   {   
      string startName = ObGetUniqueName(OBJECT_PREFIX);
      ObjectCreate(startName, OBJ_RECTANGLE, 0, iTime(NULL, TimeFrameValue, shift + NumBars),HighestHigh, iTime(NULL, TimeFrameValue, shift+1),LowestLow);      
      ObjectSet(startName , OBJPROP_COLOR, BoxColor);
      ObjectSet(startName , OBJPROP_BACK, false);
      startName = ObGetUniqueName(OBJECT_PREFIX);
      ObjectCreate(startName, OBJ_VLINE, 0, iTime(NULL, TimeFrameValue, shift + NumBars),0,0,0);
      ObjectSet(startName , OBJPROP_COLOR, StartLineColor);
   }
        
   if (ShowMarketProfile)
   {
      MakeMpHistogram(pivotHistogram, History, shift);  
      SmoothHistogram(pivotHistogram, smoothHistogram);    
      histogramOffset = DrawHistogram(smoothHistogram, histogramOffset, MarketProfileThreshold) + HistogramSpace;
   }   
   if (ShowFractal)
   {
      MakeFractalHistogram(pivotHistogram, History, shift);  
      SmoothHistogram(pivotHistogram, smoothHistogram);    
      histogramOffset = DrawHistogram(smoothHistogram, histogramOffset, FractalThreshold) + HistogramSpace;
   }   
   if (ShowTdst)
   {
      MakeTdstHistogram(pivotHistogram, History, shift);  
      SmoothHistogram(pivotHistogram, smoothHistogram);    
      histogramOffset = DrawHistogram(smoothHistogram, histogramOffset, TdstThreshold) + HistogramSpace;
   }
   return(0);
}

int  DrawHistogram(int &histogram[], int histogramOffset, double threshold)
{
   if (threshold == 0) threshold = (AverageNonZero(histogram) * HistogramThreshold);  
   double step = AverageNonZero(histogram) / HistogramSize;
   int maxHistogramEndpoint = 0;
   double histogramPrice = 0;
   for(int i = 0; i < ArraySize(histogram); i++)
   {
         histogramPrice = LowestLow + Point*PricePrecision*i;
         string line = ObGetUniqueName(SR_PREFIX);
         ObjectDelete(line);
         int endpoint = ((histogram[i] / step) + histogramOffset);
         if (endpoint > maxHistogramEndpoint) maxHistogramEndpoint= endpoint;
         ObjectCreate(line, OBJ_TREND, 0, Time[0] + (Time[0] - Time[1]) * ((histogram[i] / step) + histogramOffset) , histogramPrice ,Time[0] + histogramOffset*(Time[0] - Time[1]) ,  histogramPrice);                                           
         ObjectSet(line , OBJPROP_COLOR, LowColor);
         if (histogram[i] >= threshold){ObjectSet(line , OBJPROP_COLOR, HighColor);}
         ObjectSet(line , OBJPROP_RAY, 0);            
         ObjectSet(line , OBJPROP_WIDTH, 2);           
         ObjectSetText(line , DoubleToStr(histogram[i],2), 10);

   } 
   string thresholdLine = ObGetUniqueName(OBJECT_PREFIX);
   datetime thesholdLineTime = Time[0] + (Time[0] - Time[1])  * ((threshold / step) + histogramOffset);
   ObjectCreate(thresholdLine, OBJ_TREND, 0,thesholdLineTime ,LowestLow, thesholdLineTime,histogramPrice);
   ObjectSet(thresholdLine , OBJPROP_RAY, 0);
   ObjectSet(thresholdLine , OBJPROP_COLOR, ThresholdColor);
   return (maxHistogramEndpoint);
}

void  SmoothHistogram(int &pivotHistogram[], int &smoothHistogram[])
{
   int histogramSize =  ArraySize(pivotHistogram);
   ArrayResize(smoothHistogram,histogramSize);
   for(int i = 0; i < histogramSize; i++)
   { 
      double sum = 0;
      double count = 0;
      for (int j = MathMax(0,i - HistogramSmoothRadius);j <= MathMin(histogramSize-1, i + HistogramSmoothRadius);j++)
      {
         sum += pivotHistogram[j];
         count++;         
         if (j==i)
         {
         sum += pivotHistogram[j];
         count++;
         }
      }
      if (count == 0) count = 1;
      smoothHistogram[i] = sum / count;
   }
   
}


void InitVariables(int shift)
{
   History = MathMin(iBars(NULL, TimeFrameValue), NumBars + shift + 1);
   int i;
   for(i = 1 + shift; i < History; i++)
   {
      HighestHigh = MathMax(HighestHigh, iHigh(NULL, TimeFrameValue, i));
      LowestLow = MathMin(LowestLow, iLow(NULL, TimeFrameValue, i));
   }
   
   NumPoints = (HighestHigh - LowestLow) / StepSize + 1; 
}
 
 

double MakeMpHistogram(int &pivotHistogram[], int history, int shift)
{  
   InitArray(pivotHistogram, NumPoints);
   
   for(int i = 1 + shift; i <= history + shift + 1; i++)
   {
      if (UseFullbarMarketProfile)
      {
         double C = iLow(NULL,TimeFrameValue,i);
         double top = iHigh(NULL,TimeFrameValue,i); 
      }
      else         
      {
         C = BodyBottom(i, TimeFrameValue);
         top = BodyTop(i, TimeFrameValue);         
      }
      while(C < top)
      {
         int ind = (C - LowestLow) / StepSize;
         //Print (" i ", i, " C ", C, " ind ", ind); 
         pivotHistogram[ind]+=(GetVolume(TimeFrameValue, i));   
         C += 1.0*Point*PricePrecision;
      }       
   }
}


double MakeFractalHistogram(int &pivotHistogram[], int history, int shift)
{  
   InitArray(pivotHistogram, NumPoints);
   
   for(int i = 1 + shift; i <= history + shift + 1; i++)
   {
      int j = 0;
      int index = (iLow(NULL, TimeFrameValue, i)-LowestLow) / StepSize;
      int change = 0;
      bool isFractal = true;
      
      for (int k = 1; k <= FractalSize; k++)
      {
         if (i - k < shift) {isFractal = false; break;}
         if (iLow(NULL, TimeFrameValue, i) > iLow(NULL, TimeFrameValue,i - k)) {isFractal = false; break;}
         if (iLow(NULL, TimeFrameValue, i) > iLow(NULL, TimeFrameValue,i + k)) {isFractal = false; break;}
      }
      if (isFractal) change += GetVolume(TimeFrameValue, i);
       
         
      if (isFractal && FractalLookBack > FractalSize)
      { 
         //Print (" peakCount ", peakCount, " FractalLookBack ", FractalLookBack);
         //Analyze the trough strength forward in time
         for (j = FractalSize + 1; j < FractalLookBack; j++)
         {
            if (i - j < shift) break;
            if (iLow(NULL, TimeFrameValue, i - j) > iLow(NULL, TimeFrameValue, i )) //- j
               {change += GetVolume(TimeFrameValue, i - j) * (j - FractalSize); }
            else if (iLow(NULL, TimeFrameValue, i - j) < iLow(NULL, TimeFrameValue, i)){break;}            
         }
         //Analyze the trough strength backward in time
         for (j = FractalSize + 1; j < FractalLookBack; j++)
         {
            if (iLow(NULL, TimeFrameValue,i + j) > iLow(NULL, TimeFrameValue, i ))//+ j
               {change += GetVolume( TimeFrameValue,i + j) * (j - FractalSize); }
            else if (iLow(NULL, TimeFrameValue,i + j) < iLow(NULL, TimeFrameValue, i)){break;}           
         }   
      }
      
      pivotHistogram[index] += change;   
      
      
      /////Fractal Trough Scoring Section
      index = (iHigh(NULL, TimeFrameValue, i)-LowestLow) / StepSize;
      change = 0; 
      isFractal = true;
      
      for (k = 1; k <= FractalSize; k++)
      {
         if (i - k < shift) {isFractal = false; break;}
         if (iHigh(NULL, TimeFrameValue, i) < iHigh(NULL, TimeFrameValue,i - k)) {isFractal = false; break;}
         if (iHigh(NULL, TimeFrameValue, i) < iHigh(NULL, TimeFrameValue,i + k)) {isFractal = false; break;}
      }
      if (isFractal) change += GetVolume(TimeFrameValue, i);
       
      if (isFractal && FractalLookBack > 0)
      {
         
         //Print (" peakCount ", peakCount);
         //Analyze the peak strength forward in time
         for (j = FractalSize + 1; j < FractalLookBack; j++)
         {
            if (i - j < shift) break;
            if (iHigh(NULL, TimeFrameValue, i - j) < iHigh(NULL, TimeFrameValue, i ))
               {change += GetVolume(TimeFrameValue, i - j) * (j - FractalSize); }
            else if (iHigh(NULL, TimeFrameValue, i - j) > iHigh(NULL, TimeFrameValue, i)){break;}
         }
      
         //Analyze the peak strength backward in time
         for (j = FractalSize + 1; j < FractalLookBack; j++)
         {
            if (iHigh(NULL, TimeFrameValue,i + j) < iHigh(NULL, TimeFrameValue, i))// + j
               {change += GetVolume(TimeFrameValue,i + j) * (j - FractalSize); }
            else if (iHigh(NULL, TimeFrameValue,i + j) > iHigh(NULL, TimeFrameValue, i)) {break;}    
         }    
      }
      pivotHistogram[index] += change;   
      
      
   }
   
}


double MakeTdstHistogram(int &pivotHistogram[], int history, int shift)
{  
   InitArray(pivotHistogram, NumPoints);
   
   for(int i = 1 + shift; i <= history + shift + 1; i++)
   {
      bool isTdstSupport = false;
      bool isTdstResistance = false;
      
      if (i > shift - TdstCount && i < (history + shift) - TdstCount)
      {
         isTdstSupport = true;
         isTdstResistance = true;
         for(int j = i; j < i + TdstCount; j++)
         {
            if (iClose(NULL, TimeFrameValue,j) >= iClose(NULL, TimeFrameValue,j+4))
            {
               isTdstSupport = false;
               break;
            }
         }
         for(j = i; j < i + TdstCount; j++)
         {
            if (iClose(NULL, TimeFrameValue,j) <= iClose(NULL, TimeFrameValue,j+4))
            {
               isTdstResistance = false;
               break;
            }
         }
      }  
      if (isTdstSupport)
      {
         int index = (iClose(NULL, TimeFrameValue, i)-LowestLow) / StepSize;
         if (iLow(NULL, TimeFrameValue,i) < iLow(NULL, TimeFrameValue,i-1) &&iLow(NULL, TimeFrameValue,i) < iLow(NULL, TimeFrameValue,i-1))
         {
            pivotHistogram[index] += GetVolume(TimeFrameValue, i);
         }
         
         pivotHistogram[index] += GetVolume(TimeFrameValue, i);
      }
      if (isTdstResistance)
      {       
         //Print (" iClose(NULL, TimeFrameValue, i)", iClose(NULL, TimeFrameValue, i), " i ", i, "iTime(NULL, TimeFrameValue, i)", TimeToStr(iTime(NULL, TimeFrameValue, i)));
         index = (iClose(NULL, TimeFrameValue, i)-LowestLow) / StepSize;
         if (iHigh(NULL, TimeFrameValue,i) > iHigh(NULL, TimeFrameValue,i-1) &&iHigh(NULL, TimeFrameValue,i) > iHigh(NULL, TimeFrameValue,i-1))
         {
            pivotHistogram[index] += GetVolume(TimeFrameValue, i);
         }
         pivotHistogram[index] += GetVolume(TimeFrameValue, i);
      }
      
   }
}


double GetVolume(int TimeFrameValue, int shift)
{
   if (UseVolume)
   {
      return (iVolume(NULL,TimeFrameValue,shift));
   }
   else
   {  
      return (1);
   }
}
void InitArray(int &array[], int size)
{
   ArrayResize(array, size);
   for(int i = 0; i < size; i++) 
      array[i] = 0;
   return (array);
}


datetime GetEndLine()
{
   datetime endLineTime;
   if (UseChartDisplay)
   {
      endLineTime = Time[(WindowFirstVisibleBar()- WindowBarsPerChart()) + FractalLookBack];
   }
   else if (VisualMode)
   {
      int index = MovingShift;
      
      endLineTime = Time[index];
   }
   else
   {  endLineTime = GetShiftLine();
   }
   
   if (endLineTime == NULL)
   {  
      index = MovingShift;
      endLineTime = Time[index];
   }
   
   return (endLineTime);
   
}

int Text2Timeframe(string TimeFrameValue) {
   if (TimeFrameValue == "M1" || TimeFrameValue == "1") return (1);
   else if (TimeFrameValue == "M5" || TimeFrameValue == "5") return (5);
   else if (TimeFrameValue == "M15" || TimeFrameValue == "15") return (15);
   else if (TimeFrameValue == "M30" || TimeFrameValue == "30") return (30);
   else if (TimeFrameValue == "H1" || TimeFrameValue == "60") return (60);
   else if (TimeFrameValue == "H4" || TimeFrameValue == "240") return (240);
   else if (TimeFrameValue == "D1" || TimeFrameValue == "1440") return (1440);
   else if (TimeFrameValue == "W1" || TimeFrameValue == "10080") return (10080);
   else if (TimeFrameValue == "MN1" || TimeFrameValue == "43200") return (43200);
   return (Period());
}
  
  
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
string ObGetUniqueName(string Prefix)
  {
   ObjectId++;
   return (Prefix+" "+DoubleToStr(ObjectId, 0));
  }
//+------------------------------------------------------------------+
//|                                                                  |
//+------------------------------------------------------------------+
void ObDeleteObjectsByPrefix(string Prefix)
  {
   int L = StringLen(Prefix);
   int i = 0; 
   while(i < ObjectsTotal())
     {
       string ObjName = ObjectName(i);
       if(StringSubstr(ObjName, 0, L) != Prefix) 
         { 
           i++; 
           continue;
         }
       ObjectDelete(ObjName);
     }
  }
  
datetime GetShiftLine()
{
   int L = StringLen(ShiftLine);
   int i = 0; 
   while(i < ObjectsTotal())
   {
     string ObjName = ObjectName(i);
     if(StringSubstr(ObjName, 0, L) != ShiftLine) 
       { 
         i++; 
         continue;
       }
     
      datetime lineTime = ObjectGet(ObjName, OBJPROP_TIME1);
      return (lineTime);
   }
   return (NULL);
}
  
int GetTimeShift(datetime time)
{ 
   for(int i = 0; i < iBars(NULL,TimeFrameValue) ; i++)
   {
      //Print(" i ", i, " iTime(NULL, TimeFrameValue, i) ", TimeToStr(iTime(NULL, TimeFrameValue, i)));   
      if (iTime(NULL, TimeFrameValue, i) <= time) return (i);      
   } 
}
 
double AverageNonZero(int values[])
{
   double sum = 0;
   int count;
   for(int i = 0; i < ArraySize(values); i++)
   {
      if (values[i] != 0)
      {
         sum += values[i];
         count ++;
      }
   }
   if (count == 0) return (0);
   return (sum / count);
}

double BodyTop(int shift, int TimeFrameValue)
{
   return (MathMax(iOpen(NULL, TimeFrameValue, shift) , iClose(NULL, TimeFrameValue, shift)));
}

double BodyBottom(int shift, int TimeFrameValue)
{
   return (MathMin(iOpen(NULL, TimeFrameValue, shift) , iClose(NULL, TimeFrameValue, shift)));
}