1657 lines
36 KiB
Plaintext
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:
|
|
//
|
|
};
|
|
|
|
// |