//+------------------------------------------------------------------+
//|                                            DayBreakIndicator.mq4 |
//|                                                            LukeB |
//|                               https://www.forexfactory.com/lukeb |
//+------------------------------------------------------------------+
#property copyright "LukeB"
#property link      "https://www.forexfactory.com/lukeb"
#property version   "1.00"
#property strict
#property indicator_chart_window
#property indicator_buffers 6
//
#include <stdlib.mqh>
string short_name = "DayBreakIndi";
//======================================================================================================================================================
enum  LEVELS_IDX          { LEV_0_0,    LEV_0_236,  LEV_0_382,   LEV_0_50,   LEV_0_618,  LEV_0_786,  LEV_1_0,    LEV_1_618,  LEV_2_618,  LEV_4_236,
                            LEV_6_854,  LEV_11_09,  LEV_17_944,  LEV_29_034, LEV_46_978, LEV_76_012, LEV_122_990};
const double levels[17] = { 0.0,        0.236,      0.382,       0.50,       0.618,      0.786,      1.0,        1.618,      2.618,      4.236,
                            6.854,      11.09,      17.944,      29.034,     46.978,     76.012,     122.990 };
//======================================================================================================================================================
enum             INDI_IDX               { HIGH_STRIKE,        LOW_STRIKE,    HIGH_TP,    HIGH_SL,            LOW_TP,     LOW_SL,            endIndiIdx };
string           indiName[endIndiIdx] = { "HIGH_STRIKE",      "LOW_STRIKE",  "HIGH_TP",  "HIGH_SL",          "LOW_TP",   "LOW_SL"           };
color             drawClr[endIndiIdx] = { clrMediumVioletRed, clrIndianRed,  clrAqua,    clrMediumTurquoise, clrAqua,    clrMediumTurquoise };
ENUM_LINE_STYLE drawStyle[endIndiIdx] = { STYLE_SOLID,        STYLE_SOLID,   STYLE_DOT,  STYLE_DOT,          STYLE_DOT,  STYLE_DOT          };
int              drawIndi[endIndiIdx] = { DRAW_LINE,          DRAW_LINE,     DRAW_LINE,  DRAW_LINE,          DRAW_LINE,  DRAW_LINE          };
int             indiWidth[endIndiIdx] = {  2,                   2,             1,          1,                  1,          1                };
//
string markName[endIndiIdx]           = { "HIGH_MARK",        "LOW_MARK",    "HIGH_TP_MARK", "HIGH_SL_MARK", "LOW_TP_MARK", "LOW_SL_MARL"   };
//
struct Indicators
 {
   double indi[];
 } indicators[];
//
struct Prices
 {
   double yesterdayHigh;
   double yesterdayLow;
   double usualRange;
   datetime startTime;
   double tpPoints;
   double slPoints;
 } prices;
//
string errorString = "";
const int MIN_DAY_BARS = 25;
const int NOSHIFT = 0;
const int CHARTWINDOW = 0;
enum YES_NO {YES, NO};
//-------------------------------------------------------------------
extern LEVELS_IDX tpLevel = LEV_0_236;  // Level to mark for Take Profit
extern LEVELS_IDX slLevel = LEV_0_236;  // Level to mark for Stop Loss
extern YES_NO   useSpread = YES;        // Use Spread to move Price Marks
extern int     UniqueNum  = 37;         // A number to make this instance unique
//+------------------------------------------------------------------+
//| Custom indicator initialization function                         |
//+------------------------------------------------------------------+
int OnInit(void)
 {
   int initSuccess = INIT_SUCCEEDED;
   short_name += IntegerToString(UniqueNum);
   IndicatorShortName(short_name);
   for( INDI_IDX i = 0; i<endIndiIdx; i++ )
    {
      indiName[i] += IntegerToString(UniqueNum);
      markName[i] += IntegerToString(UniqueNum);
    }
   if( ArrayResize(indicators, endIndiIdx) != endIndiIdx )
    {
      int errCode = GetLastError();
      errorString = __FUNCTION__+" Array Resize Error: "+IntegerToString(errCode)+", "+ErrorDescription(errCode);
      Print(errorString); Comment(errorString);
      initSuccess= INIT_FAILED;
    }
   else
    {
      for(INDI_IDX idx=0; idx<endIndiIdx; idx++)
       {
         initializeIndiBuffs(idx);
       }
    }
   int dayBars = iBars(_Symbol,PERIOD_D1);
   if( dayBars < MIN_DAY_BARS )
    {
      errorString = __FUNCTION__+" Insufficient Day Bars, found: "+IntegerToString(dayBars)+" bars, need "+IntegerToString(MIN_DAY_BARS)+" bars.";
      Print(errorString); Comment(errorString);
    }
   return(INIT_SUCCEEDED);
 }
