Last Works on XSDT Pivot Detector ...
This commit is contained in:
@@ -0,0 +1,899 @@
|
|||||||
|
///////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// SaherElm IT Center MQL5 Class
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Name: XCPivotDetector
|
||||||
|
// Description: Forex Pivot 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 Pivot 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"
|
||||||
|
#include "../Libraries/x-saherelm.x-poi.extensions.lib.mq5"
|
||||||
|
#include "../Libraries/x-saherelm.x-poi.lib.mq5"
|
||||||
|
|
||||||
|
//
|
||||||
|
// Definitions ...
|
||||||
|
|
||||||
|
typedef void (*TOnPivotZoneEvent)(XBoxZone &box);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Implementation ...
|
||||||
|
class XCPivotDetector : public XCBase
|
||||||
|
{
|
||||||
|
//
|
||||||
|
public:
|
||||||
|
//
|
||||||
|
// Props ...
|
||||||
|
//
|
||||||
|
XCAlert *alert; // Alert Handler
|
||||||
|
XBoxZone peaks[]; // Peaks ...
|
||||||
|
XBoxZone vales[]; // Vales ...
|
||||||
|
|
||||||
|
//
|
||||||
|
// Event Handlers ...
|
||||||
|
TOnPivotZoneEvent mOnPeakBreakedHandlers[];
|
||||||
|
TOnPivotZoneEvent mOnValeBreakedHandlers[];
|
||||||
|
TOnPivotZoneEvent mOnPeakDetectedHandlers[];
|
||||||
|
TOnPivotZoneEvent mOnValeDetectedHandlers[];
|
||||||
|
|
||||||
|
//
|
||||||
|
// Constructor ...
|
||||||
|
XCPivotDetector(
|
||||||
|
XCAlert *_alert = NULL, //
|
||||||
|
int _maxAllowedProcessableZones = 10 //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
alert = _alert;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Normalize ...
|
||||||
|
if (_maxAllowedProcessableZones <= 5)
|
||||||
|
{
|
||||||
|
_maxAllowedProcessableZones = 5;
|
||||||
|
}
|
||||||
|
mMaxAllowedProcessableZones = _maxAllowedProcessableZones;
|
||||||
|
|
||||||
|
//
|
||||||
|
Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Deconstructor ...
|
||||||
|
~XCPivotDetector()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Clean(peaks);
|
||||||
|
Clean(vales);
|
||||||
|
|
||||||
|
//
|
||||||
|
Clean(mOnPeakDetectedHandlers);
|
||||||
|
Clean(mOnValeDetectedHandlers);
|
||||||
|
|
||||||
|
//
|
||||||
|
ZeroMemory(alert);
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Actions ...
|
||||||
|
void Process(
|
||||||
|
XOHCL &bar,
|
||||||
|
int pivotApproved = 2,
|
||||||
|
int loopback = 20 //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!bar.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Normalize ...
|
||||||
|
|
||||||
|
//
|
||||||
|
// Privot Approve ...
|
||||||
|
if (pivotApproved < 1)
|
||||||
|
{
|
||||||
|
pivotApproved = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loopback ...
|
||||||
|
if (loopback < pivotApproved * 2)
|
||||||
|
{
|
||||||
|
loopback = pivotApproved * 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Peak/Vale Validation ...
|
||||||
|
UpdateZones(bar);
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Peak/Vale Detection ...
|
||||||
|
//
|
||||||
|
// Loop through Loopback ...
|
||||||
|
XOHCL iBar;
|
||||||
|
bool has = false;
|
||||||
|
XBoxZone peakBox;
|
||||||
|
XBoxZone valeBox;
|
||||||
|
bool hasPeak = false;
|
||||||
|
bool hasVale = false;
|
||||||
|
int start = bar.Index();
|
||||||
|
double jLow = EMPTY_VALUE;
|
||||||
|
double jHigh = EMPTY_VALUE;
|
||||||
|
int end = start + loopback;
|
||||||
|
double tmpPrice = EMPTY_VALUE;
|
||||||
|
double tmpZonePrice = EMPTY_VALUE;
|
||||||
|
for (int i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
iBar.Clean();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ignore Loop if not pass Pivot Approve Length ...
|
||||||
|
if (i < start + pivotApproved)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Bar ...
|
||||||
|
has = iBar.Init(
|
||||||
|
bar.symbol,
|
||||||
|
bar.period,
|
||||||
|
i //
|
||||||
|
);
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
hasPeak = false;
|
||||||
|
hasVale = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop through Bars for Approved Side ...
|
||||||
|
int jStart = i + 1;
|
||||||
|
int jEnd = i + pivotApproved;
|
||||||
|
for (int j = jStart; j < jEnd; j++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Reading High and Low Prices ...
|
||||||
|
jLow = iLow(iBar.symbol, iBar.period, j);
|
||||||
|
jHigh = iHigh(iBar.symbol, iBar.period, j);
|
||||||
|
|
||||||
|
//
|
||||||
|
hasVale = iBar.low < jLow;
|
||||||
|
hasPeak = iBar.high > jHigh;
|
||||||
|
has = hasVale || hasPeak;
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check Peak or Vale Exists ...
|
||||||
|
has = hasPeak || hasVale;
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
iBar.Clean();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Loop through Barse for Approved Side ...
|
||||||
|
jStart = i - pivotApproved;
|
||||||
|
jEnd = i - 1;
|
||||||
|
for (int j = jStart; j < jEnd; j++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Reading High and Low Prices ...
|
||||||
|
jLow = iLow(iBar.symbol, iBar.period, j);
|
||||||
|
jHigh = iHigh(iBar.symbol, iBar.period, j);
|
||||||
|
|
||||||
|
//
|
||||||
|
if (hasPeak)
|
||||||
|
{
|
||||||
|
hasPeak = iBar.high > jHigh;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (hasVale)
|
||||||
|
{
|
||||||
|
hasVale = iBar.low < jLow;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
has = hasPeak || hasVale;
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check Peak or Vale Exists ...
|
||||||
|
has = hasPeak || hasVale;
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
iBar.Clean();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Peaks ...
|
||||||
|
if (hasPeak)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Lower Price ...
|
||||||
|
tmpPrice = EMPTY_VALUE;
|
||||||
|
tmpZonePrice = EMPTY_VALUE;
|
||||||
|
jStart = i - pivotApproved;
|
||||||
|
jEnd = i + pivotApproved;
|
||||||
|
for (int j = jStart; j < jEnd; j++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Max Price of Open/Close ...
|
||||||
|
tmpPrice = MathMax(
|
||||||
|
iClose(iBar.symbol, iBar.period, j),
|
||||||
|
iOpen(iBar.symbol, iBar.period, j) //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
tmpZonePrice =
|
||||||
|
NotEmpty(tmpZonePrice)
|
||||||
|
? tmpPrice > tmpZonePrice
|
||||||
|
? tmpPrice
|
||||||
|
: tmpZonePrice
|
||||||
|
: tmpPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Peak Box ...
|
||||||
|
peakBox.Clean();
|
||||||
|
has = peakBox.Init(
|
||||||
|
iBar.symbol,
|
||||||
|
iBar.period,
|
||||||
|
iBar.high,
|
||||||
|
tmpZonePrice,
|
||||||
|
iBar.time,
|
||||||
|
bar.time,
|
||||||
|
X_DIRECTION_BEARISH,
|
||||||
|
mPivotPrefix //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
HandlePeakDetected(peakBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Vales ...
|
||||||
|
if (hasVale)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Higher Price ...
|
||||||
|
tmpPrice = EMPTY_VALUE;
|
||||||
|
tmpZonePrice = EMPTY_VALUE;
|
||||||
|
jStart = i - pivotApproved;
|
||||||
|
jEnd = i + pivotApproved;
|
||||||
|
for (int j = jStart; j < jEnd; j++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Max Price of Open/Close ...
|
||||||
|
tmpPrice = MathMin(
|
||||||
|
iClose(iBar.symbol, iBar.period, j),
|
||||||
|
iOpen(iBar.symbol, iBar.period, j) //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
tmpZonePrice =
|
||||||
|
NotEmpty(tmpZonePrice)
|
||||||
|
? tmpPrice < tmpZonePrice
|
||||||
|
? tmpPrice
|
||||||
|
: tmpZonePrice
|
||||||
|
: tmpPrice;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Vale Box ...
|
||||||
|
valeBox.Clean();
|
||||||
|
has = valeBox.Init(
|
||||||
|
iBar.symbol,
|
||||||
|
iBar.period,
|
||||||
|
tmpZonePrice,
|
||||||
|
iBar.low,
|
||||||
|
iBar.time,
|
||||||
|
bar.time,
|
||||||
|
X_DIRECTION_BULLISH,
|
||||||
|
mPivotPrefix //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
HandleValeDetected(valeBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
iBar.Clean();
|
||||||
|
peakBox.Clean();
|
||||||
|
valeBox.Clean();
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Event Listener Registration ...
|
||||||
|
/**
|
||||||
|
* Add Peak Breaked Event Handler
|
||||||
|
*
|
||||||
|
* @param handler: TOnPivotZoneEvent
|
||||||
|
*/
|
||||||
|
void AddOnPeakBreakedHandler(TOnPivotZoneEvent handler)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (handler == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Add(handler, mOnPeakBreakedHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Vale Breaked Event Handler
|
||||||
|
*
|
||||||
|
* @param handler: TOnPivotZoneEvent
|
||||||
|
*/
|
||||||
|
void AddOnValeBreakedHandler(TOnPivotZoneEvent handler)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (handler == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Add(handler, mOnValeBreakedHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Peak Detected Event Handler
|
||||||
|
*
|
||||||
|
* @param handler: TOnPivotZoneEvent
|
||||||
|
*/
|
||||||
|
void AddOnPeakDetectedHandler(TOnPivotZoneEvent handler)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (handler == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Add(handler, mOnPeakDetectedHandlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add Vale Detected Event Handler
|
||||||
|
*
|
||||||
|
* @param handler: TOnPivotZoneEvent
|
||||||
|
*/
|
||||||
|
void AddOnValeDetectedHandler(TOnPivotZoneEvent handler)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (handler == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Add(handler, mOnValeDetectedHandlers);
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
//
|
||||||
|
protected:
|
||||||
|
/**
|
||||||
|
* Initialize Class ...
|
||||||
|
*/
|
||||||
|
void Initialize()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Clean(peaks);
|
||||||
|
Clean(vales);
|
||||||
|
|
||||||
|
//
|
||||||
|
Clean(mOnPeakDetectedHandlers);
|
||||||
|
Clean(mOnValeDetectedHandlers);
|
||||||
|
|
||||||
|
//
|
||||||
|
mPivotPrefix = "XSDT";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Peak Box Detection ...
|
||||||
|
*
|
||||||
|
* @param peakBox: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void HandlePeakDetected(
|
||||||
|
XBoxZone &peakBox //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!peakBox.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update To ...
|
||||||
|
peakBox.to = TimeCurrent();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check Peak Exists ...
|
||||||
|
bool riseEvent = false;
|
||||||
|
bool hasPeaks = HasChild(peaks);
|
||||||
|
if (!hasPeaks)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Add to Peaks ...
|
||||||
|
AddRef(peakBox, peaks);
|
||||||
|
riseEvent = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
int idx = -1;
|
||||||
|
hasPeaks = FindIndex(
|
||||||
|
idx,
|
||||||
|
peakBox,
|
||||||
|
peaks //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Only Valid if there is not any Peak Registered ...
|
||||||
|
if (!hasPeaks)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Nearest Peak ...
|
||||||
|
idx = GetYoungest(peaks);
|
||||||
|
hasPeaks = IsValidIndex(idx);
|
||||||
|
if (hasPeaks)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate New Peak based on Youngest One ...
|
||||||
|
hasPeaks = peakBox.upper > peaks[idx].upper;
|
||||||
|
if (hasPeaks)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
AddRef(peakBox, peaks);
|
||||||
|
riseEvent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Event ...
|
||||||
|
if (riseEvent)
|
||||||
|
{
|
||||||
|
NotifyPeakDetected(peakBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
CleanupPeaks();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Vale Box Detection ...
|
||||||
|
*
|
||||||
|
* @param valeBox: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void HandleValeDetected(
|
||||||
|
XBoxZone &valeBox //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!valeBox.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Update To ...
|
||||||
|
valeBox.to = TimeCurrent();
|
||||||
|
|
||||||
|
//
|
||||||
|
bool riseEvent = false;
|
||||||
|
bool hasVales = HasChild(vales);
|
||||||
|
if (!hasVales)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Add to Vales ...
|
||||||
|
AddRef(valeBox, vales);
|
||||||
|
riseEvent = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
int idx = -1;
|
||||||
|
hasVales = FindIndex(
|
||||||
|
idx,
|
||||||
|
valeBox,
|
||||||
|
vales //
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Only Valid if there is not any Vale Registered ...
|
||||||
|
if (!hasVales)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Detect Nearest Vale ...
|
||||||
|
idx = GetYoungest(vales);
|
||||||
|
hasVales = IsValidIndex(idx);
|
||||||
|
if (hasVales)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate New Vale based on Youngest One ...
|
||||||
|
hasVales = valeBox.lower < vales[idx].lower;
|
||||||
|
if (hasVales)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
AddRef(valeBox, vales);
|
||||||
|
riseEvent = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Event ...
|
||||||
|
if (riseEvent)
|
||||||
|
{
|
||||||
|
NotifyValeDetected(valeBox);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
CleanupVales();
|
||||||
|
}
|
||||||
|
|
||||||
|
// #region Event Notifiers ...
|
||||||
|
/**
|
||||||
|
* Notfiy New Peak Breaked ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void NotifyPeakBreaked(XBoxZone &box)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!box.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Log ...
|
||||||
|
if (alert != NULL)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Preparing Log Message ...
|
||||||
|
string message = "Peak Breaked: " + DoubleToString(box.upper, 4) +
|
||||||
|
" at " + TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sending Alert ...
|
||||||
|
alert.SendAlert(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Multiple Handlers ...
|
||||||
|
int count = ArraySize(mOnPeakBreakedHandlers);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mOnPeakBreakedHandlers[i](box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notfiy New Vale Breaked ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void NotifyValeBreaked(XBoxZone &box)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!box.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Log ...
|
||||||
|
if (alert != NULL)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Preparing Log Message ...
|
||||||
|
string message = "Vale Breaked: " + DoubleToString(box.lower, 4) +
|
||||||
|
" at " + TimeToString(TimeCurrent(), TIME_DATE | TIME_SECONDS);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sending Alert ...
|
||||||
|
alert.SendAlert(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Multiple Handlers ...
|
||||||
|
int count = ArraySize(mOnValeBreakedHandlers);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mOnValeBreakedHandlers[i](box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notfiy New Peak Detected ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void NotifyPeakDetected(XBoxZone &box)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!box.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Log ...
|
||||||
|
if (alert != NULL)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Preparing Log Message ...
|
||||||
|
string message = "Peak Detected: " + DoubleToString(box.upper, 4) +
|
||||||
|
" at " + TimeToString(box.from, TIME_DATE | TIME_SECONDS);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sending Alert ...
|
||||||
|
alert.SendAlert(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Multiple Handlers ...
|
||||||
|
int count = ArraySize(mOnPeakDetectedHandlers);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mOnPeakDetectedHandlers[i](box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notfiy New Vale Detected ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void NotifyValeDetected(XBoxZone &box)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
if (!box.IsValid())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Log ...
|
||||||
|
if (alert != NULL)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Preparing Log Message ...
|
||||||
|
string message = "Vale Detected: " + DoubleToString(box.lower, 4) +
|
||||||
|
" at " + TimeToString(box.from, TIME_DATE | TIME_SECONDS);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Sending Alert ...
|
||||||
|
alert.SendAlert(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Multiple Handlers ...
|
||||||
|
int count = ArraySize(mOnValeDetectedHandlers);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
mOnValeDetectedHandlers[i](box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
//
|
||||||
|
private:
|
||||||
|
//
|
||||||
|
// Props ...
|
||||||
|
string mPivotPrefix; // Prefix ...
|
||||||
|
int mMaxAllowedProcessableZones; // Max Allowed Processable Zones ...
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Apply Max Allowed Processable Peaks ...
|
||||||
|
*/
|
||||||
|
void CleanupPeaks()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (ArraySize(peaks) <= mMaxAllowedProcessableZones)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Sort(
|
||||||
|
peaks, // Collection to Sort ...
|
||||||
|
X_SORT_BY_DATE, // Sorted By ...
|
||||||
|
X_DIRECTION_BEARISH, // Direction of Sorting ...
|
||||||
|
mMaxAllowedProcessableZones // Max Allowed ...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Apply Max Allowed Processable Vales ...
|
||||||
|
*/
|
||||||
|
void CleanupVales()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
if (ArraySize(vales) <= mMaxAllowedProcessableZones)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
Sort(
|
||||||
|
vales, // Collection to Sort ...
|
||||||
|
X_SORT_BY_DATE, // Sorted By ...
|
||||||
|
X_DIRECTION_BEARISH, // Direction of Sorting ...
|
||||||
|
mMaxAllowedProcessableZones // Max Allowed ...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* if there is Zones in Peaks or Vales
|
||||||
|
* Update their To Times ...
|
||||||
|
*
|
||||||
|
* @param bar: XOHCL instance ...
|
||||||
|
**/
|
||||||
|
void UpdateZones(XOHCL &bar)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Check Bar is Valid ...
|
||||||
|
if (bar.IsValid())
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Retrieve Last Closed Bar if not Provided ...
|
||||||
|
XOHCL cBar;
|
||||||
|
if (bar.Index() == 1)
|
||||||
|
{
|
||||||
|
cBar = bar;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cBar.Init(bar.symbol, bar.period, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Validate CBar ...
|
||||||
|
// Validate Peaks and Vales ...
|
||||||
|
if (cBar.IsValid())
|
||||||
|
{
|
||||||
|
//
|
||||||
|
int removeIDX[];
|
||||||
|
|
||||||
|
//
|
||||||
|
if (HasChild(peaks))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Clean(removeIDX);
|
||||||
|
for (int i = 0; i < ArraySize(peaks); i++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate Zone by Close Price ...
|
||||||
|
if (cBar.close > peaks[i].upper)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Add(i, removeIDX);
|
||||||
|
NotifyPeakBreaked(peaks[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (HasChild(removeIDX))
|
||||||
|
{
|
||||||
|
CleanupArray(removeIDX, peaks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (HasChild(vales))
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Clean(removeIDX);
|
||||||
|
for (int i = 0; i < ArraySize(vales); i++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Validate Zone by Close Price ...
|
||||||
|
if (cBar.close < vales[i].lower)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
Add(i, removeIDX);
|
||||||
|
NotifyValeBreaked(vales[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
if (HasChild(removeIDX))
|
||||||
|
{
|
||||||
|
CleanupArray(removeIDX, vales);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
cBar.Clean();
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
datetime tc = TimeCurrent();
|
||||||
|
|
||||||
|
//
|
||||||
|
bool has = HasChild(peaks);
|
||||||
|
if (has)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
for (int i = 0; i < ArraySize(peaks); i++)
|
||||||
|
{
|
||||||
|
peaks[i].to = tc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
has = HasChild(vales);
|
||||||
|
if (has)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
for (int i = 0; i < ArraySize(vales); i++)
|
||||||
|
{
|
||||||
|
vales[i].to = tc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
CleanupPeaks();
|
||||||
|
CleanupVales();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//
|
||||||
@@ -0,0 +1,414 @@
|
|||||||
|
///////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// SaherElm IT Center MQL5 Expert Advisor
|
||||||
|
// -------------------------------------------------
|
||||||
|
// Name: XBaseEA
|
||||||
|
// Description: Session Detector Test Expert Advisor ...
|
||||||
|
//
|
||||||
|
// Maintainer:
|
||||||
|
// ------------
|
||||||
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
||||||
|
//
|
||||||
|
//////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Global Properties ...
|
||||||
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
||||||
|
#property link "https://saherelm.ir"
|
||||||
|
#property version "1.00"
|
||||||
|
#property icon "../Images/SaherElm_Logo_Color.ico"
|
||||||
|
#property description "SaherElm Session Detector Test Expert Advisor"
|
||||||
|
#property strict
|
||||||
|
|
||||||
|
// #region Imports ...
|
||||||
|
#include "../Classes/x-saherelm.pivot-detector.class.mq5"
|
||||||
|
#include "../Classes/x-saherelm.session-detector.class.mq5"
|
||||||
|
#include "../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Global Variables ...
|
||||||
|
#define ShortName "XPivotDetectorTestEA"
|
||||||
|
|
||||||
|
XCAlert *eaAlert;
|
||||||
|
XCPOIDrawer *eaDrawer;
|
||||||
|
CArrayObj eaDrawnObjects;
|
||||||
|
XCPivotDetector *eaPivotDetector;
|
||||||
|
XCSessionDetector *eaSessionDetector;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Last Processing Pivots Time ...
|
||||||
|
datetime eaLastPivotProcessing = NULL;
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Global Event Handlers ...
|
||||||
|
/**
|
||||||
|
* Initialize EA Process ...
|
||||||
|
*
|
||||||
|
* @return ( int )
|
||||||
|
*/
|
||||||
|
int OnInit()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
bool result = true;
|
||||||
|
|
||||||
|
//
|
||||||
|
result = InitializeRequirements();
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return INIT_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return INIT_SUCCEEDED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* De Initialize Expert ...
|
||||||
|
*
|
||||||
|
* @param reason: int, DeInitialization Reason ...
|
||||||
|
*
|
||||||
|
* REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function
|
||||||
|
* REASON_REMOVE 1 Program removed from a chart
|
||||||
|
* REASON_RECOMPILE 2 Program recompiled
|
||||||
|
* REASON_CHARTCHANGE 3 A symbol or a chart period is changed
|
||||||
|
* REASON_CHARTCLOSE 4 Chart closed
|
||||||
|
* REASON_PARAMETERS 5 Inputs changed by a user
|
||||||
|
* REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings
|
||||||
|
* REASON_TEMPLATE 7 Another chart template applied
|
||||||
|
* REASON_INITFAILED 8 The OnInit() handler returned a non-zero value
|
||||||
|
* REASON_CLOSE 9 Terminal closed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
void OnDeinit(const int reason)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Destroy Timer ...
|
||||||
|
// If Initialized ...
|
||||||
|
|
||||||
|
//
|
||||||
|
// Cleanup Resources ...
|
||||||
|
ZeroMemory(eaAlert);
|
||||||
|
ZeroMemory(eaDrawer);
|
||||||
|
ZeroMemory(eaPivotDetector);
|
||||||
|
ZeroMemory(eaSessionDetector);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Tick for Processing ...
|
||||||
|
*/
|
||||||
|
void OnTick()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Perform Session Detector Process ...
|
||||||
|
eaSessionDetector.Process();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Process Pivot Detector ...
|
||||||
|
HandleProcessPviots();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle On Chart Event Processing ...
|
||||||
|
|
||||||
|
* @param id: int, Specified Event ID ...
|
||||||
|
* @param lparam: long, type event parameter
|
||||||
|
* @param dparam: double, type event parameter
|
||||||
|
* @param sparam: string, type event parameter
|
||||||
|
*/
|
||||||
|
void OnChartEvent(
|
||||||
|
const int id,
|
||||||
|
const long &lparam,
|
||||||
|
const double &dparam,
|
||||||
|
const string &sparam //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Custom Actions ...
|
||||||
|
/**
|
||||||
|
* Initial Requirements of EA ...
|
||||||
|
*
|
||||||
|
* @return ( bool )
|
||||||
|
*/
|
||||||
|
bool InitializeRequirements()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
bool result = false;
|
||||||
|
|
||||||
|
//
|
||||||
|
eaDrawer = new XCPOIDrawer();
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Alert ...
|
||||||
|
eaAlert = new XCAlert(
|
||||||
|
ShortName,
|
||||||
|
true, // Enable Alert ...
|
||||||
|
true, // Log Alert ...
|
||||||
|
false, // Terminal Alert ...
|
||||||
|
false, // Mail Alert ...
|
||||||
|
false, // Push Alerts ...
|
||||||
|
false // Save Alerts ...
|
||||||
|
);
|
||||||
|
result = eaAlert != NULL;
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Session Detector ...
|
||||||
|
eaSessionDetector = new XCSessionDetector(
|
||||||
|
eaAlert, // Alert ...
|
||||||
|
true, // Use Server Time ...
|
||||||
|
0 // Timezone Offset ...
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Register Event Handlers ...
|
||||||
|
eaSessionDetector.AddOnSessionEndHandler(OnSessionEndHandler);
|
||||||
|
eaSessionDetector.AddOnOverlapEndHandler(OnOverlapEndHandler);
|
||||||
|
eaSessionDetector.AddOnSessionStartHandler(OnSessionStartHandler);
|
||||||
|
eaSessionDetector.AddOnOverlapStartHandler(OnOverlapStartHandler);
|
||||||
|
eaSessionDetector.AddOnSessionStateChangeHandler(OnSessionStateChangeHandler);
|
||||||
|
|
||||||
|
//
|
||||||
|
result = eaSessionDetector != NULL;
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Initialize Pivot Detector ...
|
||||||
|
eaPivotDetector = new XCPivotDetector(
|
||||||
|
eaAlert // Alert ...
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Register Event Handlers ...
|
||||||
|
eaPivotDetector.AddOnPeakBreakedHandler(OnPeakBreakedHandler);
|
||||||
|
eaPivotDetector.AddOnValeBreakedHandler(OnValeBreakedHandler);
|
||||||
|
eaPivotDetector.AddOnPeakDetectedHandler(OnPeakDetectedHandler);
|
||||||
|
eaPivotDetector.AddOnValeDetectedHandler(OnValeDetectedHandler);
|
||||||
|
|
||||||
|
//
|
||||||
|
result = eaPivotDetector != NULL;
|
||||||
|
if (!result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Process Pivots ...
|
||||||
|
*/
|
||||||
|
void HandleProcessPviots()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
// Initialize Pivot Processing Bar ...
|
||||||
|
XOHCL bar;
|
||||||
|
bool has = bar.Init(
|
||||||
|
_Symbol,
|
||||||
|
PERIOD_H4,
|
||||||
|
1 // Last Completed 4H Bar ...
|
||||||
|
);
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Validate Passing Bar ...
|
||||||
|
has =
|
||||||
|
!IsXValid(eaLastPivotProcessing) ||
|
||||||
|
bar.time > eaLastPivotProcessing;
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
eaPivotDetector.Process(
|
||||||
|
bar, // XOHCL Bar ...
|
||||||
|
2, // Pivot Approvation Length ...
|
||||||
|
20 // Loopback ...
|
||||||
|
);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Draw Pivots ...
|
||||||
|
XBoxZone tmps[];
|
||||||
|
CArrayObj *objects;
|
||||||
|
FillNotExists(
|
||||||
|
tmps, // Dest ...
|
||||||
|
eaPivotDetector.peaks, // Source ...
|
||||||
|
false // Force Clean ...
|
||||||
|
);
|
||||||
|
FillNotExists(
|
||||||
|
tmps, // Dest ...
|
||||||
|
eaPivotDetector.vales, // Source ...
|
||||||
|
false // Force Clean ...
|
||||||
|
);
|
||||||
|
if (HasChild(tmps))
|
||||||
|
{
|
||||||
|
eaDrawer.DrawBoxes(
|
||||||
|
tmps, // Boxes ...
|
||||||
|
objects, // Objects to Hold ...
|
||||||
|
false // Ignore Type Name ...
|
||||||
|
);
|
||||||
|
}
|
||||||
|
XClean(tmps);
|
||||||
|
AddObjects(objects, eaDrawnObjects);
|
||||||
|
if (eaDrawnObjects.Total() > 0) {
|
||||||
|
DebugBreak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Custom Event Handlers ...
|
||||||
|
|
||||||
|
// #region Pivot Detector Event Handlers ...
|
||||||
|
/**
|
||||||
|
* Calls when a Peak Breaked ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void OnPeakBreakedHandler(XBoxZone &box)
|
||||||
|
{
|
||||||
|
// DebugBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls when a Vale Breaked ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void OnValeBreakedHandler(XBoxZone &box)
|
||||||
|
{
|
||||||
|
// DebugBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls when a New Peak Detected ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void OnPeakDetectedHandler(XBoxZone &box)
|
||||||
|
{
|
||||||
|
// DebugBreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls when a New Vale Detected ...
|
||||||
|
*
|
||||||
|
* @param box: XBoxZone instance ...
|
||||||
|
*/
|
||||||
|
void OnValeDetectedHandler(XBoxZone &box)
|
||||||
|
{
|
||||||
|
// DebugBreak();
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #region Session Detector Event Handlers ...
|
||||||
|
/**
|
||||||
|
* Handle Session Start Event ...
|
||||||
|
*
|
||||||
|
* @param session: ENUM_X_SESSION_TYPE
|
||||||
|
* @param startTime: datetime
|
||||||
|
*/
|
||||||
|
void OnSessionStartHandler(
|
||||||
|
ENUM_X_SESSION_TYPE session,
|
||||||
|
datetime startTime //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Print("Session Started: ", EnumToString(session), " at ", TimeToString(startTime));
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // Custom Testing ...
|
||||||
|
// if (session == X_SESSION_NEWYORK)
|
||||||
|
// {
|
||||||
|
// DebugBreak();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Session End Event ...
|
||||||
|
*
|
||||||
|
* @param session: ENUM_X_SESSION_TYPE
|
||||||
|
* @param startTime: datetime
|
||||||
|
*/
|
||||||
|
void OnSessionEndHandler(
|
||||||
|
ENUM_X_SESSION_TYPE session,
|
||||||
|
datetime startTime //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Print("Session Ended: ", EnumToString(session), " at ", TimeToString(startTime));
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // Custom Testing ...
|
||||||
|
// if (session == X_SESSION_NEWYORK)
|
||||||
|
// {
|
||||||
|
// DebugBreak();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Overlap Start Event ...
|
||||||
|
*
|
||||||
|
* @param session: ENUM_X_SESSION_OVERLAP_TYPE
|
||||||
|
* @param startTime: datetime
|
||||||
|
*/
|
||||||
|
void OnOverlapStartHandler(
|
||||||
|
ENUM_X_SESSION_OVERLAP_TYPE overlap,
|
||||||
|
datetime startTime //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Print("Overlap Started: ", EnumToString(overlap), " at ", TimeToString(startTime));
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // Custom Testing ...
|
||||||
|
// if (overlap == X_OVERLAP_LONDON_NEWYORK)
|
||||||
|
// {
|
||||||
|
// DebugBreak();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Overlap End Event ...
|
||||||
|
*
|
||||||
|
* @param session: ENUM_X_SESSION_OVERLAP_TYPE
|
||||||
|
* @param startTime: datetime
|
||||||
|
*/
|
||||||
|
void OnOverlapEndHandler(
|
||||||
|
ENUM_X_SESSION_OVERLAP_TYPE overlap,
|
||||||
|
datetime startTime //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Print("Overlap Ended: ", EnumToString(overlap), " at ", TimeToString(startTime));
|
||||||
|
|
||||||
|
// //
|
||||||
|
// // Custom Testing ...
|
||||||
|
// if (overlap == X_OVERLAP_LONDON_NEWYORK)
|
||||||
|
// {
|
||||||
|
// DebugBreak();
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Session State Change Event ...
|
||||||
|
*
|
||||||
|
* @param session: ENUM_X_SESSION_TYPE
|
||||||
|
* @param state: ENUM_X_SESSION_STATE
|
||||||
|
*/
|
||||||
|
void OnSessionStateChangeHandler(
|
||||||
|
ENUM_X_SESSION_TYPE session,
|
||||||
|
ENUM_X_SESSION_STATE state //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Print("Session State Changed: ", EnumToString(session), " State: ", EnumToString(state));
|
||||||
|
}
|
||||||
|
// #endregion
|
||||||
|
|
||||||
|
// #endregion
|
||||||
@@ -3482,6 +3482,74 @@ int FillNotExists(
|
|||||||
//
|
//
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
int FillNotExists(
|
||||||
|
T &dest[],
|
||||||
|
T &source[],
|
||||||
|
bool forceClean = false,
|
||||||
|
ENUM_X_DIRECTION forDir = X_DIRECTION_NONE,
|
||||||
|
int maxAllowed = 0 //
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Prepare ...
|
||||||
|
if (forceClean)
|
||||||
|
{
|
||||||
|
XClean(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Normalize ...
|
||||||
|
maxAllowed = NormalizeInt(maxAllowed, 0);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Validate ...
|
||||||
|
bool has = HasChild(source);
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
int count = ArraySize(source);
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
has =
|
||||||
|
source[i].IsValid() &&
|
||||||
|
(!HasDirection(forDir) || forDir == source[i].dir);
|
||||||
|
if (!has)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
has = AddIfNotExists(source[i], dest);
|
||||||
|
if (has)
|
||||||
|
{
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Handle Cleanup Collection
|
||||||
|
// if Max Reached ...
|
||||||
|
if (maxAllowed > 0)
|
||||||
|
{
|
||||||
|
//
|
||||||
|
CleanupArray(
|
||||||
|
dest,
|
||||||
|
maxAllowed //
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
return result;
|
||||||
|
}
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
|
|
||||||
//+------------------------------------------------------------------+
|
//+------------------------------------------------------------------+
|
||||||
|
|||||||
Reference in New Issue
Block a user