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;
| |||
}
| |||
//+------------------------------------------------------------------+
|