//+------------------------------------------------------------------+ //| ICTMacros.mq5 | //| Copyright 2026, MT5 Coder Project | //+------------------------------------------------------------------+ // WHAT IT DOES // Marks ICT macro windows as chart objects (rectangle, plus optional // 50% midline, start/end vertical lines and a label) for the Asia, // London and New York sessions. Every session has its own ON/OFF input. // // Window times are entered in NEW YORK local time as // "HH:MM-HH:MM;HH:MM-HH:MM". Defaults: // London : 02:33-03:00;04:03-04:30 // New York: 08:50-09:10;09:50-10:10;10:50-11:10;11:50-12:10;13:10-13:40;15:15-15:45 // Asia : empty by default - paste Asia windows into InpAsiaMacros and // the Asia toggle starts working immediately. // A window whose end time is not later than its start time is treated as // crossing midnight (e.g. 23:30-00:15). // // TIME BASE // Chart bars are in broker server time. Window times are converted // NY local -> UTC (US DST rules implemented here) -> server time. // The server/UTC offset is auto-detected as TimeTradeServer()-TimeGMT() // rounded to 5 minutes, or taken from InpManualServerGmtOffset. // TimeGMT() depends on the accuracy of the local PC clock; switch to // ICT_SRV_MANUAL if that clock is not reliable. // // OBJECTS // Prefix "ICTMacros_". Only objects created by this program are removed. // Windows on closed bars never move. The window that is currently open is // refreshed while it is open (InpLiveBox). Windows whose bars do not exist // yet are not drawn, so a later window of the day appears once its first // bar has formed. // // LIMITATIONS // * Chart objects only - no indicator buffers, nothing for iCustom. // * MT5 chart objects have no alpha transparency: a filled box is opaque // (it is drawn behind the bars). Outline only is the default. // * Best on M1-M15. On coarse timeframes a window can cover 0 or 1 bar. // * Object count grows with InpHistoryDays - watch InpMaxObjects. // * Visual tool only: it draws no signals and does not trade. //+------------------------------------------------------------------+ #property copyright "Copyright 2026, MT5 Coder Project" #property link "https://www.mql5.com" #property version "1.00" #property description "ICT macro windows (Asia / London / New York) drawn in New York local time." #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 //--- object namespace owned by this program #define ICT_PREFIX "ICTMacros_" #define ICT_SEC_PER_DAY 86400 #define ICT_MIN_PER_DAY 1440 //--- enums enum ENUM_ICT_TIMEBASE { ICT_TB_NEWYORK = 0, ICT_TB_SERVER = 1, ICT_TB_GMT = 2 }; enum ENUM_ICT_SRVOFF { ICT_SRV_AUTO = 0, ICT_SRV_MANUAL = 1 }; enum ENUM_ICT_SESSION { ICT_ASIA = 0, ICT_LONDON = 1, ICT_NEWYORK = 2 }; //--- inputs input group "Sessions" input bool InpShowAsia = true; // Draw Asia macros input bool InpShowLondon = true; // Draw London macros input bool InpShowNewYork = true; // Draw New York macros input group "Macro windows (New York local time, format HH:MM-HH:MM;...)" input string InpAsiaMacros = ""; // Asia windows (empty = none) input string InpLondonMacros = "02:33-03:00;04:03-04:30"; input string InpNYMacros = "08:50-09:10;09:50-10:10;10:50-11:10;11:50-12:10;13:10-13:40;15:15-15:45"; input group "Time base" input ENUM_ICT_TIMEBASE InpTimeBase = ICT_TB_NEWYORK; // Reference zone of the windows above input ENUM_ICT_SRVOFF InpServerOffsetMode = ICT_SRV_AUTO; // Broker server offset detection input double InpManualServerGmtOffset = 0.0; // Server GMT offset in hours (manual mode) input group "Display" input bool InpDrawBox = true; // Draw the macro box input bool InpFillBox = false; // Fill the box (opaque, drawn behind the bars) input bool InpMidline = false; // Draw the 50% midline of the window input bool InpVerticalLines = true; // Draw vertical lines at window start/end input bool InpShowLabels = true; // Draw the window label input int InpLabelFontSize = 8; // Label font size input color InpAsiaColor = clrKhaki; input color InpLondonColor = clrSteelBlue; input color InpNewYorkColor = clrTomato; input bool InpLiveBox = true; // Refresh the window that is currently open input group "History / limits" input int InpHistoryDays = 20; // Past days to draw (0 = current day only) input int InpMaxObjects = 2000; // Safety cap of objects per rebuild input bool InpCleanStaleObjects = true; // Remove leftover ICTMacros_ objects of former runs input group "Diagnostics" input bool InpVerboseLog = true; // Print time base / offset / object diagnostics //--- one macro window (times are minutes from midnight in the reference zone) struct MacroWindow { int session; // ENUM_ICT_SESSION int index; // 1-based index inside the session int startMin; // start, minutes from midnight int endMin; // end, minutes from midnight (may exceed 1440 when crossing midnight) string label; // display label, e.g. "London 02:33-03:00" }; //--- state MacroWindow g_win[]; // parsed windows of all enabled session lists int g_winCount = 0; string g_names[]; // objects of the current rebuild int g_nameCount = 0; string g_prevNames[]; // objects of the previous rebuild (stale-object diff) int g_prevCount = 0; datetime g_lastBarTime = 0; int g_srvOffsetSec = 0; // broker server UTC offset in seconds double g_liveHi = 0.0; double g_liveLo = 0.0; bool g_loggedSummary = false; bool g_capWarned = false; bool g_orphanSwept = false; //+------------------------------------------------------------------+ //| Small text helpers | //+------------------------------------------------------------------+ string Trim(const string src) { string s = src; StringTrimLeft(s); StringTrimRight(s); return s; } bool AllDigits(const string s) { const int n = StringLen(s); if(n == 0) return false; for(int i = 0; i < n; i++) { const ushort c = StringGetCharacter(s, i); if(c < 48 || c > 57) // '0' .. '9' return false; } return true; } //| Parse "HH:MM" into minutes from midnight. | bool ParseHHMM(const string src, int &minutes) { const string t = Trim(src); string hm[]; if(StringSplit(t, ':', hm) != 2) return false; const string hs = Trim(hm[0]); const string ms = Trim(hm[1]); if(!AllDigits(hs) || !AllDigits(ms)) return false; const int h = (int)StringToInteger(hs); const int m = (int)StringToInteger(ms); if(h < 0 || h > 23 || m < 0 || m > 59) return false; minutes = h * 60 + m; return true; } //+------------------------------------------------------------------+ //| Session helpers | //+------------------------------------------------------------------+ string SessionTag(const int session) { switch(session) { case ICT_ASIA: return "Asia"; case ICT_LONDON: return "London"; case ICT_NEWYORK: return "NY"; } return "Sess"; } string SessionLabel(const int session) { switch(session) { case ICT_ASIA: return "Asia"; case ICT_LONDON: return "London"; case ICT_NEWYORK: return "New York"; } return "Session"; } color SessionColor(const int session) { switch(session) { case ICT_ASIA: return InpAsiaColor; case ICT_LONDON: return InpLondonColor; case ICT_NEWYORK: return InpNewYorkColor; } return clrGray; } bool SessionEnabled(const int session) { switch(session) { case ICT_ASIA: return InpShowAsia; case ICT_LONDON: return InpShowLondon; case ICT_NEWYORK: return InpShowNewYork; } return false; } string TimeBaseName(const ENUM_ICT_TIMEBASE tb) { switch(tb) { case ICT_TB_NEWYORK: return "NewYork"; case ICT_TB_SERVER: return "Server"; case ICT_TB_GMT: return "GMT"; } return "Unknown"; } //+------------------------------------------------------------------+ //| Parse one session window list, appending to dst. | //| Returns the number of windows added. Invalid tokens are skipped | //| with a diagnostic instead of aborting the whole list. | //+------------------------------------------------------------------+ int ParseWindowList(const string src, const int session, MacroWindow &dst[]) { if(StringLen(Trim(src)) == 0) return 0; int added = 0; string parts[]; const int count = StringSplit(src, ';', parts); for(int i = 0; i < count; i++) { const string token = Trim(parts[i]); if(token == "") continue; string range[]; if(StringSplit(token, '-', range) != 2) { PrintFormat("ICTMacros: skipped window '%s' - expected HH:MM-HH:MM", token); continue; } int startMin = 0, endMin = 0; if(!ParseHHMM(range[0], startMin) || !ParseHHMM(range[1], endMin)) { PrintFormat("ICTMacros: skipped window '%s' - invalid time", token); continue; } if(endMin == startMin) { PrintFormat("ICTMacros: skipped window '%s' - zero length", token); continue; } const int n = ArraySize(dst); if(ArrayResize(dst, n + 1) != n + 1) { Print("ICTMacros: out of memory while parsing macro windows"); break; } dst[n].session = session; dst[n].index = added + 1; dst[n].startMin = startMin; dst[n].endMin = (endMin < startMin) ? endMin + ICT_MIN_PER_DAY : endMin; dst[n].label = StringFormat("%s %02d:%02d-%02d:%02d", SessionLabel(session), startMin / 60, startMin % 60, endMin / 60, endMin % 60); added++; } return added; } //+------------------------------------------------------------------+ //| America/New_York DST rules | //| DST starts 02:00 local (07:00 UTC) on the 2nd Sunday of March, | //| DST ends 02:00 local (06:00 UTC) on the 1st Sunday of November| //+------------------------------------------------------------------+ datetime NthSundayUtc(const int year, const int month, const int nth, const int hourUtc) { MqlDateTime d; d.year = year; d.mon = month; d.day = 1; d.hour = hourUtc; d.min = 0; d.sec = 0; d.day_of_week = 0; d.day_of_year = 0; const datetime first = StructToTime(d); MqlDateTime f; TimeToStruct(first, f); const int dow = f.day_of_week; // 0 = Sunday const int offsetDays = ((7 - dow) % 7) + (nth - 1) * 7; return (datetime)((long)first + (long)offsetDays * ICT_SEC_PER_DAY); } bool IsUsDst(const datetime utc) { MqlDateTime d; TimeToStruct(utc, d); const datetime dstStart = NthSundayUtc(d.year, 3, 2, 7); const datetime dstEnd = NthSundayUtc(d.year, 11, 1, 6); return (utc >= dstStart && utc < dstEnd); } //| NY local = UTC + offset (EDT -4h, EST -5h) | int NyUtcOffsetSec(const datetime utc) { return IsUsDst(utc) ? -4 * 3600 : -5 * 3600; } //+------------------------------------------------------------------+ //| Time conversion helpers | //+------------------------------------------------------------------+ datetime DayStart(const datetime t) { return (datetime)((long)t - (long)((long)t % ICT_SEC_PER_DAY)); } datetime MakeNaiveDay(const int year, const int month, const int day) { MqlDateTime d; d.year = year; d.mon = month; d.day = day; d.hour = 0; d.min = 0; d.sec = 0; d.day_of_week = 0; d.day_of_year = 0; return StructToTime(d); } //| Convert a window boundary (minutes from midnight of refDay in the | //| reference zone) into broker server time. | datetime WindowTimeToServer(const datetime refDay, int minutesOfDay) { long extraDays = 0; while(minutesOfDay >= ICT_MIN_PER_DAY) { minutesOfDay -= ICT_MIN_PER_DAY; extraDays++; } const datetime naive = (datetime)((long)refDay + extraDays * ICT_SEC_PER_DAY + (long)minutesOfDay * 60); if(InpTimeBase == ICT_TB_SERVER) return naive; // the list is already in server time datetime utc = naive; // for GMT the naive stamp is UTC if(InpTimeBase == ICT_TB_NEWYORK) { const int off1 = NyUtcOffsetSec(naive); // first guess from the naive stamp utc = (datetime)((long)naive - (long)off1); const int off2 = NyUtcOffsetSec(utc); // second pass, correct across the DST switch if(off2 != off1) utc = (datetime)((long)naive - (long)off2); } return (datetime)((long)utc + (long)g_srvOffsetSec); } //| "Now" expressed in the wall clock of the configured time base. | datetime ReferenceNow() { const datetime srvNow = TimeTradeServer(); if(InpTimeBase == ICT_TB_SERVER) return srvNow; const datetime utcNow = (datetime)((long)srvNow - (long)g_srvOffsetSec); if(InpTimeBase == ICT_TB_GMT) return utcNow; return (datetime)((long)utcNow + (long)NyUtcOffsetSec(utcNow)); } //| Broker server UTC offset in seconds, snapped to 5 minutes. | int DetectServerOffsetSec() { if(InpTimeBase == ICT_TB_SERVER) return 0; if(InpServerOffsetMode == ICT_SRV_MANUAL) return (int)MathRound(InpManualServerGmtOffset * 3600.0 / 300.0) * 300; const double diff = (double)((long)TimeTradeServer() - (long)TimeGMT()); return (int)MathRound(diff / 300.0) * 300; } //+------------------------------------------------------------------+ //| First index whose time is >= value (arrays arrive oldest-first). | //+------------------------------------------------------------------+ int LowerBound(const datetime &time[], const int total, const datetime value) { int lo = 0, hi = total; while(lo < hi) { const int mid = lo + (hi - lo) / 2; if(time[mid] < value) lo = mid + 1; else hi = mid; } return lo; } //+------------------------------------------------------------------+ //| Chart object helpers (own namespace only) | //+------------------------------------------------------------------+ void SetCommonProps(const string name, const color clr, const int width, const bool back) { ObjectSetInteger(0, name, OBJPROP_COLOR, clr); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_SOLID); ObjectSetInteger(0, name, OBJPROP_WIDTH, width); ObjectSetInteger(0, name, OBJPROP_BACK, back); ObjectSetInteger(0, name, OBJPROP_SELECTABLE, false); ObjectSetInteger(0, name, OBJPROP_SELECTED, false); ObjectSetInteger(0, name, OBJPROP_HIDDEN, true); } bool EnsureRect(const string name, const datetime t1, const double p1, const datetime t2, const double p2, const color clr) { if(ObjectFind(0, name) < 0) { if(!ObjectCreate(0, name, OBJ_RECTANGLE, 0, t1, p1, t2, p2)) { PrintFormat("ICTMacros: ObjectCreate failed for %s (error %d)", name, GetLastError()); return false; } } else { ObjectMove(0, name, 0, t1, p1); ObjectMove(0, name, 1, t2, p2); } SetCommonProps(name, clr, 1, true); ObjectSetInteger(0, name, OBJPROP_FILL, InpFillBox); return true; } bool EnsureTrend(const string name, const datetime t1, const double p1, const datetime t2, const double p2, const color clr) { if(ObjectFind(0, name) < 0) { if(!ObjectCreate(0, name, OBJ_TREND, 0, t1, p1, t2, p2)) { PrintFormat("ICTMacros: ObjectCreate failed for %s (error %d)", name, GetLastError()); return false; } } else { ObjectMove(0, name, 0, t1, p1); ObjectMove(0, name, 1, t2, p2); } SetCommonProps(name, clr, 1, true); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, name, OBJPROP_RAY_LEFT, false); ObjectSetInteger(0, name, OBJPROP_RAY_RIGHT, false); return true; } bool EnsureVLine(const string name, const datetime t, const color clr) { if(ObjectFind(0, name) < 0) { if(!ObjectCreate(0, name, OBJ_VLINE, 0, t, 0.0)) { PrintFormat("ICTMacros: ObjectCreate failed for %s (error %d)", name, GetLastError()); return false; } } else ObjectMove(0, name, 0, t, 0.0); SetCommonProps(name, clr, 1, true); ObjectSetInteger(0, name, OBJPROP_STYLE, STYLE_DOT); return true; } bool EnsureText(const string name, const datetime t, const double price, const string text, const color clr, const int fontSize) { if(ObjectFind(0, name) < 0) { if(!ObjectCreate(0, name, OBJ_TEXT, 0, t, price)) { PrintFormat("ICTMacros: ObjectCreate failed for %s (error %d)", name, GetLastError()); return false; } } else ObjectMove(0, name, 0, t, price); SetCommonProps(name, clr, 1, false); int fs = fontSize; if(fs < 6) fs = 6; if(fs > 20) fs = 20; ObjectSetString(0, name, OBJPROP_TEXT, text); ObjectSetString(0, name, OBJPROP_TOOLTIP, text); ObjectSetString(0, name, OBJPROP_FONT, "Arial"); ObjectSetInteger(0, name, OBJPROP_FONTSIZE, fs); ObjectSetInteger(0, name, OBJPROP_ANCHOR, ANCHOR_LEFT_LOWER); return true; } //+------------------------------------------------------------------+ //| Object name registry (never touches objects of other programs) | //+------------------------------------------------------------------+ void RegisterName(const string name) { const int size = ArraySize(g_names); if(g_nameCount >= size) { if(ArrayResize(g_names, size + 64) <= size) return; // registry full: keep drawing, skip tracking } g_names[g_nameCount] = name; g_nameCount++; } bool NameInList(const string name, string &list[], const int count) { for(int i = 0; i < count; i++) if(list[i] == name) return true; return false; } bool CapacityOk() { if(g_nameCount + 4 > InpMaxObjects) { if(!g_capWarned) { PrintFormat("ICTMacros: object cap InpMaxObjects=%d reached - older windows are skipped", InpMaxObjects); g_capWarned = true; } return false; } return true; } //| Remove ICTMacros_ objects left behind by a former run. Runs once per | //| initialization. Disable InpCleanStaleObjects when two copies of this | //| indicator share one chart. | void SweepOrphanObjects() { if(!InpCleanStaleObjects || g_orphanSwept) return; g_orphanSwept = true; const int total = ObjectsTotal(0, -1, -1); string doomed[]; int doomedCount = 0; for(int i = 0; i < total; i++) { const string name = ObjectName(0, i, -1, -1); if(StringFind(name, ICT_PREFIX) != 0) // only this program's namespace continue; if(NameInList(name, g_names, g_nameCount)) continue; const int size = ArraySize(doomed); if(doomedCount >= size) if(ArrayResize(doomed, size + 32) <= size) break; doomed[doomedCount] = name; doomedCount++; } for(int i = 0; i < doomedCount; i++) ObjectDelete(0, doomed[i]); if(doomedCount > 0) PrintFormat("ICTMacros: removed %d leftover object(s) from a previous run", doomedCount); } //+------------------------------------------------------------------+ //| Drawing | //+------------------------------------------------------------------+ string DateTag(const datetime refDay) { MqlDateTime d; TimeToStruct(refDay, d); return StringFormat("%04d%02d%02d", d.year, d.mon, d.day); } void DrawWindow(const int wi, const datetime refDay, const datetime tStart, const datetime tEnd, const double hi, const double lo) { const int session = g_win[wi].session; const color clr = SessionColor(session); const string base = StringFormat("%s%s_%02d_%s", ICT_PREFIX, SessionTag(session), g_win[wi].index, DateTag(refDay)); if(InpDrawBox || InpFillBox) if(EnsureRect(base, tStart, lo, tEnd, hi, clr)) RegisterName(base); if(InpMidline) { const string midName = base + "_mid"; const double mid = (hi + lo) / 2.0; if(EnsureTrend(midName, tStart, mid, tEnd, mid, clr)) RegisterName(midName); } if(InpVerticalLines) { const string startName = base + "_S"; const string endName = base + "_E"; if(EnsureVLine(startName, tStart, clr)) RegisterName(startName); if(EnsureVLine(endName, tEnd, clr)) RegisterName(endName); } if(InpShowLabels) { const string labelName = base + "_L"; if(EnsureText(labelName, tStart, hi, g_win[wi].label, clr, InpLabelFontSize)) RegisterName(labelName); } } //| Full rebuild. Runs on init and on every new bar. | void RebuildAll(const datetime &time[], const double &high[], const double &low[], const int rates_total) { g_nameCount = 0; g_capWarned = false; g_liveHi = 0.0; g_liveLo = 0.0; int days = InpHistoryDays; if(days < 0) days = 0; if(days > 200) days = 200; const datetime refDay = DayStart(ReferenceNow()); bool stop = false; for(int d = 0; d <= days && !stop; d++) { const datetime day = (datetime)((long)refDay - (long)d * ICT_SEC_PER_DAY); for(int w = 0; w < g_winCount && !stop; w++) { if(!SessionEnabled(g_win[w].session)) continue; if(!CapacityOk()) { stop = true; break; } const datetime t1 = WindowTimeToServer(day, g_win[w].startMin); const datetime t2 = WindowTimeToServer(day, g_win[w].endMin); const int i1 = LowerBound(time, rates_total, t1); if(i1 >= rates_total) continue; // window has not started yet const int i2 = LowerBound(time, rates_total, t2); const int last = i2 - 1; // last bar inside the window if(last < i1) continue; // no bars (weekend / holiday / gap) double hi = high[i1]; double lo = low[i1]; for(int i = i1 + 1; i <= last; i++) { if(high[i] > hi) hi = high[i]; if(low[i] < lo) lo = low[i]; } datetime tEnd = (datetime)((long)time[last] + PeriodSeconds()); if(tEnd > t2) tEnd = t2; DrawWindow(w, day, time[i1], tEnd, hi, lo); } } //--- drop objects of the previous rebuild that are no longer valid for(int i = 0; i < g_prevCount; i++) if(!NameInList(g_prevNames[i], g_names, g_nameCount)) ObjectDelete(0, g_prevNames[i]); //--- swap registries if(ArrayResize(g_prevNames, g_nameCount) != g_nameCount) Print("ICTMacros: could not resize the object registry"); for(int i = 0; i < g_nameCount; i++) g_prevNames[i] = g_names[i]; g_prevCount = g_nameCount; ChartRedraw(); } //| Refresh only the window that is currently open (tick path). | void RefreshLiveWindow(const datetime &time[], const double &high[], const double &low[], const int rates_total) { if(!InpLiveBox || rates_total < 2) return; const datetime lastBar = time[rates_total - 1]; const datetime refDay = DayStart(ReferenceNow()); for(int w = 0; w < g_winCount; w++) { if(!SessionEnabled(g_win[w].session)) continue; const datetime t1 = WindowTimeToServer(refDay, g_win[w].startMin); const datetime t2 = WindowTimeToServer(refDay, g_win[w].endMin); if(lastBar < t1 || lastBar >= t2) continue; // this window is not open right now const int i1 = LowerBound(time, rates_total, t1); if(i1 >= rates_total) return; double hi = high[i1]; double lo = low[i1]; for(int i = i1 + 1; i < rates_total; i++) { if(high[i] > hi) hi = high[i]; if(low[i] < lo) lo = low[i]; } if(hi == g_liveHi && lo == g_liveLo) return; // extremes unchanged: nothing to do const string base = StringFormat("%s%s_%02d_%s", ICT_PREFIX, SessionTag(g_win[w].session), g_win[w].index, DateTag(refDay)); if(ObjectFind(0, base) < 0) return; // box of this window not built yet datetime tEnd = (datetime)((long)lastBar + PeriodSeconds()); if(tEnd > t2) tEnd = t2; ObjectMove(0, base, 0, time[i1], lo); ObjectMove(0, base, 1, tEnd, hi); if(InpMidline) { const string midName = base + "_mid"; if(ObjectFind(0, midName) >= 0) { const double mid = (hi + lo) / 2.0; ObjectMove(0, midName, 0, time[i1], mid); ObjectMove(0, midName, 1, tEnd, mid); } } if(InpShowLabels) { const string labelName = base + "_L"; if(ObjectFind(0, labelName) >= 0) ObjectMove(0, labelName, 0, time[i1], hi); } g_liveHi = hi; g_liveLo = lo; ChartRedraw(); return; } } void LogSummary() { if(!InpVerboseLog) return; int asia = 0, london = 0, ny = 0; for(int i = 0; i < g_winCount; i++) { if(g_win[i].session == ICT_ASIA) asia++; else if(g_win[i].session == ICT_LONDON) london++; else ny++; } PrintFormat("ICTMacros: base=%s server offset=%d min windows=%d (Asia %d, London %d, NY %d) history=%d d objects=%d", TimeBaseName(InpTimeBase), g_srvOffsetSec / 60, g_winCount, asia, london, ny, InpHistoryDays, g_nameCount); if(InpTimeBase == ICT_TB_NEWYORK) { const datetime utcNow = (datetime)((long)TimeTradeServer() - (long)g_srvOffsetSec); const datetime nyNow = (datetime)((long)utcNow + (long)NyUtcOffsetSec(utcNow)); string zoneTag = "EST"; if(IsUsDst(utcNow)) zoneTag = "EDT"; PrintFormat("ICTMacros: New York local time now %s (%s)", TimeToString(nyNow, TIME_DATE | TIME_MINUTES), zoneTag); } } //+------------------------------------------------------------------+ //| Indicator events | //+------------------------------------------------------------------+ int OnInit() { IndicatorSetString(INDICATOR_SHORTNAME, "ICT Macros"); ArrayResize(g_win, 0); g_winCount = 0; g_winCount += ParseWindowList(InpAsiaMacros, ICT_ASIA, g_win); g_winCount += ParseWindowList(InpLondonMacros, ICT_LONDON, g_win); g_winCount += ParseWindowList(InpNYMacros, ICT_NEWYORK, g_win); g_srvOffsetSec = DetectServerOffsetSec(); g_lastBarTime = 0; g_liveHi = 0.0; g_liveLo = 0.0; g_loggedSummary = false; g_capWarned = false; g_orphanSwept = false; g_nameCount = 0; g_prevCount = 0; ArrayResize(g_names, 0); ArrayResize(g_prevNames, 0); if(g_winCount == 0) Print("ICTMacros: no macro windows configured - nothing will be drawn"); if(!InpShowAsia && !InpShowLondon && !InpShowNewYork) Print("ICTMacros: all sessions are disabled - nothing will be drawn"); if(g_winCount > 0 && PeriodSeconds() > 1800) PrintFormat("ICTMacros: chart period is %d s, macro windows may cover 0 or 1 bar (M1-M15 recommended)", PeriodSeconds()); if(InpTimeBase != ICT_TB_SERVER && InpServerOffsetMode == ICT_SRV_AUTO && InpVerboseLog) PrintFormat("ICTMacros: server/UTC offset detected as %d minutes", g_srvOffsetSec / 60); return INIT_SUCCEEDED; } void OnDeinit(const int reason) { for(int i = 0; i < g_nameCount; i++) ObjectDelete(0, g_names[i]); for(int i = 0; i < g_prevCount; i++) ObjectDelete(0, g_prevNames[i]); g_nameCount = 0; g_prevCount = 0; ArrayResize(g_names, 0); ArrayResize(g_prevNames, 0); ChartRedraw(); } 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; const bool newBar = (time[rates_total - 1] != g_lastBarTime); const bool firstPass = (prev_calculated <= 0) || (prev_calculated > rates_total); if(firstPass || newBar) { //--- keep the server offset in sync with broker DST switches if(InpTimeBase != ICT_TB_SERVER && InpServerOffsetMode == ICT_SRV_AUTO) { const int detected = DetectServerOffsetSec(); if(detected != g_srvOffsetSec) { if(InpVerboseLog) PrintFormat("ICTMacros: server/UTC offset changed from %d to %d minutes", g_srvOffsetSec / 60, detected / 60); g_srvOffsetSec = detected; } } g_lastBarTime = time[rates_total - 1]; RebuildAll(time, high, low, rates_total); if(!g_loggedSummary) { g_loggedSummary = true; LogSummary(); } SweepOrphanObjects(); } else RefreshLiveWindow(time, high, low, rates_total); return rates_total; } //+------------------------------------------------------------------+