void initializeIndiBuffs(INDI_IDX idx)
 {
   SetIndexBuffer    (idx, indicators[idx].indi);
   SetIndexStyle     (idx, drawIndi[idx], drawStyle[idx], indiWidth[idx], drawClr[idx]);
   SetIndexLabel     (idx, indiName[idx]);
   SetIndexEmptyValue(idx, EMPTY_VALUE);
   SetIndexShift     (idx, 0);
   SetIndexDrawBegin (idx, 0);
 }
//+------------------------------------------------------------------+ 
//| Custom indicator deinitialization function                       | 
//+------------------------------------------------------------------+ 
void OnDeinit(const int reason)
 {
   long chartID = ChartID();
   for( INDI_IDX i = 0; i<endIndiIdx; i++ )
    {
      ObjectDelete(chartID, indiName[i] );
      ObjectDelete(chartID, markName[i] );
    }
   if(StringLen(errorString)>0)
      Comment("");
 } 
//+------------------------------------------------------------------+
//| Custom indicator iteration function                              |
//+------------------------------------------------------------------+
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 oldestBarNum;
   if( prev_calculated < 1 ) // The first time
    {
      oldestBarNum = rates_total-2;  // time of oldest should not be older than MIN_DAY_BARS, but thats just more code
    }
   else
    {
      oldestBarNum = rates_total-prev_calculated;
    }
   runIndicator(oldestBarNum);
   //
   return(rates_total);
 }
//==============================================================
void runIndicator(const int& oldestBarNum)
 {
   for( int barIdx = oldestBarNum; barIdx>=0; barIdx-- )
    {
      if( thereIsANewDayBar(barIdx) )
       {
         doDailyRates(prices,barIdx);
         setEmptyBar(barIdx+1);
       }
      loadIndicatorBuffers(prices, barIdx);
    }
   displayPriceMarks();
 }
//============================================================
bool thereIsANewDayBar(const int& barIdx)
 {
   static datetime lastDayBarOneTime = 0;
   bool newBar = false;
   int currentDayBarOne = iBarShift(_Symbol, PERIOD_D1, Time[barIdx], false);
   datetime dayBarOneTime;
   dayBarOneTime = iTime(_Symbol,PERIOD_D1,currentDayBarOne);
   if( lastDayBarOneTime != dayBarOneTime )
    {
      lastDayBarOneTime = dayBarOneTime;
      newBar = true;
    }
   return( newBar );
 }
//==============================================================
void doDailyRates(Prices& values,const int& barIdx)
 {
   int barOne = iBarShift(_Symbol,PERIOD_D1,Time[barIdx],false)+1;
   int barTwo = barOne+1;
   //
   values.usualRange     = iATR(_Symbol,PERIOD_D1,20,barOne);    // values.yesterdayHigh - values.yesterdayLow;
   values.yesterdayHigh  = iHigh(_Symbol, PERIOD_D1, barOne);    // Use Bar 1, bar zero range is zero at the start of the day, and grows for much of the day.
   values.yesterdayLow   = iLow(_Symbol, PERIOD_D1,  barOne);
   values.startTime      = iTime(_Symbol, PERIOD_D1, barOne);
   if( TimeDayOfWeek(values.startTime) == SUNDAY )                // Add Sunday to Fridy
    {
      double fridayHigh = iHigh(_Symbol, PERIOD_D1, barTwo);
      double fridayLow  = iLow(_Symbol, PERIOD_D1,  barTwo);
      values.yesterdayHigh  = (values.yesterdayHigh>fridayHigh)?values.yesterdayHigh:fridayHigh;
      values.yesterdayLow   = (values.yesterdayLow<fridayLow)?values.yesterdayLow:fridayLow;
    }
   values.tpPoints = levels[tpLevel]*values.usualRange;
   values.slPoints = levels[slLevel]*values.usualRange;
 }
