dinexspace-free-indicators/Indicators/DinexDailyLevels.mq5
221 lines
9.3 KiB
MQL5
//+------------------------------------------------------------------+
//| DinexSpace | Daily High/Low & Previous Day Levels |
//| Draws previous day high, low and close, today's open, today's |
//| running high/low and (optionally) previous week high/low, one |
//| segment per day, with a panel of levels and distance from price. |
//| Values come from the symbol's own D1/W1 bars (broker server day).|
//+------------------------------------------------------------------+
#property copyright "(c) 2026 DinexSpace"
#property version "1.00"
#property description "Previous day high/low/close, today's open, today's high/low and previous week high/low."
#property description "One segment per day, with a panel showing each level and its distance from price."
#property strict
#property indicator_chart_window
#property indicator_buffers 0
#property indicator_plots 0
input group "Levels"
input bool InpShowPD = true; // Previous day high / low
input color InpColPDH = clrSpringGreen; // Previous day high colour
input color InpColPDL = clrAqua; // Previous day low colour
input bool InpShowPDC = true; // Previous day close
input color InpColPDC = clrDarkGray; // Previous day close colour
input bool InpShowDO = true; // Today's open
input color InpColDO = C'255,176,0'; // Today's open colour
input bool InpShowTHL = false; // Today's high / low (live)
input color InpColTHL = clrWhite; // Today's high / low colour
input bool InpShowPW = false; // Previous week high / low
input color InpColPW = C'142,124,255'; // Previous week colour
input group "Display"
input int InpDays = 5; // Days of history
input ENUM_LINE_STYLE InpStyle = STYLE_SOLID; // Line style
input int InpWidth = 1; // Line width
input bool InpLabels = true; // Labels with price (today)
input int InpLblOff = 3; // Label offset (bars right of price)
input bool InpPanel = true; // Levels panel
input ENUM_BASE_CORNER InpCorner = CORNER_RIGHT_UPPER; // Panel corner
const string PFX = "DNXDL_";
#define NL 8
string g_tag[NL] = {"PDH", "PDL", "PDC", "Open", "High", "Low", "PWH", "PWL"};
string g_name[NL] = {"Prev. day high", "Prev. day low", "Prev. day close", "Today's open",
"Today's high", "Today's low", "Prev. week high", "Prev. week low"};
bool g_on[NL];
color g_col[NL];
datetime g_lastDay = 0;
//+------------------------------------------------------------------+
//| Level k of the day that is `d` days back (d = 0 is today). |
//| Returns 0 when the data is not available. |
//+------------------------------------------------------------------+
double LevelValue(int k, int d)
{
switch(k)
{
case 0: return iHigh(_Symbol, PERIOD_D1, d + 1);
case 1: return iLow(_Symbol, PERIOD_D1, d + 1);
case 2: return iClose(_Symbol, PERIOD_D1, d + 1);
case 3: return iOpen(_Symbol, PERIOD_D1, d);
case 4: return iHigh(_Symbol, PERIOD_D1, d);
case 5: return iLow(_Symbol, PERIOD_D1, d);
}
datetime dayStart = iTime(_Symbol, PERIOD_D1, d);
if(dayStart == 0)
return 0;
int w = iBarShift(_Symbol, PERIOD_W1, dayStart, false);
if(w < 0)
return 0;
return (k == 6) ? iHigh(_Symbol, PERIOD_W1, w + 1) : iLow(_Symbol, PERIOD_W1, w + 1);
}
void DrawSegment(int k, int d, datetime t0, datetime t1, double y)
{
string nm = PFX + g_tag[k] + "_" + IntegerToString(d);
if(ObjectFind(0, nm) < 0)
{
ObjectCreate(0, nm, OBJ_TREND, 0, t0, y, t1, y);
ObjectSetInteger(0, nm, OBJPROP_COLOR, g_col[k]);
ObjectSetInteger(0, nm, OBJPROP_STYLE, InpStyle);
ObjectSetInteger(0, nm, OBJPROP_WIDTH, InpWidth);
ObjectSetInteger(0, nm, OBJPROP_RAY_RIGHT, false);
ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
ObjectSetInteger(0, nm, OBJPROP_BACK, true);
}
ObjectMove(0, nm, 0, t0, y);
ObjectMove(0, nm, 1, t1, y);
}
void DrawLabel(int k, datetime t, double y)
{
string nm = PFX + g_tag[k] + "_lbl";
if(ObjectFind(0, nm) < 0)
{
ObjectCreate(0, nm, OBJ_TEXT, 0, t, y);
ObjectSetInteger(0, nm, OBJPROP_ANCHOR, ANCHOR_LEFT);
ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 8);
ObjectSetInteger(0, nm, OBJPROP_COLOR, g_col[k]);
ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
}
ObjectMove(0, nm, 0, t, y);
ObjectSetString(0, nm, OBJPROP_TEXT, g_tag[k] + " " + DoubleToString(y, _Digits));
}
void PanelCell(int row, int col, string text, color clr)
{
string nm = PFX + "p_" + IntegerToString(row) + "_" + IntegerToString(col);
bool right = (InpCorner == CORNER_RIGHT_UPPER || InpCorner == CORNER_RIGHT_LOWER);
bool lower = (InpCorner == CORNER_LEFT_LOWER || InpCorner == CORNER_RIGHT_LOWER);
if(ObjectFind(0, nm) < 0)
{
ObjectCreate(0, nm, OBJ_LABEL, 0, 0, 0);
ObjectSetInteger(0, nm, OBJPROP_CORNER, InpCorner);
ObjectSetInteger(0, nm, OBJPROP_ANCHOR, right ? (lower ? ANCHOR_RIGHT_LOWER : ANCHOR_RIGHT_UPPER)
: (lower ? ANCHOR_LEFT_LOWER : ANCHOR_LEFT_UPPER));
// columns are placed from the panel's outer edge inwards
int x = right ? 10 + (2 - col) * 95 : 10 + col * 95;
ObjectSetInteger(0, nm, OBJPROP_XDISTANCE, x);
ObjectSetInteger(0, nm, OBJPROP_YDISTANCE, 20 + (lower ? NL + 1 - row : row) * 15);
ObjectSetInteger(0, nm, OBJPROP_FONTSIZE, 8);
ObjectSetString(0, nm, OBJPROP_FONT, "Consolas");
ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
}
ObjectSetString(0, nm, OBJPROP_TEXT, text);
ObjectSetInteger(0, nm, OBJPROP_COLOR, clr);
}
void UpdatePanel(double price, bool intraday)
{
if(!InpPanel)
return;
PanelCell(0, 0, "Level", clrGray);
PanelCell(0, 1, "Price", clrGray);
PanelCell(0, 2, "To level", clrGray);
for(int k = 0; k < NL; k++)
{
double y = LevelValue(k, 0);
bool ok = y > 0 && (intraday || k < 4 || k > 5);
double dist = y - price;
PanelCell(k + 1, 0, g_name[k], g_col[k]);
PanelCell(k + 1, 1, ok ? DoubleToString(y, _Digits) : "-", clrWhite);
PanelCell(k + 1, 2, ok ? (dist >= 0 ? "+" : "") + DoubleToString(dist, _Digits) : "-", dist >= 0 ? clrSpringGreen : clrAqua);
}
PanelCell(NL + 1, 0, intraday ? "DinexSpace" : "Lines need an intraday chart", intraday ? clrSpringGreen : clrOrange);
}
int OnInit()
{
bool on[NL] = {true, true, true, true, true, true, true, true};
on[0] = on[1] = InpShowPD; on[2] = InpShowPDC; on[3] = InpShowDO;
on[4] = on[5] = InpShowTHL; on[6] = on[7] = InpShowPW;
color col[NL] = {clrNONE, clrNONE, clrNONE, clrNONE, clrNONE, clrNONE, clrNONE, clrNONE};
col[0] = InpColPDH; col[1] = InpColPDL; col[2] = InpColPDC; col[3] = InpColDO;
col[4] = InpColTHL; col[5] = InpColTHL; col[6] = InpColPW; col[7] = InpColPW;
for(int k = 0; k < NL; k++)
{
g_on[k] = on[k];
g_col[k] = col[k];
}
if(InpDays < 1 || InpDays > 60)
return INIT_PARAMETERS_INCORRECT;
g_lastDay = 0;
return INIT_SUCCEEDED;
}
void OnDeinit(const int reason)
{
ObjectsDeleteAll(0, PFX);
}
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[])
{
if(rates_total < 2)
return 0;
ArraySetAsSeries(time, true);
ArraySetAsSeries(close, true);
bool intraday = PeriodSeconds(_Period) < 86400;
datetime today = iTime(_Symbol, PERIOD_D1, 0);
if(today == 0)
return 0; // D1 history not loaded yet: try again on the next tick
if(intraday)
{
// Full redraw once per new day (or on first run); otherwise only today's segments move.
int dFrom = (today != g_lastDay || prev_calculated == 0) ? InpDays - 1 : 0;
if(dFrom > 0)
ObjectsDeleteAll(0, PFX);
for(int d = dFrom; d >= 0; d--)
{
datetime t0 = iTime(_Symbol, PERIOD_D1, d);
if(t0 == 0)
continue;
datetime t1 = (d == 0) ? time[0] : iTime(_Symbol, PERIOD_D1, d - 1) - PeriodSeconds(_Period);
for(int k = 0; k < NL; k++)
{
if(!g_on[k])
continue;
double y = LevelValue(k, d);
if(y > 0)
DrawSegment(k, d, t0, t1, y);
}
}
g_lastDay = today;
if(InpLabels)
for(int k = 0; k < NL; k++)
{
double y = LevelValue(k, 0);
if(g_on[k] && y > 0)
DrawLabel(k, time[0] + InpLblOff * PeriodSeconds(_Period), y);
}
}
UpdatePanel(close[0], intraday);
return rates_total;
}
//+------------------------------------------------------------------+