//+------------------------------------------------------------------+ //| ForexSessionDetector.mq5 | //| Copyright 2023, MetaQuotes Software Corp. | //| https://www.metaquotes.net/ | //+------------------------------------------------------------------+ #property copyright "Copyright 2023, MetaQuotes Software Corp." #property link "https://www.metaquotes.net/" #property version "1.00" #property indicator_chart_window #property indicator_buffers 0 #property indicator_plots 0 //+------------------------------------------------------------------+ //| Session Times | //+------------------------------------------------------------------+ input color SydneyColor = clrRoyalBlue; // Sydney session color input color TokyoColor = clrGreen; // Tokyo session color input color LondonColor = clrRed; // London session color input color NewYorkColor = clrDodgerBlue; // New York session color input int TextSize = 8; // Font size input bool ShowHighLow = true; // Show High/Low prices input bool ShowSessionTimes = true; // Show Session Times //+------------------------------------------------------------------+ //| Global Variables | //+------------------------------------------------------------------+ struct SessionInfo { string name; int startHour; int startMinute; int endHour; int endMinute; color clr; datetime sessionStart; datetime sessionEnd; double high; double low; bool active; }; SessionInfo sessions[4]; string fontFace = "Arial"; //+------------------------------------------------------------------+ //| Custom indicator initialization function | //+------------------------------------------------------------------+ int OnInit() { // Initialize session information sessions[0].name = "Sydney"; sessions[0].startHour = 22; sessions[0].startMinute = 0; sessions[0].endHour = 6; sessions[0].endMinute = 0; sessions[0].clr = SydneyColor; sessions[1].name = "Tokyo"; sessions[1].startHour = 0; sessions[1].startMinute = 0; sessions[1].endHour = 9; sessions[1].endMinute = 0; sessions[1].clr = TokyoColor; sessions[2].name = "London"; sessions[2].startHour = 8; sessions[2].startMinute = 0; sessions[2].endHour = 17; sessions[2].endMinute = 0; sessions[2].clr = LondonColor; sessions[3].name = "New York"; sessions[3].startHour = 13; sessions[3].startMinute = 0; sessions[3].endHour = 22; sessions[3].endMinute = 0; sessions[3].clr = NewYorkColor; return (INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Custom indicator iteration function | //+------------------------------------------------------------------+ 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[]) { // Clear previous objects ObjectsDeleteAll(0, "Session_"); // Process each session for (int i = 0; i < 4; i++) { ProcessSession(i, time, high, low, rates_total); } return (rates_total); } //+------------------------------------------------------------------+ //| Process a single session | //+------------------------------------------------------------------+ void ProcessSession(int sessionIndex, const datetime &time[], const double &high[], const double &low[], int rates_total) { SessionInfo session = sessions[sessionIndex]; // Find current session start and end times datetime currentTime = TimeCurrent(); MqlDateTime currentTimeStruct; TimeToStruct(currentTime, currentTimeStruct); datetime sessionStart, sessionEnd; // Handle sessions that span midnight if (session.startHour > session.endHour) { // Session spans midnight (e.g., Sydney 22:00-6:00) if (currentTimeStruct.hour >= session.startHour || currentTimeStruct.hour < session.endHour) { sessionStart = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.startHour, session.startMinute, 0); if (currentTimeStruct.hour < session.startHour) sessionStart -= 86400; // Previous day sessionEnd = sessionStart + (session.endHour - session.startHour) * 3600 + (session.endMinute - session.startMinute) * 60; if (sessionEnd <= sessionStart) sessionEnd += 86400; session.active = true; } else { sessionStart = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.startHour, session.startMinute, 0); sessionEnd = sessionStart + (session.endHour - session.startHour) * 3600 + (session.endMinute - session.startMinute) * 60; if (sessionEnd <= sessionStart) sessionEnd += 86400; session.active = false; } } else { // Normal session (doesn't span midnight) if (currentTimeStruct.hour >= session.startHour && currentTimeStruct.hour < session.endHour) { sessionStart = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.startHour, session.startMinute, 0); sessionEnd = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.endHour, session.endMinute, 0); session.active = true; } else { sessionStart = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.startHour, session.startMinute, 0); sessionEnd = StructToTime(currentTimeStruct.year, currentTimeStruct.mon, currentTimeStruct.day, session.endHour, session.endMinute, 0); session.active = false; } } session.sessionStart = sessionStart; session.sessionEnd = sessionEnd; // Find high/low for the session int startIndex = iBarShift(NULL, 0, sessionStart); int endIndex = iBarShift(NULL, 0, sessionEnd); if (startIndex == -1 || endIndex == -1) return; session.high = high[ArrayMaximum(high, endIndex, startIndex - endIndex + 1)]; session.low = low[ArrayMinimum(low, endIndex, startIndex - endIndex + 1)]; // Draw session information on chart DrawSessionInfo(session, startIndex, endIndex, sessionIndex); } //+------------------------------------------------------------------+ //| Get chart window price max | //+------------------------------------------------------------------+ double GetWindowPriceMax() { double maxPrice; ChartGetDouble(0, CHART_PRICE_MAX, 0, maxPrice); return maxPrice; } //+------------------------------------------------------------------+ //| Get chart window price min | //+------------------------------------------------------------------+ double GetWindowPriceMin() { double minPrice; ChartGetDouble(0, CHART_PRICE_MIN, 0, minPrice); return minPrice; } //+------------------------------------------------------------------+ //| Draw session information on chart | //+------------------------------------------------------------------+ void DrawSessionInfo(SessionInfo &session, int startIndex, int endIndex, int sessionIndex) { string objName; color textColor = session.clr; double priceMax = GetWindowPriceMax(); double priceMin = GetWindowPriceMin(); // Draw vertical lines for session start/end objName = "Session_" + session.name + "_Start"; ObjectCreate(0, objName, OBJ_VLINE, 0, session.sessionStart, 0); ObjectSetInteger(0, objName, OBJPROP_COLOR, session.clr); ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1); ObjectSetInteger(0, objName, OBJPROP_BACK, true); objName = "Session_" + session.name + "_End"; ObjectCreate(0, objName, OBJ_VLINE, 0, session.sessionEnd, 0); ObjectSetInteger(0, objName, OBJPROP_COLOR, session.clr); ObjectSetInteger(0, objName, OBJPROP_STYLE, STYLE_DOT); ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1); ObjectSetInteger(0, objName, OBJPROP_BACK, true); // Draw session name label if (ShowSessionTimes) { objName = "Session_" + session.name + "_Label"; ObjectCreate(0, objName, OBJ_TEXT, 0, session.sessionStart, priceMax - (priceMax - priceMin) * 0.1 * (sessionIndex + 1)); ObjectSetString(0, objName, OBJPROP_TEXT, session.name + " " + TimeToString(session.sessionStart, TIME_MINUTES) + " - " + TimeToString(session.sessionEnd, TIME_MINUTES)); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, TextSize); ObjectSetString(0, objName, OBJPROP_FONT, fontFace); ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_LEFT_UPPER); ObjectSetInteger(0, objName, OBJPROP_BACK, false); } // Draw high/low prices if enabled if (ShowHighLow) { objName = "Session_" + session.name + "_High"; ObjectCreate(0, objName, OBJ_TEXT, 0, session.sessionStart + (session.sessionEnd - session.sessionStart) / 2, session.high); ObjectSetString(0, objName, OBJPROP_TEXT, "H: " + DoubleToString(session.high, _Digits)); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, TextSize); ObjectSetString(0, objName, OBJPROP_FONT, fontFace); ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_LOWER); ObjectSetInteger(0, objName, OBJPROP_BACK, false); objName = "Session_" + session.name + "_Low"; ObjectCreate(0, objName, OBJ_TEXT, 0, session.sessionStart + (session.sessionEnd - session.sessionStart) / 2, session.low); ObjectSetString(0, objName, OBJPROP_TEXT, "L: " + DoubleToString(session.low, _Digits)); ObjectSetInteger(0, objName, OBJPROP_COLOR, textColor); ObjectSetInteger(0, objName, OBJPROP_FONTSIZE, TextSize); ObjectSetString(0, objName, OBJPROP_FONT, fontFace); ObjectSetInteger(0, objName, OBJPROP_ANCHOR, ANCHOR_UPPER); ObjectSetInteger(0, objName, OBJPROP_BACK, false); } // Highlight active session if (session.active) { objName = "Session_" + session.name + "_Background"; ObjectCreate(0, objName, OBJ_RECTANGLE, 0, session.sessionStart, priceMax, session.sessionEnd, priceMin); ObjectSetInteger(0, objName, OBJPROP_COLOR, session.clr); ObjectSetInteger(0, objName, OBJPROP_BGCOLOR, session.clr); ObjectSetInteger(0, objName, OBJPROP_BACK, true); ObjectSetInteger(0, objName, OBJPROP_FILL, true); ObjectSetInteger(0, objName, OBJPROP_WIDTH, 1); } } //+------------------------------------------------------------------+ //| Create datetime from components | //+------------------------------------------------------------------+ datetime StructToTime(int year, int month, int day, int hour, int minute, int second) { MqlDateTime mdt; mdt.year = year; mdt.mon = month; mdt.day = day; mdt.hour = hour; mdt.min = minute; mdt.sec = second; return StructToTime(mdt); } //+------------------------------------------------------------------+