| //+------------------------------------------------------------------+
|
| //| DinexSpace | Day/Week Separators |
|
| //| Vertical lines at the start of each day, week and month, with |
|
| //| optional day-of-week labels. The day can start at the broker's |
|
| //| midnight or at a custom server hour (for example a 17:00 New |
|
| //| York forex day). It only marks time; it gives no signals. |
|
| //+------------------------------------------------------------------+
|
| #property copyright "(c) 2026 DinexSpace"
|
| #property version "1.00"
|
| #property description "Vertical separators for days, weeks and months, with day-of-week labels."
|
| #property description "Day start at server midnight or a custom server hour. Marks time only, no signals."
|
| #property strict
|
| #property indicator_chart_window
|
| #property indicator_buffers 0
|
| #property indicator_plots 0
|
|
|
| input group "Separators"
|
| input bool InpDay = true; // Day
|
| input color InpColDay = C'70,70,70'; // Day colour
|
| input ENUM_LINE_STYLE InpStyDay = STYLE_DOT; // Day style
|
| input bool InpWeek = true; // Week
|
| input color InpColWeek = C'0,140,140'; // Week colour
|
| input ENUM_LINE_STYLE InpStyWeek = STYLE_DASH; // Week style
|
| input bool InpMonth = true; // Month
|
| input color InpColMon = clrSpringGreen; // Month colour
|
| input ENUM_LINE_STYLE InpStyMon = STYLE_SOLID; // Month style
|
|
|
| input group "Day start"
|
| input int InpDayHour = 0; // Day starts at server hour (0 = server midnight)
|
|
|
| input group "Display"
|
| input bool InpLabels = true; // Day-of-week labels
|
| input color InpColLbl = clrDimGray; // Label colour
|
| input int InpKeep = 200; // Separators to keep
|
| input int InpMaxBars = 5000; // Bars to scan
|
|
|
| const string PFX = "DNXSEP_";
|
| string g_names[]; // separator object names, oldest first
|
|
|
| string Dow(int d)
|
| {
|
| string n[7] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
|
| return (d >= 0 && d < 7) ? n[d] : "";
|
| }
|
|
|
| // Trading day key of a server time, with the day starting at InpDayHour.
|
| datetime DayKey(datetime t)
|
| {
|
| datetime s = t - InpDayHour * 3600;
|
| return s - s % 86400;
|
| }
|
|
|
| void AddSeparator(datetime t, double y, color clr, ENUM_LINE_STYLE st, string text)
|
| {
|
| string nm = PFX + IntegerToString((long)t);
|
| if(ObjectFind(0, nm) < 0)
|
| ObjectCreate(0, nm, OBJ_VLINE, 0, t, 0);
|
| ObjectSetInteger(0, nm, OBJPROP_COLOR, clr);
|
| ObjectSetInteger(0, nm, OBJPROP_STYLE, st);
|
| ObjectSetInteger(0, nm, OBJPROP_BACK, true);
|
| ObjectSetInteger(0, nm, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, nm, OBJPROP_HIDDEN, true);
|
| if(InpLabels)
|
| {
|
| string lb = nm + "_lbl";
|
| if(ObjectFind(0, lb) < 0)
|
| ObjectCreate(0, lb, OBJ_TEXT, 0, t, y);
|
| ObjectSetInteger(0, lb, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER);
|
| ObjectSetInteger(0, lb, OBJPROP_FONTSIZE, 7);
|
| ObjectSetInteger(0, lb, OBJPROP_COLOR, InpColLbl);
|
| ObjectSetInteger(0, lb, OBJPROP_SELECTABLE, false);
|
| ObjectSetInteger(0, lb, OBJPROP_HIDDEN, true);
|
| ObjectMove(0, lb, 0, t, y);
|
| ObjectSetString(0, lb, OBJPROP_TEXT, text == "" ? " " : text); // an empty label text shows "Label"
|
| }
|
| int n = ArraySize(g_names);
|
| if(n > 0 && g_names[n - 1] == nm)
|
| return; // same bar processed again
|
| ArrayResize(g_names, n + 1);
|
| g_names[n] = nm;
|
| if(n + 1 > InpKeep)
|
| {
|
| ObjectDelete(0, g_names[0]);
|
| ObjectDelete(0, g_names[0] + "_lbl");
|
| ArrayRemove(g_names, 0, 1);
|
| }
|
| }
|
|
|
| int OnInit()
|
| {
|
| if(InpDayHour < 0 || InpDayHour > 23 || InpKeep < 10 || InpMaxBars < 100)
|
| return INIT_PARAMETERS_INCORRECT;
|
| ArrayResize(g_names, 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;
|
| if(PeriodSeconds(_Period) >= 86400)
|
| return rates_total; // separators only make sense on intraday charts
|
| ArraySetAsSeries(time, false);
|
| ArraySetAsSeries(low, false);
|
| int from;
|
| if(prev_calculated == 0)
|
| {
|
| ObjectsDeleteAll(0, PFX);
|
| ArrayResize(g_names, 0);
|
| from = MathMax(1, rates_total - InpMaxBars);
|
| }
|
| else
|
| from = MathMax(1, prev_calculated - 1);
|
|
|
| for(int i = from; i < rates_total; i++)
|
| {
|
| MqlDateTime a, b;
|
| TimeToStruct(DayKey(time[i]), a);
|
| TimeToStruct(DayKey(time[i - 1]), b);
|
| bool newDay = DayKey(time[i]) != DayKey(time[i - 1]);
|
| bool newMonth = newDay && a.mon != b.mon;
|
| // a new week starts when the trading day's weekday goes backwards (e.g. Fri -> Mon) or 7+ days pass
|
| bool newWeek = newDay && (a.day_of_week < b.day_of_week || DayKey(time[i]) - DayKey(time[i - 1]) >= 7 * 86400);
|
| if(InpMonth && newMonth)
|
| AddSeparator(time[i], low[i], InpColMon, InpStyMon, Dow(a.day_of_week));
|
| else if(InpWeek && newWeek)
|
| AddSeparator(time[i], low[i], InpColWeek, InpStyWeek, Dow(a.day_of_week));
|
| else if(InpDay && newDay)
|
| AddSeparator(time[i], low[i], InpColDay, InpStyDay, Dow(a.day_of_week));
|
| }
|
| return rates_total;
|
| }
|
| //+------------------------------------------------------------------+
|