//==============================================================
void loadIndicatorBuffers(const Prices& values, int const barIdx)
 {
   indicators[HIGH_STRIKE].indi[barIdx] = values.yesterdayHigh;
   indicators[HIGH_TP].indi[barIdx]     = values.yesterdayHigh + values.tpPoints;
   indicators[HIGH_SL].indi[barIdx]     = values.yesterdayHigh - values.slPoints;
   //
   indicators[LOW_STRIKE].indi[barIdx] = values.yesterdayLow;
   indicators[LOW_TP].indi[barIdx]     = values.yesterdayLow - values.tpPoints;
   indicators[LOW_SL].indi[barIdx]     = values.yesterdayLow + values.slPoints;
 }
//==============================================================
void setEmptyBar(const int emptyBar)
 {
   indicators[HIGH_STRIKE].indi[emptyBar] = EMPTY_VALUE;
   indicators[HIGH_TP].indi[emptyBar]     = EMPTY_VALUE;
   indicators[HIGH_SL].indi[emptyBar]     = EMPTY_VALUE;
   //
   indicators[LOW_STRIKE].indi[emptyBar] = EMPTY_VALUE;
   indicators[LOW_TP].indi[emptyBar]     = EMPTY_VALUE;
   indicators[LOW_SL].indi[emptyBar]     = EMPTY_VALUE;
 }
//==============================================================
void displayPriceMarks()
 {
   int markPriceIdx = 0;
   double markPrice;
   markPrice = (useSpread==YES)?indicators[HIGH_STRIKE].indi[markPriceIdx] + (Ask-Bid):indicators[HIGH_STRIKE].indi[markPriceIdx];
   setPriceMark(markName[HIGH_STRIKE], drawClr[HIGH_STRIKE], markPrice );
   //
   markPrice = indicators[HIGH_TP].indi[markPriceIdx];
   setPriceMark(markName[HIGH_TP], drawClr[HIGH_TP], markPrice );
   //
   markPrice = (useSpread==YES)?indicators[HIGH_SL].indi[markPriceIdx] + (Ask-Bid):indicators[HIGH_SL].indi[markPriceIdx];
   setPriceMark(markName[HIGH_SL], drawClr[HIGH_SL], markPrice );
   //
   markPrice = indicators[LOW_STRIKE].indi[markPriceIdx];
   setPriceMark(markName[LOW_STRIKE], drawClr[LOW_STRIKE], markPrice );
   //
   markPrice = (useSpread==YES)?indicators[LOW_TP].indi[markPriceIdx] + (Ask-Bid):indicators[LOW_TP].indi[markPriceIdx];
   setPriceMark(markName[LOW_TP], drawClr[LOW_TP], markPrice );
   //
   markPrice = (useSpread==YES)?indicators[LOW_SL].indi[markPriceIdx] + (Ask-Bid):indicators[LOW_SL].indi[markPriceIdx];
   setPriceMark(markName[LOW_SL], drawClr[LOW_SL], markPrice );
 }
//==============================================================
void setPriceMark(const string& objName, const color& objClr, const double& price)
 {
   long chartID = ChartID();
   if( ObjectFind(chartID,objName) < 0 )
    {
      if( !ObjectCreate(chartID, objName,OBJ_ARROW_RIGHT_PRICE, CHARTWINDOW,Time[0], price) ) 
       {
         int errCode = GetLastError();
         errorString = __FUNCTION__+" failed to create the right price labe: "+objName+", error: "+IntegerToString(errCode)+", "+ErrorDescription(errCode);
         Print(errorString); Comment(errorString);
       }
      else
       {
         ObjectSetInteger(chartID, objName, OBJPROP_COLOR,      objClr); 
         ObjectSetInteger(chartID, objName, OBJPROP_STYLE,      STYLE_SOLID); 
         ObjectSetInteger(chartID, objName, OBJPROP_WIDTH,      2); 
         ObjectSetInteger(chartID, objName, OBJPROP_BACK,       false); 
         ObjectSetInteger(chartID, objName, OBJPROP_SELECTABLE, false); 
         ObjectSetInteger(chartID, objName, OBJPROP_SELECTED,   false); 
         ObjectSetInteger(chartID, objName, OBJPROP_HIDDEN,     true); 
       }
    }
   else
    {
      ObjectMove(chartID,objName,0,Time[0],price);
    }
 }
//==============================================================