/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class // ------------------------------------------------- // Name: XCSessionDetector // Description: Forex Session Detector with Event System // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property library #property copyright "Copyright 2023, SaherElm IT Center" #property link "https://saherelm.ir" #property version "1.00" #property description "SaherElm Forex Session Detector Class" #property strict // // Imports ... #include "../Classes/x-saherelm.base.class.mq5" #include "../Classes/x-saherelm.x-alert.class.mq5" #include "../Libraries/x-saherelm.common.extensions.lib.mq5" #include "../Libraries/x-saherelm.x-enums.lib.mq5" // // Definitions ... // #region Session Types ... // // Forex Session Types ... enum ENUM_X_SESSION_TYPE { X_SESSION_NONE = 0, // None X_SESSION_SYDNEY = 1, // Sydney Session X_SESSION_TOKYO = 2, // Tokyo Session X_SESSION_LONDON = 3, // London Session X_SESSION_NEWYORK = 4, // New York Session X_SESSION_FRANKFURT = 5, // Frankfurt Session X_SESSION_SINGAPORE = 6, // Singapore Session X_SESSION_HONGKONG = 7, // Hong Kong Session }; /** * Validate ... * * @param value: ENUM_X_SESSION_TYPE member ... * * @return ( bool ) */ bool IsValid(ENUM_X_SESSION_TYPE value) { // bool result = false; // result = value != NULL && value != X_SESSION_NONE; // return result; } bool IsXValid(ENUM_X_SESSION_TYPE value) { return IsValid(value); } /** * Cpnverts to String ... * * @param value: ENUM_X_SESSION_TYPE ... * * @return ( string ) */ string ToXString(ENUM_X_SESSION_TYPE value) { // string result = NULL; // if (value == NULL) { return result; } // switch (value) { // case X_SESSION_NONE: result = "NONE"; break; // case X_SESSION_SYDNEY: result = "SYDNEY"; break; // case X_SESSION_TOKYO: result = "TOKYO"; break; // case X_SESSION_LONDON: result = "LONDON"; break; // case X_SESSION_NEWYORK: result = "NEWYORK"; break; // case X_SESSION_FRANKFURT: result = "FRANKFURT"; break; // case X_SESSION_SINGAPORE: result = "SINGAPORE"; break; // case X_SESSION_HONGKONG: result = "HONGKONG"; break; } // return result; } /** * Parse From String ... * * @param value: string ... * * @return ( ENUM_X_SESSION_TYPE ) */ ENUM_X_SESSION_TYPE FromXString(string value) { // ENUM_X_SESSION_TYPE result = X_SESSION_NONE; // // Validate ... if (!IsXValid(value)) { return result; } // if (value == ToXString(X_SESSION_NONE)) { result = X_SESSION_NONE; } else if (value == ToXString(X_SESSION_SYDNEY)) { result = X_SESSION_SYDNEY; } else if (value == ToXString(X_SESSION_TOKYO)) { result = X_SESSION_TOKYO; } else if (value == ToXString(X_SESSION_LONDON)) { result = X_SESSION_LONDON; } else if (value == ToXString(X_SESSION_NEWYORK)) { result = X_SESSION_NEWYORK; } else if (value == ToXString(X_SESSION_FRANKFURT)) { result = X_SESSION_FRANKFURT; } else if (value == ToXString(X_SESSION_SINGAPORE)) { result = X_SESSION_SINGAPORE; } else if (value == ToXString(X_SESSION_HONGKONG)) { result = X_SESSION_HONGKONG; } // return result; } // #endregion // #region Session Overlap Types ... // // Session Overlap Types ... enum ENUM_X_SESSION_OVERLAP_TYPE { X_OVERLAP_NONE = 0, // None X_OVERLAP_SYDNEY_TOKYO = 1, // Sydney-Tokyo Overlap X_OVERLAP_TOKYO_LONDON = 2, // Tokyo-London Overlap X_OVERLAP_LONDON_NEWYORK = 3, // London-New York Overlap (Most Liquid) X_OVERLAP_LONDON_FRANKFURT = 4, // London-Frankfurt Overlap X_OVERLAP_NEWYORK_SYDNEY = 5, // New York-Sydney Overlap X_OVERLAP_TOKYO_SINGAPORE = 6, // Tokyo-Singapore Overlap X_OVERLAP_HONGKONG_TOKYO = 7, // Hong Kong-Tokyo Overlap }; /** * Validate ... * * @param value: ENUM_X_SESSION_OVERLAP_TYPE member ... * * @return ( bool ) */ bool IsValid(ENUM_X_SESSION_OVERLAP_TYPE value) { // bool result = false; // result = value != NULL && value != X_OVERLAP_NONE; // return result; } bool IsXValid(ENUM_X_SESSION_OVERLAP_TYPE value) { return IsValid(value); } /** * Cpnverts to String ... * * @param value: ENUM_X_SESSION_OVERLAP_TYPE ... * * @return ( string ) */ string ToXString(ENUM_X_SESSION_OVERLAP_TYPE value) { // string result = NULL; // // Validate ... if (value == NULL) { return result; } // switch (value) { // case X_OVERLAP_NONE: result = "NONE"; break; // case X_OVERLAP_SYDNEY_TOKYO: result = "SYDNEY_TOKYO"; break; // case X_OVERLAP_TOKYO_LONDON: result = "TOKYO_LONDON"; break; // case X_OVERLAP_LONDON_NEWYORK: result = "LONDON_NEWYORK"; break; // case X_OVERLAP_LONDON_FRANKFURT: result = "LONDON_FRANKFURT"; break; // case X_OVERLAP_NEWYORK_SYDNEY: result = "NEWYORK_SYDNEY"; break; // case X_OVERLAP_TOKYO_SINGAPORE: result = "TOKYO_SINGAPORE"; break; // case X_OVERLAP_HONGKONG_TOKYO: result = "HONGKONG_TOKYO"; break; } // return result; } // #endregion // #region Session State ... // // Session State ... enum ENUM_X_SESSION_STATE { X_SESSION_STATE_INACTIVE = 0, // Session Not Active X_SESSION_STATE_ACTIVE = 1, // Session Active X_SESSION_STATE_STARTING = 2, // Session Starting (First Bar) X_SESSION_STATE_ENDING = 3, // Session Ending (Last Bar) }; /** * Check a Session State is Active or not ... * * @param value: ENUM_X_SESSION_STATE * * @return ( bool ) */ bool IsXActive(ENUM_X_SESSION_STATE value) { // bool result = false; // result = value == X_SESSION_STATE_ACTIVE; // return result; } // #endregion // #region Event Handler Types ... typedef void (*TOnSessionStart)(ENUM_X_SESSION_TYPE session, datetime startTime); typedef void (*TOnSessionEnd)(ENUM_X_SESSION_TYPE session, datetime endTime); typedef void (*TOnOverlapStart)(ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime startTime); typedef void (*TOnOverlapEnd)(ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime endTime); typedef void (*TOnSessionStateChange)(ENUM_X_SESSION_TYPE session, ENUM_X_SESSION_STATE state); // #endregion // #region Session Configuration ... struct XSessionConfig { // string name; // Session Name ENUM_X_SESSION_TYPE type; // Session Type string startTime; // Start Time (HH:MM) string endTime; // End Time (HH:MM) color sessionColor; // Session Color for Visualization bool isActive; // Is Session Active datetime lastStartTime; // Last Start Time datetime lastEndTime; // Last End Time ENUM_X_SESSION_STATE state; // Current State // // Constructor ... XSessionConfig() { Clean(); } // // Tools ... // void Clean() { // name = NULL; type = X_SESSION_NONE; startTime = NULL; endTime = NULL; sessionColor = CLR_NONE; isActive = false; lastStartTime = NULL; lastEndTime = NULL; state = X_SESSION_STATE_INACTIVE; // ZeroMemory(this); } // void Default(ENUM_X_SESSION_TYPE _type) { // Clean(); // // Validate ... if (!IsXValid(_type)) { return; } // type = _type; name = ToXString(_type); // switch (_type) { // case X_SESSION_SYDNEY: startTime = "22:00"; endTime = "07:00"; sessionColor = clrAqua; break; // case X_SESSION_TOKYO: startTime = "00:00"; endTime = "09:00"; sessionColor = clrYellow; break; // case X_SESSION_SINGAPORE: startTime = "01:00"; endTime = "09:00"; sessionColor = clrOrange; break; // case X_SESSION_HONGKONG: startTime = "01:00"; endTime = "09:00"; sessionColor = clrGold; break; // case X_SESSION_FRANKFURT: startTime = "07:00"; endTime = "16:00"; sessionColor = clrLime; break; // case X_SESSION_LONDON: startTime = "08:00"; endTime = "17:00"; sessionColor = clrGreen; break; // case X_SESSION_NEWYORK: startTime = "13:00"; endTime = "22:00"; sessionColor = clrRed; break; // default: name = "Unknown"; startTime = "00:00"; endTime = "00:00"; sessionColor = clrGray; break; } } // bool IsValid() { // bool result = false; // result = IsXValid(type) && IsXValid(name) && IsXValid(startTime) && IsXValid(endTime); // return result; } }; // #endregion // #region Overlap Configuration ... struct XOverlapConfig { // string name; // Overlap Name ENUM_X_SESSION_OVERLAP_TYPE type; // Overlap Type ENUM_X_SESSION_TYPE session1; // First Session ENUM_X_SESSION_TYPE session2; // Second Session string overlapStartTime; // Overlap Start Time string overlapEndTime; // Overlap End Time color overlapColor; // Overlap Color bool isActive; // Is Overlap Active datetime lastStartTime; // Last Start Time datetime lastEndTime; // Last End Time // // Constructor ... XOverlapConfig() { Clean(); } // // Tools ... // void Clean() { // name = NULL; type = X_OVERLAP_NONE; session1 = X_SESSION_NONE; session2 = X_SESSION_NONE; overlapStartTime = NULL; overlapEndTime = NULL; overlapColor = CLR_NONE; isActive = false; lastStartTime = NULL; lastEndTime = NULL; // ZeroMemory(this); } // void Default(ENUM_X_SESSION_OVERLAP_TYPE _type) { // Clean(); // if (!IsXValid(_type)) { return; } // type = _type; name = ToXString(_type); // switch (_type) { // case X_OVERLAP_SYDNEY_TOKYO: session1 = X_SESSION_SYDNEY; session2 = X_SESSION_TOKYO; overlapStartTime = "00:00"; overlapEndTime = "07:00"; overlapColor = clrYellow; break; // case X_OVERLAP_TOKYO_LONDON: session1 = X_SESSION_TOKYO; session2 = X_SESSION_LONDON; overlapStartTime = "08:00"; overlapEndTime = "09:00"; overlapColor = clrLime; break; // case X_OVERLAP_LONDON_NEWYORK: session1 = X_SESSION_LONDON; session2 = X_SESSION_NEWYORK; overlapStartTime = "13:00"; overlapEndTime = "17:00"; overlapColor = clrOrange; break; // case X_OVERLAP_LONDON_FRANKFURT: session1 = X_SESSION_LONDON; session2 = X_SESSION_FRANKFURT; overlapStartTime = "08:00"; overlapEndTime = "16:00"; overlapColor = clrGreen; break; // case X_OVERLAP_NEWYORK_SYDNEY: session1 = X_SESSION_NEWYORK; session2 = X_SESSION_SYDNEY; overlapStartTime = "22:00"; overlapEndTime = "22:00"; overlapColor = clrPurple; break; // case X_OVERLAP_TOKYO_SINGAPORE: session1 = X_SESSION_TOKYO; session2 = X_SESSION_SINGAPORE; overlapStartTime = "01:00"; overlapEndTime = "09:00"; overlapColor = clrGold; break; // case X_OVERLAP_HONGKONG_TOKYO: session1 = X_SESSION_HONGKONG; session2 = X_SESSION_TOKYO; overlapStartTime = "01:00"; overlapEndTime = "09:00"; overlapColor = clrPink; break; // default: name = "Unknown"; session1 = X_SESSION_NONE; session2 = X_SESSION_NONE; overlapStartTime = "00:00"; overlapEndTime = "00:00"; overlapColor = clrGray; break; } } // bool IsValid() { // bool result = false; // result = IsXValid(type) && IsXValid(name) && IsXValid(session1) && IsXValid(session2); // return result; } }; // #endregion // // Implementation ... class XCSessionDetector : public XCBase { // public: // // Props ... // XCAlert *alert; // Alert Handler XSessionConfig sessions[]; // All Sessions XOverlapConfig overlaps[]; // All Overlaps // // Event Handlers ... TOnSessionEnd mOnSessionEndHandlers[]; TOnOverlapEnd mOnOverlapEndHandlers[]; TOnSessionStart mOnSessionStartHandlers[]; TOnOverlapStart mOnOverlapStartHandlers[]; TOnSessionStateChange mOnSessionStateChangeHandlers[]; // // Time Tracking ... datetime lastCheckTime; // Last Check Time int timezoneOffset; // Timezone Offset (in Hours) bool useServerTime; // Use Server Time or UTC // // Constructor ... XCSessionDetector( XCAlert *_alert = NULL, bool _useServerTime = true, int _timezoneOffset = 0 // ) { alert = _alert; useServerTime = _useServerTime; timezoneOffset = _timezoneOffset; lastCheckTime = NULL; // InitializeSessions(); InitializeOverlaps(); } // // Deconstructor ... ~XCSessionDetector() { // Clean(sessions); Clean(overlaps); Clean(mOnSessionEndHandlers); Clean(mOnOverlapEndHandlers); Clean(mOnSessionStartHandlers); Clean(mOnOverlapStartHandlers); Clean(mOnSessionStateChangeHandlers); // ZeroMemory(alert); } // #region Getters ... /** * Get Session Config by Type * * @param sessionType: ENUM_X_SESSION_TYPE * * @return ( XSessionConfig ) */ XSessionConfig GetSessionConfig(ENUM_X_SESSION_TYPE sessionType) { // XSessionConfig result; result.Clean(); // if (!IsXValid(sessionType)) { return result; } // int sessionsCount = ArraySize(sessions); for (int i = 0; i < sessionsCount; i++) { // if (sessions[i].type == sessionType) { result = sessions[i]; break; } } // return result; } /** * Get Overlap Config by Type * * @param overlapType: ENUM_X_SESSION_OVERLAP_TYPE * * @return ( XOverlapConfig ) */ XOverlapConfig GetOverlapConfig(ENUM_X_SESSION_OVERLAP_TYPE overlapType) { // XOverlapConfig result; result.Clean(); // if (!IsXValid(overlapType)) { return result; } // int overlapsCount = ArraySize(overlaps); for (int i = 0; i < overlapsCount; i++) { // if (overlaps[i].type == overlapType) { result = overlaps[i]; break; } } // return result; } /** * Get Current Active Session * * @param currentTime: datetime * * @return ( ENUM_X_SESSION_TYPE ) */ ENUM_X_SESSION_TYPE GetCurrentSession(datetime currentTime = NULL) { // ENUM_X_SESSION_TYPE result = X_SESSION_NONE; // if (!IsXValid(currentTime)) { currentTime = GetCurrentTime(); } // int sessionsCount = ArraySize(sessions); for (int i = 0; i < sessionsCount; i++) { // if (IsSessionActive(sessions[i].type, currentTime)) { result = sessions[i].type; break; } } // return result; } /** * Get Current Active Overlap * * @param currentTime: datetime * * @return ( ENUM_X_SESSION_OVERLAP_TYPE ) */ ENUM_X_SESSION_OVERLAP_TYPE GetCurrentOverlap(datetime currentTime = NULL) { // ENUM_X_SESSION_OVERLAP_TYPE result = X_OVERLAP_NONE; // if (!IsXValid(currentTime)) { currentTime = GetCurrentTime(); } // int overlapsCount = ArraySize(overlaps); for (int i = 0; i < overlapsCount; i++) { // if (IsOverlapActive(overlaps[i], currentTime)) { result = overlaps[i].type; break; } } // return result; } // #endregion // #region Tools & Actions ... /** * Initialize All Forex Sessions */ void InitializeSessions() { // Clean(sessions); // // Sydney Session XSessionConfig sydney; sydney.Default(X_SESSION_SYDNEY); AddRef(sydney, sessions); // // Tokyo Session XSessionConfig tokyo; tokyo.Default(X_SESSION_TOKYO); AddRef(tokyo, sessions); // // Singapore Session XSessionConfig singapore; singapore.Default(X_SESSION_SINGAPORE); AddRef(singapore, sessions); // // Hong Kong Session XSessionConfig hongkong; hongkong.Default(X_SESSION_HONGKONG); AddRef(hongkong, sessions); // // Frankfurt Session XSessionConfig frankfurt; frankfurt.Default(X_SESSION_FRANKFURT); AddRef(frankfurt, sessions); // // London Session XSessionConfig london; london.Default(X_SESSION_LONDON); AddRef(london, sessions); // // New York Session XSessionConfig newyork; newyork.Default(X_SESSION_NEWYORK); AddRef(newyork, sessions); } /** * Initialize All Session Overlaps */ void InitializeOverlaps() { // Clean(overlaps); // // Sydney-Tokyo Overlap XOverlapConfig sydneyTokyo; sydneyTokyo.Default(X_OVERLAP_SYDNEY_TOKYO); AddRef(sydneyTokyo, overlaps); // // Tokyo-London Overlap XOverlapConfig tokyoLondon; tokyoLondon.Default(X_OVERLAP_TOKYO_LONDON); AddRef(tokyoLondon, overlaps); // // London-New York Overlap (Most Important) XOverlapConfig londonNewYork; londonNewYork.Default(X_OVERLAP_LONDON_NEWYORK); AddRef(londonNewYork, overlaps); // // London-Frankfurt Overlap XOverlapConfig londonFrankfurt; londonFrankfurt.Default(X_OVERLAP_LONDON_FRANKFURT); AddRef(londonFrankfurt, overlaps); // // New York-Sydney Overlap XOverlapConfig newyorkSydney; newyorkSydney.Default(X_OVERLAP_NEWYORK_SYDNEY); AddRef(newyorkSydney, overlaps); } /** * Get Current Time (Adjusted for Timezone) * * @return ( datetime ) */ datetime GetCurrentTime() { // datetime currentTime = useServerTime ? TimeCurrent() : TimeGMT(); // if (timezoneOffset != 0) { currentTime = (datetime)((int)currentTime + (timezoneOffset * 3600)); } // return currentTime; } /** * Parse Time String to DateTime (Today's Date + Time) * * @param timeString: String (HH:MM format) * @param baseDate: datetime (Base Date) * * @return ( datetime ) */ datetime ParseTimeToDateTime( string timeString, datetime baseDate // ) { // datetime result = NULL; // if (!IsXValid(timeString)) { return result; } // MqlDateTime dt; TimeToStruct(baseDate, dt); // string parts[]; int partsCount = SplitContent(parts, timeString, ":"); if (partsCount >= 2) { // dt.hour = (int)parts[0]; dt.min = (int)parts[1]; dt.sec = 0; // result = StructToTime(dt); } // return result; } /** * Check if Current Time is Within Session Time Range * * @param startTime: String (HH:MM) * @param endTime: String (HH:MM) * @param currentTime: datetime * * @return ( bool ) */ bool IsTimeInRange( string startTime, string endTime, datetime currentTime // ) { // bool result = false; // if (!IsXValid(startTime) || !IsXValid(endTime)) { return result; } // datetime todayStart = ParseTimeToDateTime(startTime, currentTime); datetime todayEnd = ParseTimeToDateTime(endTime, currentTime); // // Handle Sessions that Cross Midnight if (todayEnd < todayStart) { // // Session crosses midnight result = (currentTime >= todayStart || currentTime < todayEnd); } else { // // Normal session result = (currentTime >= todayStart && currentTime < todayEnd); } // return result; } /** * Check if Current Time is Within Overlap Time Range * * @param overlap: XOverlapConfig reference * @param currentTime: datetime * * @return ( bool ) */ bool IsOverlapActive( XOverlapConfig &overlap, datetime currentTime // ) { // bool result = false; // if (!overlap.IsValid()) { return result; } // // Check if Both Sessions are Active bool session1Active = IsSessionActive(overlap.session1, currentTime); bool session2Active = IsSessionActive(overlap.session2, currentTime); // result = session1Active && session2Active; // return result; } /** * Check if Specific Session is Active * * @param sessionType: ENUM_X_SESSION_TYPE * @param currentTime: datetime * * @return ( bool ) */ bool IsSessionActive( ENUM_X_SESSION_TYPE sessionType, datetime currentTime // ) { // bool result = false; // int sessionsCount = ArraySize(sessions); for (int i = 0; i < sessionsCount; i++) { // if (sessions[i].type == sessionType) { // result = IsTimeInRange( sessions[i].startTime, sessions[i].endTime, currentTime // ); // break; } } // return result; } /** * Get Active Sessions * * @param activeSessions: ENUM_X_SESSION_TYPE collection reference * @param currentTime: datetime * * @return ( int ) */ int GetActiveSessions( ENUM_X_SESSION_TYPE &activeSessions[], datetime currentTime = NULL // ) { // int result = 0; Clean(activeSessions); // if (!IsXValid(currentTime)) { currentTime = GetCurrentTime(); } // int sessionsCount = ArraySize(sessions); for (int i = 0; i < sessionsCount; i++) { // if (IsSessionActive(sessions[i].type, currentTime)) { Add(sessions[i].type, activeSessions); } } // result = ArraySize(activeSessions); // return result; } /** * Get Active Overlaps * * @param activeOverlaps: ENUM_X_SESSION_OVERLAP_TYPE collection reference * @param currentTime: datetime * * @return ( int ) */ int GetActiveOverlaps( ENUM_X_SESSION_OVERLAP_TYPE &activeOverlaps[], datetime currentTime = NULL // ) { // int result = 0; Clean(activeOverlaps); // if (!IsXValid(currentTime)) { currentTime = GetCurrentTime(); } // int overlapsCount = ArraySize(overlaps); for (int i = 0; i < overlapsCount; i++) { // if (IsOverlapActive(overlaps[i], currentTime)) { Add(overlaps[i].type, activeOverlaps); } } // result = ArraySize(activeOverlaps); // return result; } // #endregion // #region Processors ... /** * Process Session Detection (Call on Every Tick or Bar) * * @param currentTime: datetime (Optional, uses current time if NULL) */ void Process(datetime currentTime = NULL) { // if (!IsXValid(currentTime)) { currentTime = GetCurrentTime(); } // // Check if Enough Time Passed Since Last Check (Prevent Multiple Fires) if (IsXValid(lastCheckTime) && (int)currentTime - (int)lastCheckTime < 60) // Minimum 1 minute between checks { return; } // lastCheckTime = currentTime; // // Process All Sessions int sessionsCount = ArraySize(sessions); for (int i = 0; i < sessionsCount; i++) { ProcessSession(sessions[i], currentTime); } // // Process All Overlaps int overlapsCount = ArraySize(overlaps); for (int i = 0; i < overlapsCount; i++) { ProcessOverlap(overlaps[i], currentTime); } } /** * Process Individual Session * * @param session: XSessionConfig reference * @param currentTime: datetime */ void ProcessSession( XSessionConfig &session, datetime currentTime // ) { // if (!session.IsValid()) { return; } // bool wasActive = session.isActive; bool isActive = IsTimeInRange( session.startTime, session.endTime, currentTime); // // Session Started if (isActive && !wasActive) { // session.isActive = true; session.lastStartTime = currentTime; session.state = X_SESSION_STATE_STARTING; // NotifySessionStart(session.type, currentTime); NotifySessionStateChange(session.type, session.state); // // Log Alert if (alert != NULL) { // string message = "Session Started: " + session.name + " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS); // alert.SendAlert(message); } } // // Session Ended else if (!isActive && wasActive) { // session.isActive = false; session.lastEndTime = currentTime; session.state = X_SESSION_STATE_ENDING; // NotifySessionEnd(session.type, currentTime); NotifySessionStateChange(session.type, session.state); // // Log Alert if (alert != NULL) { // string message = "Session Ended: " + session.name + " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS); // alert.SendAlert(message); } } // // Session Active (Ongoing) else if (isActive && wasActive) { session.state = X_SESSION_STATE_ACTIVE; } // // Session Inactive else { session.state = X_SESSION_STATE_INACTIVE; } } /** * Process Individual Overlap * * @param overlap: XOverlapConfig reference * @param currentTime: datetime */ void ProcessOverlap( XOverlapConfig &overlap, datetime currentTime // ) { // if (!overlap.IsValid()) { return; } // bool wasActive = overlap.isActive; bool isActive = IsOverlapActive(overlap, currentTime); // // Overlap Started if (isActive && !wasActive) { // overlap.isActive = true; overlap.lastStartTime = currentTime; // NotifyOverlapStart(overlap.type, currentTime); // // Log Alert if (alert != NULL) { // string message = "Overlap Started: " + overlap.name + " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS); // alert.SendAlert(message); } } // // Overlap Ended else if (!isActive && wasActive) { // overlap.isActive = false; overlap.lastEndTime = currentTime; // NotifyOverlapEnd(overlap.type, currentTime); // // Log Alert if (alert != NULL) { // string message = "Overlap Ended: " + overlap.name + " at " + TimeToString(currentTime, TIME_DATE | TIME_SECONDS); // alert.SendAlert(message); } } } // #endregion // #region Event Listener Registration ... /** * Add Session Start Event Handler * * @param handler: TOnSessionStart */ void AddOnSessionStartHandler(TOnSessionStart handler) { // if (handler == NULL) { return; } // Add(handler, mOnSessionStartHandlers); } /** * Add Session End Event Handler * * @param handler: TOnSessionEnd */ void AddOnSessionEndHandler(TOnSessionEnd handler) { // if (handler == NULL) { return; } // Add(handler, mOnSessionEndHandlers); } /** * Add Overlap Start Event Handler * * @param handler: TOnOverlapStart */ void AddOnOverlapStartHandler(TOnOverlapStart handler) { // if (handler == NULL) { return; } // Add(handler, mOnOverlapStartHandlers); } /** * Add Overlap End Event Handler * * @param handler: TOnOverlapEnd */ void AddOnOverlapEndHandler(TOnOverlapEnd handler) { // if (handler == NULL) { return; } // Add(handler, mOnOverlapEndHandlers); } /** * Add Session State Change Event Handler * * @param handler: TOnSessionStateChange */ void AddOnSessionStateChangeHandler(TOnSessionStateChange handler) { // if (handler == NULL) { return; } // Add(handler, mOnSessionStateChangeHandlers); } // #endregion // protected: // // #region Event Notifiers ... /** * Notify Session Start Event * * @param session: ENUM_X_SESSION_TYPE * @param startTime: datetime */ void NotifySessionStart( ENUM_X_SESSION_TYPE session, datetime startTime // ) { // // Validate ... if (!IsXValid(session)) { return; } // // Multiple Handlers ... int count = ArraySize(mOnSessionStartHandlers); for (int i = 0; i < count; i++) { // mOnSessionStartHandlers[i]( session, startTime // ); } } /** * Notify Session End Event * * @param session: ENUM_X_SESSION_TYPE * @param endTime: datetime */ void NotifySessionEnd( ENUM_X_SESSION_TYPE session, datetime endTime // ) { // // Validate ... if (!IsXValid(session)) { return; } // // Multiple Handlers ... int count = ArraySize(mOnSessionEndHandlers); for (int i = 0; i < count; i++) { // mOnSessionEndHandlers[i]( session, endTime // ); } } /** * Notify Overlap Start Event * * @param overlap: ENUM_X_SESSION_OVERLAP_TYPE * @param startTime: datetime */ void NotifyOverlapStart( ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime startTime // ) { // // Validate ... if (!IsXValid(overlap)) { return; } // // Multiple Handlers ... int count = ArraySize(mOnOverlapStartHandlers); for (int i = 0; i < count; i++) { // mOnOverlapStartHandlers[i]( overlap, startTime // ); } } /** * Notify Overlap End Event * * @param overlap: ENUM_X_SESSION_OVERLAP_TYPE * @param endTime: datetime */ void NotifyOverlapEnd( ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime endTime // ) { // // Validate ... if (!IsXValid(overlap)) { return; } // // Multiple Handlers ... int count = ArraySize(mOnOverlapEndHandlers); for (int i = 0; i < count; i++) { // mOnOverlapEndHandlers[i]( overlap, endTime // ); } } /** * Notify Session State Change Event * * @param session: ENUM_X_SESSION_TYPE * @param state: ENUM_X_SESSION_STATE */ void NotifySessionStateChange( ENUM_X_SESSION_TYPE session, ENUM_X_SESSION_STATE state // ) { // // Validate ... if (!IsXValid(session)) { return; } // // Multiple Handlers ... int count = ArraySize(mOnSessionStateChangeHandlers); for (int i = 0; i < count; i++) { // mOnSessionStateChangeHandlers[i]( session, state); } } // #endregion private: // }; //