711 lines
17 KiB
Plaintext
711 lines
17 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class
|
|
// -------------------------------------------------
|
|
// Name: XCXOM1Signaller ...
|
|
// Description: XOM1EA Signaller Class ...
|
|
//
|
|
//
|
|
// 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 XOM1EA Signaller Class"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../../Helpers/x-saherelm.xom1.helper.mq5"
|
|
#include "../Classes/xom1ea.x-poi.drawer.class.mq5"
|
|
#include "../Libraries/xom1ea.lib.mq5"
|
|
|
|
//
|
|
// Definitions ...
|
|
|
|
//
|
|
// All Available Signaller Configurations
|
|
// which allow to Decide Based on Zones ...
|
|
struct XOM1SignallerConfig
|
|
{
|
|
//
|
|
// Props ...
|
|
bool processAllTicks; // Do Process All Ticks ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XOM1SignallerConfig()
|
|
{
|
|
Clean();
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
/**
|
|
* Cleanup ...
|
|
*/
|
|
void Clean()
|
|
{
|
|
ZeroMemory(this);
|
|
}
|
|
};
|
|
|
|
//
|
|
// Implementation ...
|
|
|
|
//
|
|
// Base Signaller Class ...
|
|
// Provides Zone Detection Mechanism ...
|
|
class XCXOM1SignallerBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
string name; // Name ...
|
|
string symbol; // Symbol ...
|
|
XOM1Zone zones[]; // Detected Zones ...
|
|
XOM1Inputs inputs; // Indicator Inputs ...
|
|
ENUM_TIMEFRAMES period; // Period ...
|
|
XOM1SignallerConfig config; // Configuration ...
|
|
XCXOM1POIDrawer drawer; // POI Drawer ...
|
|
|
|
//
|
|
// Constructor ...
|
|
XCXOM1SignallerBase(
|
|
string _name, // Name ...
|
|
string _symbol, // Symbol ...
|
|
ENUM_TIMEFRAMES _period, // Period ...
|
|
XOM1Inputs &_inputs, // Indicator Inputs ...
|
|
XOM1SignallerConfig &_config // Config ...
|
|
)
|
|
{
|
|
//
|
|
// Validate ...
|
|
bool isValid =
|
|
IsSpecifiedValid(_name) &&
|
|
IsSpecifiedValid(_symbol) &&
|
|
IsSpecifiedValid(_period) &&
|
|
_inputs.IsValid();
|
|
if (!isValid)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Make Helper Class Instance ...
|
|
mHelper = new XCXOM1Helper();
|
|
bool isInited = mHelper.Init(
|
|
_symbol,
|
|
_period,
|
|
_inputs //
|
|
);
|
|
if (!isInited)
|
|
{
|
|
//
|
|
delete mHelper;
|
|
ZeroMemory(mHelper);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set Props ...
|
|
name = _name;
|
|
symbol = _symbol;
|
|
period = _period;
|
|
inputs = _inputs;
|
|
config = _config;
|
|
|
|
//
|
|
// Set Dfault Values ...
|
|
mProcessedTicksCount = 0;
|
|
mLastCheckedBarTime = NULL;
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCXOM1SignallerBase()
|
|
{
|
|
//
|
|
inputs.Clean();
|
|
|
|
//
|
|
SpecifiedClean(zones);
|
|
|
|
//
|
|
delete mHelper;
|
|
ZeroMemory(mHelper);
|
|
}
|
|
|
|
//
|
|
// Signal Event Handlers ...
|
|
|
|
//
|
|
void AddOnSignalEventHandler(TOnSignal handler)
|
|
{
|
|
//
|
|
if (handler == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
Add(
|
|
handler,
|
|
mOnSignalEventHandlers //
|
|
);
|
|
}
|
|
|
|
//
|
|
void NotifyOnSignalEvent(XSignal &signal)
|
|
{
|
|
//
|
|
int count = ArraySize(mOnSignalEventHandlers);
|
|
bool has = IsValidSize(count) && signal.IsValid();
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Calling Event Handlers ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
mOnSignalEventHandlers[i](signal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Actions ...
|
|
|
|
/**
|
|
* Handle Ticks Process ...
|
|
*
|
|
* @param barIndex: int
|
|
*/
|
|
void ProcessTick(int barIndex = 0)
|
|
{
|
|
//
|
|
// Normalize Bar Index ...
|
|
if (barIndex < 0)
|
|
{
|
|
barIndex = 0;
|
|
}
|
|
|
|
//
|
|
// Define Requirements ...
|
|
XOHCL iBar;
|
|
bool has = false;
|
|
bool isNewBar = false;
|
|
|
|
//
|
|
// Get Current Bar Time ...
|
|
datetime cBarTime = GetBarTime(
|
|
symbol,
|
|
period,
|
|
barIndex //
|
|
);
|
|
|
|
//
|
|
// Validate Bar Time ...
|
|
has = IsSpecifiedValid(cBarTime);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check if new Bar, Reset Processed Ticks Count ...
|
|
isNewBar = cBarTime > mLastCheckedBarTime;
|
|
if (isNewBar)
|
|
{
|
|
//
|
|
mProcessedTicksCount = 0;
|
|
ProcessNewBar(barIndex);
|
|
}
|
|
|
|
//
|
|
// Check Allow Process ...
|
|
// Process All Ticks ...
|
|
// Process New Bars ...
|
|
has = config.processAllTicks ||
|
|
(!config.processAllTicks && isNewBar);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set Last Works ...
|
|
mProcessedTicksCount++;
|
|
mLastCheckedBarTime = cBarTime;
|
|
|
|
//
|
|
// Initialize iBar ...
|
|
has = iBar.Init(
|
|
symbol,
|
|
period,
|
|
barIndex //
|
|
);
|
|
if (!has)
|
|
{
|
|
//
|
|
iBar.Clean();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Indicator Helper ...
|
|
mHelper.Update(barIndex);
|
|
|
|
//
|
|
// Process Exists Zone ...
|
|
ProcessZones(barIndex);
|
|
|
|
//
|
|
// Do All Processing Mechanism ...
|
|
|
|
//
|
|
// Reading Conditions from Helper ...
|
|
XOM1Conditions iConditions;
|
|
has = mHelper.GetConditions(
|
|
iConditions,
|
|
barIndex //
|
|
);
|
|
if (!has)
|
|
{
|
|
//
|
|
iBar.Clean();
|
|
iConditions.Clean();
|
|
return;
|
|
}
|
|
|
|
//
|
|
int zIdx = 0;
|
|
int cIdx = zIdx + 1;
|
|
int pIdx = cIdx + 1;
|
|
int p2Idx = pIdx + 1;
|
|
|
|
//
|
|
// Check Zones Flags ...
|
|
bool checkPVZones = false;
|
|
bool checkMAZones = true;
|
|
|
|
//
|
|
// Check PV Zones ...
|
|
if (checkPVZones)
|
|
{
|
|
//
|
|
// Check New Peak or Vale Happens ...
|
|
|
|
//
|
|
// New Vale Over Last ...
|
|
bool isValeReason = iConditions.valeBuffer[cIdx] > iConditions.valeBuffer[pIdx];
|
|
|
|
//
|
|
// New Peak Under Last ...
|
|
bool isPeakReason = iConditions.peakBuffer[cIdx] < iConditions.peakBuffer[pIdx];
|
|
|
|
//
|
|
has = isValeReason || isPeakReason;
|
|
if (has)
|
|
{
|
|
//
|
|
// Prepare Reason ...
|
|
ENUM_XOM1_ZONE_REASON iReason = isValeReason
|
|
? X_OM1_ZONE_REASON_VALE
|
|
: X_OM1_ZONE_REASON_PEAK;
|
|
|
|
//
|
|
// Extract Upper / Lower Values ...
|
|
double iUpper = iConditions.peakBuffer[pIdx];
|
|
double iLower = iConditions.valeBuffer[pIdx];
|
|
|
|
//
|
|
// Calculate Mid Range ...
|
|
double iMiddle = iLower + ((iUpper - iLower) / 2);
|
|
if (iMiddle > 0)
|
|
{
|
|
iMiddle = NormalizePrice(iMiddle, symbol);
|
|
}
|
|
|
|
//
|
|
// Detect Reason Price ...
|
|
double iPrice =
|
|
isValeReason
|
|
? iConditions.valeBuffer[cIdx]
|
|
: iConditions.peakBuffer[cIdx];
|
|
if (iPrice > 0)
|
|
{
|
|
iPrice = NormalizePrice(iPrice, symbol);
|
|
}
|
|
|
|
//
|
|
// Detect Reason Direction ...
|
|
ENUM_X_DIRECTION iDir = iPrice > iMiddle
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Try to Initialize Zone ...
|
|
XOM1Zone iZone;
|
|
has = iZone.Init(
|
|
symbol,
|
|
period,
|
|
name, // Signaller Name ...
|
|
iUpper,
|
|
iLower,
|
|
iPrice,
|
|
iDir,
|
|
iReason,
|
|
iConditions.bars[pIdx].time, // From ...
|
|
iConditions.bars[zIdx].time // To
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Add New Detected Zone to Zones ...
|
|
AddRef(
|
|
iZone,
|
|
zones //
|
|
);
|
|
|
|
//
|
|
// Prepare Signal ...
|
|
PrepareSignal(iZone);
|
|
}
|
|
|
|
//
|
|
iZone.Clean();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check MA Zones ...
|
|
if (checkMAZones)
|
|
{
|
|
//
|
|
// Crossed Over ...
|
|
bool isFastCrossedOverSlow = iConditions.maFastBuffer[cIdx] > iConditions.maSlowBuffer[cIdx] &&
|
|
!(iConditions.maFastBuffer[pIdx] >= iConditions.maSlowBuffer[pIdx]);
|
|
|
|
//
|
|
// Crossed Under ...
|
|
bool isFastCrossedUnderSlow = iConditions.maFastBuffer[cIdx] < iConditions.maSlowBuffer[cIdx] &&
|
|
!(iConditions.maFastBuffer[pIdx] <= iConditions.maSlowBuffer[pIdx]);
|
|
|
|
//
|
|
double iFast = iConditions.maFastBuffer[cIdx];
|
|
double iSlow = iConditions.maSlowBuffer[cIdx];
|
|
double iFibUpper = iConditions.fiboUpperBuffer[cIdx];
|
|
double iFibLower = iConditions.fiboLowerBuffer[cIdx];
|
|
|
|
//
|
|
double iValues[] = {
|
|
iFast,
|
|
iSlow,
|
|
iFibUpper,
|
|
iFibLower //
|
|
};
|
|
|
|
//
|
|
// Extract Upper / Lower Values ...
|
|
double iUpper = GetMax(iValues);
|
|
double iLower = GetMin(iValues);
|
|
|
|
//
|
|
double iZoneUpper = MathMax(iFibUpper, iFibLower);
|
|
double iZoneLower = MathMin(iFibUpper, iFibLower);
|
|
|
|
//
|
|
has = isFastCrossedOverSlow || isFastCrossedUnderSlow;
|
|
|
|
//
|
|
// Validate Cross Between Zone ...
|
|
if (has)
|
|
{
|
|
//
|
|
// Validate MA Place ...
|
|
has = iFast >= iZoneLower &&
|
|
iFast <= iZoneUpper &&
|
|
iSlow >= iZoneLower &&
|
|
iSlow <= iZoneUpper;
|
|
|
|
//
|
|
// Validate PSar ...
|
|
bool isSarBullish = iConditions.sarStateBuffer[cIdx] > 0;
|
|
bool isSarBearish = iConditions.sarStateBuffer[cIdx] < 0;
|
|
has =
|
|
has &&
|
|
(isFastCrossedOverSlow
|
|
? isSarBullish
|
|
: isSarBearish //
|
|
);
|
|
|
|
//
|
|
// Validate Fibo Zone ...
|
|
for (int idx = cIdx; idx < cIdx + 2; idx++)
|
|
{
|
|
//
|
|
// Check Fibo Upper ...
|
|
has = has &&
|
|
iConditions.fiboUpperBuffer[idx] == iConditions.fiboUpperBuffer[idx + 1];
|
|
if (!has)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check Fibo Lower ...
|
|
has = has &&
|
|
iConditions.fiboLowerBuffer[idx] == iConditions.fiboLowerBuffer[idx + 1];
|
|
if (!has)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Moving Forward if Validated ...
|
|
if (has)
|
|
{
|
|
//
|
|
// Prepare Reason ...
|
|
ENUM_XOM1_ZONE_REASON iReason = isFastCrossedOverSlow
|
|
? X_OM1_ZONE_REASON_FCOS
|
|
: X_OM1_ZONE_REASON_FCUS;
|
|
|
|
//
|
|
// Calculate Mid Range ...
|
|
double iMiddle = iLower + ((iUpper - iLower) / 2);
|
|
if (iMiddle > 0)
|
|
{
|
|
iMiddle = NormalizePrice(iMiddle, symbol);
|
|
}
|
|
|
|
//
|
|
// Detect Reason Price ...
|
|
double iPrice = iConditions.maFastBuffer[cIdx];
|
|
if (iPrice > 0)
|
|
{
|
|
iPrice = NormalizePrice(iPrice, symbol);
|
|
}
|
|
|
|
//
|
|
// Detect Reason Direction ...
|
|
ENUM_X_DIRECTION iDir = isFastCrossedOverSlow
|
|
? X_DIRECTION_BULLISH
|
|
: X_DIRECTION_BEARISH;
|
|
|
|
//
|
|
// Try to Initialize Zone ...
|
|
XOM1Zone iZone;
|
|
has = iZone.Init(
|
|
symbol,
|
|
period,
|
|
name, // Signaller Name ...
|
|
iZoneUpper,
|
|
iZoneLower,
|
|
iPrice,
|
|
iDir,
|
|
iReason,
|
|
iConditions.bars[pIdx].time, // From ...
|
|
iConditions.bars[zIdx].time // To
|
|
);
|
|
if (has)
|
|
{
|
|
//
|
|
// Add New Detected Zone to Zones ...
|
|
AddRef(
|
|
iZone,
|
|
zones //
|
|
);
|
|
|
|
//
|
|
// Prepare Signal ...
|
|
PrepareSignal(iZone);
|
|
}
|
|
|
|
//
|
|
iZone.Clean();
|
|
}
|
|
|
|
//
|
|
Clean(iValues);
|
|
}
|
|
|
|
//
|
|
// Clean Resources ...
|
|
iBar.Clean();
|
|
iConditions.Clean();
|
|
}
|
|
|
|
//
|
|
// Virtuals ...
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
XCXOM1Helper *mHelper; // Indicator Helper ...
|
|
|
|
//
|
|
TOnSignal mOnSignalEventHandlers[]; // Signal Recieved Event Handlers ...
|
|
|
|
//
|
|
int mProcessedTicksCount; // Prcoessed Ticks Count ...
|
|
datetime mLastCheckedBarTime; // Last Checked Bar Time ...
|
|
|
|
/**
|
|
* Handle New Bar's Process ...
|
|
*
|
|
* @param barIndex: int
|
|
*/
|
|
void ProcessNewBar(int barIndex = 0)
|
|
{
|
|
//
|
|
// Normalize Bar Index ...
|
|
if (barIndex < 0)
|
|
{
|
|
barIndex = 0;
|
|
}
|
|
|
|
//
|
|
// Try to Update Zone's Lifetime ...
|
|
int count = ArraySize(zones);
|
|
bool has = count > 0;
|
|
if (has)
|
|
{
|
|
//
|
|
// Loop through Zones ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Update Lifetime by Bar Index ...
|
|
// zones[i].UpdateByBarIndex(barIndex);
|
|
|
|
//
|
|
// drawer.DrawZone(
|
|
// zones[i],
|
|
// true, // Draw Price ...
|
|
// true, // Draw Middle ...
|
|
// true // Draw From ...
|
|
// );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check XOMZones Validations ...
|
|
}
|
|
|
|
/**
|
|
* Process Exists Zones ...
|
|
* Detect a Zone for trigger ...
|
|
*
|
|
* @param barIndex: int
|
|
*/
|
|
void ProcessZones(int barIndex = 0)
|
|
{
|
|
//
|
|
// Check Zones Exists ...
|
|
int count = ArraySize(zones);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop Through Zones ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Converts iZone to Signal and Notify Signal ...
|
|
*
|
|
* @param iZone: XOM1Zone
|
|
*/
|
|
void PrepareSignal(XOM1Zone &zone)
|
|
{
|
|
//
|
|
// Validate Zone ...
|
|
if (!zone.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create Signal ...
|
|
XSignal signal;
|
|
|
|
//
|
|
signal.time = zone.to;
|
|
signal.provider = name;
|
|
signal.symbol = zone.symbol;
|
|
signal.period = zone.period;
|
|
signal.mode = X_ORDER_MODE_MARKET;
|
|
signal.conditions = "XConditions";
|
|
signal.comment = ToXString(zone.reason);
|
|
signal.type = zone.IsBullish()
|
|
? POSITION_TYPE_BUY
|
|
: POSITION_TYPE_SELL;
|
|
|
|
//
|
|
// Get Entry ...
|
|
signal.entry = GetXEntry(zone.symbol, zone.dir);
|
|
|
|
//
|
|
// Calculate SL ...
|
|
double providedSL = zone.IsBullish()
|
|
? zone.lower
|
|
: zone.upper;
|
|
signal.sl = providedSL;
|
|
|
|
//
|
|
// Calculate Risk ...
|
|
double risk = MathAbs(signal.entry - signal.sl);
|
|
|
|
//
|
|
// Calculate Reward ...
|
|
double r2r = 2;
|
|
double reward = risk * r2r;
|
|
|
|
//
|
|
// Calculate TP ...
|
|
double providedTP = zone.IsBullish()
|
|
? signal.entry + reward
|
|
: signal.entry - reward;
|
|
signal.tp = providedTP;
|
|
|
|
//
|
|
// Calculate Targets ...
|
|
// TODO: Handle this ...
|
|
|
|
//
|
|
// Set Min Volume ...
|
|
signal.volume = 0.01;
|
|
|
|
//
|
|
NotifyOnSignalEvent(signal);
|
|
}
|
|
};
|
|
|
|
// |