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