dinexspace-free-indicators/Indicators/DinexPeriodSeparators.mq5
141 строка
5,6 КиБ
MQL5
ИсходныйПостоянная ссылкаОбычный видИстория
//+------------------------------------------------------------------+
//| 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;
}
//+------------------------------------------------------------------+