Files
MQL5Data/Classes/x-saherelm.session-detector.class.mq5
T
2026-02-27 01:46:11 +03:30

1657 lines
36 KiB
Plaintext

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