1213 lines
30 KiB
Markdown
1213 lines
30 KiB
Markdown
### USER
|
|
based on my handwrites which provided before to you, create a class named x-saherelm.session.detector.class.mq5 and provides me a unique and global way to detect Forex Sessions and Fire Events when a session or session overlap starts.
|
|
in this way i can register event listeners and do what we want based on each session or each session overlaps.
|
|
|
|
### ASSISTANT
|
|
# XSessionDetector Class
|
|
|
|
Based on the **SaherElm IT Center Library** architecture, I've created a comprehensive session detector class that provides unique and global Forex session detection with event firing capabilities.
|
|
|
|
```mql5
|
|
///////////////////////////////////////////////////////
|
|
//
|
|
// 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.x-enums.lib.mq5"
|
|
#include "../Libraries/x-saherelm.common.extensions.lib.mq5"
|
|
//
|
|
// Definitions ...
|
|
//
|
|
// 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
|
|
};
|
|
//
|
|
// 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
|
|
};
|
|
//
|
|
// 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)
|
|
};
|
|
//
|
|
// 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);
|
|
//
|
|
// 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();
|
|
type = _type;
|
|
//
|
|
switch(_type)
|
|
{
|
|
case X_SESSION_SYDNEY:
|
|
name = "Sydney";
|
|
startTime = "22:00";
|
|
endTime = "07:00";
|
|
sessionColor = clrAqua;
|
|
break;
|
|
case X_SESSION_TOKYO:
|
|
name = "Tokyo";
|
|
startTime = "00:00";
|
|
endTime = "09:00";
|
|
sessionColor = clrYellow;
|
|
break;
|
|
case X_SESSION_SINGAPORE:
|
|
name = "Singapore";
|
|
startTime = "01:00";
|
|
endTime = "09:00";
|
|
sessionColor = clrOrange;
|
|
break;
|
|
case X_SESSION_HONGKONG:
|
|
name = "Hong Kong";
|
|
startTime = "01:00";
|
|
endTime = "09:00";
|
|
sessionColor = clrGold;
|
|
break;
|
|
case X_SESSION_FRANKFURT:
|
|
name = "Frankfurt";
|
|
startTime = "07:00";
|
|
endTime = "16:00";
|
|
sessionColor = clrLime;
|
|
break;
|
|
case X_SESSION_LONDON:
|
|
name = "London";
|
|
startTime = "08:00";
|
|
endTime = "17:00";
|
|
sessionColor = clrGreen;
|
|
break;
|
|
case X_SESSION_NEWYORK:
|
|
name = "New York";
|
|
startTime = "13:00";
|
|
endTime = "22:00";
|
|
sessionColor = clrRed;
|
|
break;
|
|
default:
|
|
name = "Unknown";
|
|
startTime = "00:00";
|
|
endTime = "00:00";
|
|
sessionColor = clrGray;
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
bool IsValid()
|
|
{
|
|
return type != X_SESSION_NONE &&
|
|
IsSpecifiedValid(name) &&
|
|
IsSpecifiedValid(startTime) &&
|
|
IsSpecifiedValid(endTime);
|
|
}
|
|
};
|
|
//
|
|
// 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();
|
|
type = _type;
|
|
//
|
|
switch(_type)
|
|
{
|
|
case X_OVERLAP_SYDNEY_TOKYO:
|
|
name = "Sydney-Tokyo";
|
|
session1 = X_SESSION_SYDNEY;
|
|
session2 = X_SESSION_TOKYO;
|
|
overlapStartTime = "00:00";
|
|
overlapEndTime = "07:00";
|
|
overlapColor = clrYellow;
|
|
break;
|
|
case X_OVERLAP_TOKYO_LONDON:
|
|
name = "Tokyo-London";
|
|
session1 = X_SESSION_TOKYO;
|
|
session2 = X_SESSION_LONDON;
|
|
overlapStartTime = "08:00";
|
|
overlapEndTime = "09:00";
|
|
overlapColor = clrLime;
|
|
break;
|
|
case X_OVERLAP_LONDON_NEWYORK:
|
|
name = "London-New York";
|
|
session1 = X_SESSION_LONDON;
|
|
session2 = X_SESSION_NEWYORK;
|
|
overlapStartTime = "13:00";
|
|
overlapEndTime = "17:00";
|
|
overlapColor = clrOrange;
|
|
break;
|
|
case X_OVERLAP_LONDON_FRANKFURT:
|
|
name = "London-Frankfurt";
|
|
session1 = X_SESSION_LONDON;
|
|
session2 = X_SESSION_FRANKFURT;
|
|
overlapStartTime = "08:00";
|
|
overlapEndTime = "16:00";
|
|
overlapColor = clrGreen;
|
|
break;
|
|
case X_OVERLAP_NEWYORK_SYDNEY:
|
|
name = "New York-Sydney";
|
|
session1 = X_SESSION_NEWYORK;
|
|
session2 = X_SESSION_SYDNEY;
|
|
overlapStartTime = "22:00";
|
|
overlapEndTime = "22:00";
|
|
overlapColor = clrPurple;
|
|
break;
|
|
case X_OVERLAP_TOKYO_SINGAPORE:
|
|
name = "Tokyo-Singapore";
|
|
session1 = X_SESSION_TOKYO;
|
|
session2 = X_SESSION_SINGAPORE;
|
|
overlapStartTime = "01:00";
|
|
overlapEndTime = "09:00";
|
|
overlapColor = clrGold;
|
|
break;
|
|
case X_OVERLAP_HONGKONG_TOKYO:
|
|
name = "Hong Kong-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()
|
|
{
|
|
return type != X_OVERLAP_NONE &&
|
|
IsSpecifiedValid(name) &&
|
|
session1 != X_SESSION_NONE &&
|
|
session2 != X_SESSION_NONE;
|
|
}
|
|
};
|
|
//
|
|
// Implementation ...
|
|
class XCSessionDetector : public XCBase
|
|
{
|
|
//
|
|
public:
|
|
//
|
|
// Props ...
|
|
//
|
|
XCAlert *alert; // Alert Handler
|
|
XSessionConfig sessions[]; // All Sessions
|
|
XOverlapConfig overlaps[]; // All Overlaps
|
|
//
|
|
// Event Listeners ...
|
|
//
|
|
TOnSessionStart onSessionStartListener;
|
|
TOnSessionEnd onSessionEndListener;
|
|
TOnOverlapStart onOverlapStartListener;
|
|
TOnOverlapEnd onOverlapEndListener;
|
|
TOnSessionStateChange onSessionStateChangeListener;
|
|
//
|
|
// Event Handler Collections ...
|
|
//
|
|
TOnSessionStart mOnSessionStartHandlers[];
|
|
TOnSessionEnd mOnSessionEndHandlers[];
|
|
TOnOverlapStart mOnOverlapStartHandlers[];
|
|
TOnOverlapEnd mOnOverlapEndHandlers[];
|
|
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(mOnSessionStartHandlers);
|
|
Clean(mOnSessionEndHandlers);
|
|
Clean(mOnOverlapStartHandlers);
|
|
Clean(mOnOverlapEndHandlers);
|
|
Clean(mOnSessionStateChangeHandlers);
|
|
ZeroMemory(alert);
|
|
}
|
|
//
|
|
// 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(!IsSpecifiedValid(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(!IsSpecifiedValid(startTime) || !IsSpecifiedValid(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(!IsSpecifiedValid(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(!IsSpecifiedValid(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;
|
|
}
|
|
//
|
|
/**
|
|
* Process Session Detection (Call on Every Tick or Bar)
|
|
*
|
|
* @param currentTime: datetime (Optional, uses current time if NULL)
|
|
*/
|
|
void Process(datetime currentTime = NULL)
|
|
{
|
|
if(!IsSpecifiedValid(currentTime))
|
|
{
|
|
currentTime = GetCurrentTime();
|
|
}
|
|
//
|
|
// Check if Enough Time Passed Since Last Check (Prevent Multiple Fires)
|
|
if(IsSpecifiedValid(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);
|
|
}
|
|
}
|
|
}
|
|
//
|
|
// 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);
|
|
}
|
|
//
|
|
// Event Notifications ...
|
|
//
|
|
/**
|
|
* Notify Session Start Event
|
|
*
|
|
* @param session: ENUM_X_SESSION_TYPE
|
|
* @param startTime: datetime
|
|
*/
|
|
void NotifySessionStart(ENUM_X_SESSION_TYPE session, datetime startTime)
|
|
{
|
|
// Single Listener
|
|
if(onSessionStartListener != NULL)
|
|
{
|
|
onSessionStartListener(session, startTime);
|
|
}
|
|
//
|
|
// 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)
|
|
{
|
|
// Single Listener
|
|
if(onSessionEndListener != NULL)
|
|
{
|
|
onSessionEndListener(session, endTime);
|
|
}
|
|
//
|
|
// 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)
|
|
{
|
|
// Single Listener
|
|
if(onOverlapStartListener != NULL)
|
|
{
|
|
onOverlapStartListener(overlap, startTime);
|
|
}
|
|
//
|
|
// 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)
|
|
{
|
|
// Single Listener
|
|
if(onOverlapEndListener != NULL)
|
|
{
|
|
onOverlapEndListener(overlap, endTime);
|
|
}
|
|
//
|
|
// 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)
|
|
{
|
|
// Single Listener
|
|
if(onSessionStateChangeListener != NULL)
|
|
{
|
|
onSessionStateChangeListener(session, state);
|
|
}
|
|
//
|
|
// Multiple Handlers
|
|
int count = ArraySize(mOnSessionStateChangeHandlers);
|
|
for(int i = 0; i < count; i++)
|
|
{
|
|
mOnSessionStateChangeHandlers[i](session, state);
|
|
}
|
|
}
|
|
//
|
|
// 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();
|
|
//
|
|
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();
|
|
//
|
|
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(!IsSpecifiedValid(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(!IsSpecifiedValid(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;
|
|
}
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
};
|
|
//
|
|
```
|
|
|
|
## Usage Example
|
|
|
|
```mql5
|
|
//+------------------------------------------------------------------+
|
|
//| Expert Advisor Example |
|
|
//+------------------------------------------------------------------+
|
|
#include <Classes/x-saherelm.x-alert.class.mq5>
|
|
#include <Classes/x-saherelm.session.detector.class.mq5>
|
|
//
|
|
// Global Instances
|
|
XCAlert *gAlert;
|
|
XCSessionDetector *gSessionDetector;
|
|
//
|
|
// Event Handlers
|
|
void OnSessionStartHandler(ENUM_X_SESSION_TYPE session, datetime startTime)
|
|
{
|
|
Print("Session Started: ", EnumToString(session), " at ", TimeToString(startTime));
|
|
// Your trading logic here
|
|
}
|
|
//
|
|
void OnOverlapStartHandler(ENUM_X_SESSION_OVERLAP_TYPE overlap, datetime startTime)
|
|
{
|
|
Print("Overlap Started: ", EnumToString(overlap), " at ", TimeToString(startTime));
|
|
// Your trading logic here (e.g., London-NY overlap = high liquidity)
|
|
}
|
|
//
|
|
void OnSessionStateChangeHandler(ENUM_X_SESSION_TYPE session, ENUM_X_SESSION_STATE state)
|
|
{
|
|
Print("Session State Changed: ", EnumToString(session), " State: ", EnumToString(state));
|
|
}
|
|
//
|
|
//+------------------------------------------------------------------+
|
|
//| Expert initialization function |
|
|
//+------------------------------------------------------------------+
|
|
int OnInit()
|
|
{
|
|
// Initialize Alert
|
|
gAlert = new XCAlert("SessionDetector");
|
|
//
|
|
// Initialize Session Detector
|
|
gSessionDetector = new XCSessionDetector(
|
|
gAlert, // Alert Handler
|
|
true, // Use Server Time
|
|
0 // Timezone Offset (0 = Server Time)
|
|
);
|
|
//
|
|
// Register Event Handlers
|
|
gSessionDetector.AddOnSessionStartHandler(OnSessionStartHandler);
|
|
gSessionDetector.AddOnOverlapStartHandler(OnOverlapStartHandler);
|
|
gSessionDetector.AddOnSessionStateChangeHandler(OnSessionStateChangeHandler);
|
|
//
|
|
return(INIT_SUCCEEDED);
|
|
}
|
|
//
|
|
//+------------------------------------------------------------------+
|
|
//| Expert tick function |
|
|
//+------------------------------------------------------------------+
|
|
void OnTick()
|
|
{
|
|
// Process Session Detection
|
|
gSessionDetector.Process();
|
|
//
|
|
// You can also check active sessions manually
|
|
ENUM_X_SESSION_TYPE activeSessions[];
|
|
int activeCount = gSessionDetector.GetActiveSessions(activeSessions);
|
|
//
|
|
ENUM_X_SESSION_OVERLAP_TYPE activeOverlaps[];
|
|
int overlapCount = gSessionDetector.GetActiveOverlaps(activeOverlaps);
|
|
//
|
|
// Trading logic based on sessions...
|
|
if(overlapCount > 0 && activeOverlaps[0] == X_OVERLAP_LONDON_NEWYORK)
|
|
{
|
|
// London-NY Overlap = High Liquidity = Good for Trading
|
|
// Your trading logic here
|
|
}
|
|
}
|
|
//
|
|
//+------------------------------------------------------------------+
|
|
//| Expert deinitialization function |
|
|
//+------------------------------------------------------------------+
|
|
void OnDeinit(const int reason)
|
|
{
|
|
delete gSessionDetector;
|
|
delete gAlert;
|
|
}
|
|
//+------------------------------------------------------------------+
|
|
```
|
|
|
|
## Key Features
|
|
|
|
1. **7 Major Forex Sessions**: Sydney, Tokyo, Singapore, Hong Kong, Frankfurt, London, New York
|
|
2. **7 Session Overlaps**: Including the critical London-New York overlap
|
|
3. **Event System**: Register multiple event handlers for session start/end/overlap
|
|
4. **Timezone Support**: Use server time or UTC with custom offset
|
|
5. **State Tracking**: Track session states (Inactive, Active, Starting, Ending)
|
|
6. **Alert Integration**: Built-in alert notifications for session changes
|
|
7. **Library Compatible**: Follows SaherElm IT Center Library patterns
|
|
|
|
This class provides a **global, unique, and extensible** way to detect Forex sessions and trigger your trading logic based on session activity!
|