Files
xMQL5/MQLTestWorkspace/BKPS/14030322/Classes/x-saherelm.x121.xea.class.mq5
T

2230 lines
51 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XSCX121EA
// Description: provides all X121 EA requirements ...
// - X5 Provider;
//
//
// Maintainer:
// ------------
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
//
//////////////////////////////////////////////////////
//
// Global Properties ...
#property library
#property copyright "Copyright 2023, SaherElm IT Center"
#property link "https://www.saherelm.ir"
#property version "1.00"
#property strict
//
// Imports ...
#include "../Classes/x-saherelm.xea.class.mq5"
#include "../Classes/x-saherelm.x121.provider.class.mq5"
//
// Define On Signal Event Handler Type Specified for X5 ...
typedef void (*TX121OnSignal)(X121ProviderDescriptor &descriptor);
//
// Position Protect Structure ...
struct XProtectedPosition
{
//
ulong ticket; // Position Ticket
string symbol; // Position Symbol
string provider; // Position Signaller
ENUM_TIMEFRAMES period; // Position Time Frame
ENUM_POSITION_TYPE type; // Position Type
double volume; // Position Volume
double entry; // Position Entry
double sl; // Position Stop Loss
double tp; // Position Take Profit
datetime openAt; // Position Opening Time
//
// Working Variables ...
double recoveryZoneStep; // Used Recovery Zone Step
int lastRecoveryLevel; // Last Recovery Level
datetime lastRecoveryTime; // Last Recovery Time
double lastVolume; // Last Recovery Volume
ulong tickets[]; // Recovery Trades Tickets
//
// Constructor ...
XProtectedPosition()
{
Clean();
}
//
// Tools ...
//
// Initialized ...
bool Init(
double mRecoveryZoneStep,
XPosition &mPosition //
)
{
//
bool result = false;
//
// Validate Params ...
result =
//
mPosition.IsValid() &&
mRecoveryZoneStep > 0
//
;
if (!result)
{
return result;
}
//
Clean();
//
sl = mPosition.sl;
tp = mPosition.tp;
type = mPosition.type;
entry = mPosition.entry;
ticket = mPosition.ticket;
symbol = mPosition.symbol;
period = mPosition.period;
volume = mPosition.volume;
openAt = mPosition.openAt;
provider = mPosition.provider;
recoveryZoneStep = mRecoveryZoneStep;
//
result = IsValid();
//
return result;
}
//
// Cleanup ...
void Clean()
{
//
ticket = 0;
symbol = NULL;
period = NULL;
provider = NULL;
volume = 0;
entry = 0;
sl = 0;
tp = 0;
openAt = NULL;
lastRecoveryLevel = 0;
lastVolume = 0;
lastRecoveryTime = NULL;
Clean(tickets);
}
//
// Validate ...
bool IsValid()
{
//
bool result = false;
//
result =
//
ticket > 0 &&
volume > 0 &&
entry > 0 &&
tp > 0 &&
sl > 0 &&
IsSpecifiedValid(symbol) &&
IsSpecifiedValid(provider) &&
IsSpecifiedValid(period)
//
;
//
return result;
}
//
// Check Protected Type ...
bool IsLong()
{
//
bool result = false;
//
result = IsValid();
if (!result)
{
return result;
}
//
result = IsLong(this.type);
//
return result;
}
//
// Retrieve Tick Based on Protected ...
bool GetTick(
MqlTick &tick //
)
{
//
bool result = false;
//
// Validate ...
result = IsValid();
if (!result)
{
return result;
}
//
result = GetTick(
this.symbol,
tick //
);
//
return result;
}
//
// Retrieve Current Exit Price based on Protected ...
double GetExit()
{
//
double result = GetExit(
this.symbol,
this.type //
);
//
return result;
}
//
// Retrieve Current Entry Price based on Protected ...
double GetEntry()
{
//
double result = GetEntry(
this.symbol,
this.type //
);
//
return result;
}
//
// Calculate Recovery Step Price ...
double GetRecoveryAreaPrice()
{
//
double result = 0;
//
// Validate ...
if (!IsValid())
{
return result;
}
//
double stepPrice = PointToPrice(
recoveryZoneStep,
symbol //
);
//
bool isLong = IsLong(type);
//
result =
isLong
? entry - stepPrice
: entry + stepPrice;
//
return result;
}
//
// Check Protected Has Recovery or not ...
// it it's true ... fill signal by proper info ...
bool GetRecoverySignal(
XSignal &signal // Result ...
)
{
//
bool result = false;
//
signal.Clean();
//
// Validate ...
result = IsValid();
if (!result)
{
return result;
}
//
// Check Position is Reoverable or not ...
bool isLong = IsLong();
double recoveryPrice = GetRecoveryAreaPrice();
//
int currRecoveryLevel = lastRecoveryLevel + 1;
//
double mExit = GetExit();
double mEntry = GetEntry();
double recoveryDiff = MathAbs(mEntry - recoveryPrice);
//
// First Check Opposite Direction ...
bool canOppositDirectionRecover =
//
// Check State for Opposit Recovery ...
lastRecoveryLevel % 2 == 0 &&
//
(isLong
? mExit < recoveryPrice
: mExit > recoveryPrice)
//
;
//
// Check Same Direction Recovery ...
bool canSameDirectionRecover =
//
// Since for Same Direction Recover we have to at leaset 1 Opposite Recovery ...
lastRecoveryLevel % 2 == 1 &&
//
(isLong
? mEntry >= entry
: mEntry <= entry)
//
;
//
// Check Protected Can Recoverable or not ...
result =
//
lastRecoveryLevel < 5 &&
(canSameDirectionRecover ||
canOppositDirectionRecover)
//
;
if (!result)
{
return result;
}
//
// Check Last Recovery Time ...
int age =
!IsValid(this.lastRecoveryTime)
? -1
: iBarShift(
this.symbol,
this.period,
this.lastRecoveryTime,
false //
);
result =
age == -1
? true
: age >= 5;
if (!result)
{
return result;
}
//
// Now we have to Prepare Signal based on Recovery Mode ...
//
// Recovery Position Type ...
ENUM_POSITION_TYPE mType =
canSameDirectionRecover
? this.type
: GetOpposit(this.type);
//
bool isMLong = IsLong(mType);
//
// Recovery Position Entry Price ...
mEntry = GetEntry(
this.symbol,
mType //
);
//
// SL and TP ...
double mTP =
canSameDirectionRecover
? this.tp
: this.sl;
mTP =
isMLong
? mTP + recoveryDiff
: mTP - recoveryDiff;
//
double mSL =
canSameDirectionRecover
? this.sl
: this.tp;
mSL =
isMLong
? mSL - recoveryDiff
: mSL + recoveryDiff;
//
// Volume ...
// For Volume Calculating we have to use Recovery Level ...
// double mVolume =
// lastVolume == 0
// ? volume * 1
// : lastVolume * 1;
double mVolume =
canSameDirectionRecover
? volume
: volume * 2;
//
result = signal.Prepare(
this.symbol,
this.provider,
this.period,
mType,
X_ORDER_MODE_MARKET,
mEntry,
mVolume,
mSL,
mTP //
);
//
return result;
}
};
//
// a Position Protector Class ...
class XSCPositionProtector : public XSCBaseAlert
{
//
// Public ...
public:
//
// Props ...
//
bool enable;
//
// Protected Positions ...
XProtectedPosition protecteds[];
//
// Trader Class Instance ...
XSCTrade *mTrader;
//
// Constructor ...
void XSCPositionProtector()
{
//
Clean(protecteds);
//
SetRecoveryStepDivider(5);
SetRecoveryStepPeriod(PERIOD_D1);
}
//
// Deconstructor ...
void ~XSCPositionProtector()
{
delete mTrader;
}
//
// Setter(s) / Getter(s) ...
//
void SetRecoveryStepPeriod(ENUM_TIMEFRAMES value)
{
this.recoveryStepPeriod = value;
}
//
ENUM_TIMEFRAMES GetRecoveryStepPeriod()
{
return this.recoveryStepPeriod;
}
//
void SetRecoveryStepDivider(int value)
{
//
if (value <= 3)
{
value = 3;
}
//
this.recoveryStepDivider = value;
}
//
int GetRecoveryStepDivider()
{
return this.recoveryStepDivider;
}
//
void SetMinRequiredProfitPerTrade(double value)
{
//
if (value < 0)
{
value = 0;
}
//
this.minRequiredProfitPerTrade = value;
}
//
double GetMinRequiredProfitPerTrade()
{
return this.minRequiredProfitPerTrade;
}
//
void SetMinRequiredProfitPerTradeVolumeFactor(double value)
{
//
if (value < 0)
{
value = 0;
}
//
this.minRequiredProfitPerTradeVolumeFactor = value;
}
//
double GetMinRequiredProfitPerTradeVolumeFactor()
{
return this.minRequiredProfitPerTradeVolumeFactor;
}
//
// Tools ...
//
// Initial Class ...
bool Init(
bool mEnable,
XSCTrade *trader,
double mMinRequiredProfitPerTrade = 0.5,
double mMinRequiredProfitPerTradeVolumeFactor = 0.01 //
)
{
//
bool result = false;
//
this.enable = mEnable;
//
result = trader != NULL;
if (!result)
{
return result;
}
//
this.mTrader = trader;
//
SetMinRequiredProfitPerTrade(mMinRequiredProfitPerTrade);
SetMinRequiredProfitPerTradeVolumeFactor(mMinRequiredProfitPerTradeVolumeFactor);
//
return result;
}
//
// Handle Trades for Processing ...
// this used for Automating Recovery Positions ...
// we have to call this in our EA Class ...
void HandleState(const XOnTradeHandlerState &state)
{
//
if (!enable) {
return;
}
//
if (
!state.hasNewDeal &&
!state.hasNewOrder &&
!state.hasNewPosition &&
!state.hasNewHistoryOrder)
{
return;
}
//
// Here we Can Handle new Positions ...
// To Protect ...
//
// Detect New Positions ...
if (!state.hasNewPosition)
{
//
// This means a Position SL or TP or Close ...
return;
}
//
ulong lastPositionTicket = mTrader.GetLastOpenPositionTicket();
if (lastPositionTicket <= 0)
{
return;
}
//
// Retrieve XPosition ...
XPosition position;
bool isRetrieved = mTrader.GetPosition(
lastPositionTicket,
position //
);
if (!isRetrieved)
{
return;
}
//
// Now we Have Position Struct ...
// - First Check Position is new Regular Position or
// it is a Support Position;
// - then We have to Decide what to Do ...
//
// Check Position is New Or Not ...
ulong parentTicket = ExtractEQMSupportedTicket(position.comment);
if (!NotEmptyZero(parentTicket))
{
//
AddPosition(position);
}
else
{
//
UpdatePosition(
parentTicket,
position //
);
}
}
//
// Here we Process all Protected Positions ...
// for Handling Zone Recovery ...
void Process()
{
//
if (!enable) {
return;
}
//
int protectedsCount = ArraySize(protecteds);
if (IsValidSize(protectedsCount))
{
//
// Loop through Exists ...
for (int i = 0; i < protectedsCount; i++)
{
//
// Do Protection ...
HandlePositionsProtecting(protecteds[i]);
}
}
//
string comment = XEQMSupportToken + " Hege ...";
//
// Handle Hedging All Positions ...
bool allowHedge = AllowHedge();
if (allowHedge)
{
//
XPosition positions[];
int positionsCount = mTrader.GetPositions(positions);
if (!IsValidSize(positions) || positionsCount <= 1)
{
//
// Here We Can Close if Still Positions is EQM Support ...
bool canClosePosition =
//
positionsCount == 1 &&
positions[0].provider == XEQMSupportToken &&
positions[0].profit > (-1 * positions[0].swap) + (minRequiredProfitPerTrade * (positions[0].volume / minRequiredProfitPerTradeVolumeFactor))
//
;
if (canClosePosition)
{
//
bool isClosed = mTrader.Close(
positions[0].ticket,
comment //
);
}
return;
}
//
double profit = SpecifiedCalculatePositionsProfit(positions);
double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
positions,
minRequiredProfitPerTrade,
minRequiredProfitPerTradeVolumeFactor //
);
bool canHedge = SpecifiedIsPositionsReadyForHedge(
positions,
minRequiredProfitPerTrade,
minRequiredProfitPerTradeVolumeFactor //
);
if (!canHedge)
{
//
ProtectMargin();
return;
}
//
ENUM_X_SIGNAL_EXECUTION_RESULT states[];
int closed = mTrader.Close(
positions,
comment //
);
if (IsValidSize(closed))
{
Clean(protecteds);
}
}
}
//
// Sync Positions ...
//
// Remove Support ...
bool Remove(ulong ticket)
{
//
bool result = false;
//
if (!enable) {
return result;
}
//
int protectedIDX = FindProtectedIndex(ticket);
result = protectedIDX >= 0;
if (!result)
{
return result;
}
//
// Close all Protected Support Positions if Exists ...
string comment = "EQM Close In Profit ...";
//
int ticketsCount = ArraySize(protecteds[protectedIDX].tickets);
if (IsValidSize(ticketsCount))
{
//
for (int i = 0; i < ticketsCount; i++)
{
//
XPosition iPosition;
bool isRetrieved = mTrader.GetPosition(
protecteds[protectedIDX].tickets[i],
iPosition //
);
if (!isRetrieved)
{
continue;
}
//
bool isClosed = mTrader.Close(
iPosition.ticket,
comment //
);
}
}
//
result = ArrayRemove(
protecteds,
protectedIDX,
1 //
);
//
// TODO: Update Collection here ...
//
return result;
}
//
// Protected ...
protected:
//
// Props ...
double minRequiredProfitPerTrade; // Minimum Required Profit Per Trade for Hedging
double minRequiredProfitPerTradeVolumeFactor; // Calculate Required Profit Based on Volume Factor for Hedging
int recoveryStepDivider; // Recovery Step Divider
ENUM_TIMEFRAMES recoveryStepPeriod; // Recovery Step Period
//
// Tools ...
//
// Here we recieve a new Position and
// Prepare it for Protecting ...
void AddPosition(XPosition &position)
{
//
// - Recieve Last Day Up and Low Boundary
// - Divided to 5
// - Find Recovery Zone Step
// - Calculate Recoery Zone for this Specific Position
// - Prepare it's Structure
// - Add it to Collection
//
// Validate Params ...
if (!position.IsValid())
{
return;
}
//
double recoveryStep = CalculateRecoveryZoneStep(position);
if (recoveryStep <= 0)
{
return;
}
//
XProtectedPosition item;
bool isInited = item.Init(
recoveryStep,
position //
);
if (!isInited)
{
return;
}
//
AddRef(
item,
protecteds //
);
//
// TODO: Also Here We Can Collect Data ...
}
//
// Here we recieve a Support Position and
// need to Update Parent Protected Position's
// info ...
void UpdatePosition(
ulong parentTicket, // Parent Position Ticket
XPosition &position // Support Position
)
{
//
Print("Update Protected Position: " + ToString(parentTicket));
}
//
// Calculate Position Recovery one Step ...
double CalculateRecoveryZoneStep(XPosition &position)
{
//
double result = 0;
//
// Validate Position ...
if (!position.IsValid())
{
return result;
}
//
// Validate Position must have TP and SL ...
if (position.tp == 0 || position.sl == 0)
{
return result;
}
//
// Calculate Position Risk at Point ...
double point = GetPoints(position.symbol);
int digits = GetDigits(position.symbol);
double riskPoint = NormalizeDouble(MathAbs(position.entry - position.sl), digits) / point;
//
// Retrieve Bar for finding Recovery Step ...
XOHCL rBar;
bool isInited = rBar.Init(
position.symbol,
recoveryStepPeriod,
1 //
);
if (!isInited)
{
return result;
}
//
// Calculate Recovery Step ...
double riskPointRecoveryStep = riskPoint / 2;
double recoveryStep = (NormalizeDouble(MathAbs(rBar.high - rBar.low), digits) / point) / recoveryStepDivider;
result = MathMin(riskPointRecoveryStep, recoveryStep);
//
return result;
}
//
// Here we Implement all Protecting Senarios here ...
void HandlePositionsProtecting(XProtectedPosition &item)
{
//
// Check protected Validation ...
if (!item.IsValid())
{
return;
}
//
// Check Has Recovery Signal Or Not ...
XSignal signal;
bool hasRecovery = item.GetRecoverySignal(signal);
if (!hasRecovery)
{
return;
}
//
signal.provider = XEQMSupportToken;
string comment = GenerateEQMSupportTag(item.ticket);
signal.comment = comment;
//
// Remove Support Signal TP and SL ...
signal.tp = 0;
signal.sl = 0;
//
ENUM_X_SIGNAL_EXECUTION_RESULT state;
bool isExecuted = mTrader.ExecuteSignal(
signal,
state,
ORDER_TIME_GTC,
NULL,
false // Ignore Policies ...
);
if (isExecuted)
{
//
// Do What we Want ...
item.lastRecoveryLevel++;
item.lastVolume = signal.volume;
item.lastRecoveryTime = TimeCurrent();
Add(
signal.positionId,
item.tickets //
);
//
int protectedIDX = FindProtectedIndex(item.ticket);
if (protectedIDX < 0)
{
return;
}
//
protecteds[protectedIDX] = item;
//
// TODO: Update Collect here ...
}
}
//
// Check Hedging is Enabled ...
bool AllowHedge()
{
//
bool result =
//
minRequiredProfitPerTrade > 0 &&
minRequiredProfitPerTradeVolumeFactor > 0
//
;
//
return result;
}
//
// Protecting Margin by Free Coveraged Positions ...
void ProtectMargin()
{
//
double freeMargin = mTrader.mAccount.GetFreeMargin();
//
double balance = mTrader.mAccount.GetBalance();
double equity = mTrader.mAccount.GetEquity();
//
double selectedBalance = MathMin(balance, equity);
//
// Retrieve All Positions ...
XPosition positions[];
int positionsCount = mTrader.GetPositions(positions);
if (!IsValidSize(positionsCount))
{
return;
}
//
bool canForceHedging =
positionsCount >= 25 ||
freeMargin <= selectedBalance / 2;
if (!canForceHedging)
{
return;
}
//
// First Check Hedging By Half of Required Profit ...
double profit = SpecifiedCalculatePositionsProfit(positions);
double requiredProfit = SpecifiedCalculateRequiredProfitForHedge(
positions,
minRequiredProfitPerTrade,
minRequiredProfitPerTradeVolumeFactor //
);
bool canHedge =
//
profit > requiredProfit / 2
//
;
if (canHedge)
{
//
string comment = XEQMSupportToken + " Hege ...";
int closed = mTrader.Close(
positions,
comment //
);
//
return;
}
//
// TODO: Enable this if we want ...
return;
//
// Retrieve In Drawdown Positions ...
XPosition inDPositions[];
int inDPositionsCount = ExtractInDrawdownPositions(
positions,
inDPositions ///
);
if (!IsValidSize(inDPositionsCount))
{
return;
}
//
// Retrieve In Profit Positions ...
XPosition inPPositions[];
int inPPositionsCount = ExtractInProfitPositions(
positions,
inPPositions //
);
if (!IsValidSize(inPPositionsCount))
{
return;
}
//
// if we can't Hedge all Positions ...
// now we are looking to pair Positions for hedge ...
// - Select Max In Drawdown Position;
// - Looking for Coverage it inside In Profit Positions;
// - Clease All of them ...
int maxInDIDX = FindMaxDrawdownIndex(inDPositions);
if (maxInDIDX < 0)
{
return;
}
//
string comment = XEQMSupportToken + " Force Hege ...";
//
XPosition maxInDCoverages[];
int maxInDCoveragesCount = FindCoverageDrawdownPosition(
inDPositions[maxInDIDX],
inPPositions,
maxInDCoverages,
minRequiredProfitPerTrade,
minRequiredProfitPerTradeVolumeFactor //
);
if (!IsValidSize(maxInDCoveragesCount))
{
return;
}
//
bool isClosedMaxInD = mTrader.Close(
inDPositions[maxInDIDX].ticket,
comment //
);
if (isClosedMaxInD)
{
//
int closed = mTrader.Close(
maxInDCoverages,
comment //
);
}
}
//
// Protected Collection Management ...
//
int CountProtecteds()
{
return ArraySize(protecteds);
}
//
int FindProtectedIndex(XProtectedPosition &item)
{
//
int result = -1;
//
if (!item.IsValid())
{
return result;
}
//
result = FindProtectedIndex(item.ticket);
//
return result;
}
//
int FindProtectedIndex(ulong ticket)
{
//
int result = -1;
//
if (!NotEmptyZero(ticket))
{
return result;
}
//
int protectedsCount = CountProtecteds();
if (!IsValidSize(protectedsCount))
{
return result;
}
//
for (int i = 0; i < protectedsCount; i++)
{
//
if (protecteds[i].ticket == ticket)
{
//
result = i;
break;
}
}
//
return result;
}
};
//
// Class Definition ...
class XSCX121EA : public XSCBaseEA
{
//
// Public ...
public:
//
// Props ...
//
// Constructors ...
XSCX121EA(
//
// XTrade Class Requirements ...
int slippage, // Specify Slippage
ulong magicNumber, // Specify Magic Number
//
// Time Management ...
// TODO: ...
//
// XSCTrade Event Handlers ...
TOnStopLoss onStopLossTriggered = NULL, // On StopLoass Triggered Event Handler
TOnTakeProfit onTakeProfitTriggered = NULL, // On TakeProfit Triggered Event Handler
TOnDealsChanged onDealsChangedHandler = NULL, // On Deals Changed Event Handler
TOnOrdersChanged onOrdersChangedHandler = NULL, // On Orders Changed Event Handler
TOnPositionsChanged onPositionsChangedHandler = NULL, // On Positions Changed Event Handler
TOnTradeStateChanged onTradeStateChangedHandler = NULL, // On Trade State Changed Event Handler
//
// Custom Event Handler ...
TOnSignal onSignalHandler = NULL // On Signal Event Handler
) : XSCBaseEA(slippage,
magicNumber,
onStopLossTriggered,
onTakeProfitTriggered,
onDealsChangedHandler,
onOrdersChangedHandler,
onPositionsChangedHandler,
onTradeStateChangedHandler //
)
{
//
mProtector = new XSCPositionProtector();
mProtector.Init(false, mTrader);
}
//
// Deconstructor ...
~XSCX121EA()
{
delete mProtector;
}
//
// Properties Getter(s) / Setter(s) ...
//
// Add X5 Specified Signal Event Handler ...
void AddOnSignalEventHandler(TX121OnSignal listener)
{
//
Add(
listener,
mX121OnSignalEventHandlers
//
);
}
//
// Add Specified X5 Provider ...
bool AddProvider(X121ProviderDescriptor &descriptor)
{
//
bool result = false;
//
// Validate Inputs ...
result = descriptor.Init();
if (!result)
{
return result;
}
//
AddRef(
descriptor,
mDescriptors //
);
//
return result;
}
//
// Overrides ...
//
// Customize Token ...
string GetToken() override
{
return GetSpecificToken(this);
}
//
string GetTag() override
{
return this.GetToken();
}
//
void Draw() override
{
//
int descriptorsCount = CountDescriptors();
if (descriptorsCount <= 0)
{
return;
}
//
for (int i = 0; i < descriptorsCount; i++)
{
//
X121ProviderDescriptor iDescriptor = mDescriptors[i];
//
iDescriptor.provider.Draw();
}
}
//
// Check Provider for any Guards ...
bool CheckForGuard(XGuard &guards[]) override
{
//
bool result = false;
//
Clean(guards);
//
int descriptorsCount = CountDescriptors();
result = descriptorsCount > 0;
if (!result)
{
return result;
}
//
// Loop Through Descriptors for Collecting Guards ...
for (int i = 0; i < descriptorsCount; i++)
{
//
X121ProviderDescriptor iDescriptor = mDescriptors[i];
//
XGuard iGuards[];
int iGuardsCount = iDescriptor.HasGuard(iGuards);
if (!IsValidSize(iGuardsCount))
{
continue;
}
//
Copy(
iGuards,
guards,
false //
);
}
//
result = ArraySize(guards) > 0;
//
return result;
}
//
// Request Provider to Collect all
// Potentially Signals and then
// filters theme here and passed them
// for Executing ...
int RequestForSignal(
XSignal &signals[] // Holds Signals ...
) override
{
//
bool result = 0;
//
Clean(signals);
//
int descriptorsCount = CountDescriptors();
if (descriptorsCount <= 0)
{
return result;
}
//
for (int i = 0; i < descriptorsCount; i++)
{
//
X121ProviderDescriptor iDescriptor = mDescriptors[i];
//
if (iDescriptor.provider.CanIgnoreProcess())
{
continue;
}
//
int iSignalsCount = iDescriptor.HasSignal();
if (IsValidSize(iSignalsCount))
{
//
// Here we Can double check Signals by Conditions
// for Score Base Filtering ...
iDescriptor.provider.SetWaitsUntilNewBar(true);
//
Copy(
iDescriptor.signals,
signals,
false
//
);
//
NotifyX121OnSignalEventHandlers(iDescriptor);
}
}
//
result = ArraySize(signals);
//
return result;
}
//
// Request for Support Signals using Guard ...
bool RequestForSupport(
XSignal &support, // Holds Support Signal, if Provided
string provider, // Positions Provided by Specific Provider
string symbol, // Trading Symbol
ENUM_X_POSITION_TYPE type, // Trading Type
ENUM_TIMEFRAMES period // Specified Time Frame
) override
{
//
// TODO: Implement this ...
//
// Support Senario ...
// Check Support Position Exists or not ...
// Check Positions for Support based on Types ...
// Update Untriggered Positions ...
return false;
}
//
// Here we Manage Signals for Executing ...
// - Check Policies based on their Signaller ...
// - Check Same Time Open Positions ...
// - Check Signal Age for new Trade ...
// and etc ...
int HandleSignalManagement(XSignal &signals[]) override
{
//
int result = 0;
//
int signalsCount = ArraySize(signals);
if (!IsValidSize(signalsCount))
{
return result;
}
//
XSignal tmpSignals[];
Copy(
signals,
tmpSignals //
);
//
Clean(signals);
//
for (int i = 0; i < signalsCount; i++)
{
//
XSignal iSignal = tmpSignals[i];
//
// Find Provider Descriptor Which Issued this Signal ...
int iProviderIDX = FindDescriptorIndex(
iSignal.symbol,
iSignal.period);
if (!IsValidIndex(iProviderIDX))
{
//
string message = "Couldn't find Signal Descriptor ...";
Log(message);
//
continue;
}
//
// Now we Have to Find Signaller Which Issued this Signal ...
int iSignallerIDX = mDescriptors[iProviderIDX]
.FindSignallerIndex(iSignal.provider);
if (!IsValidIndex(iSignallerIDX))
{
//
string message = "Couldn't find Signal Provider ...";
Log(message);
//
continue;
}
//
// Check Position Type is Enabled or not ...
bool isLong = IsLong(iSignal.type);
bool isAllowedType = isLong
? mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowLong
: mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowShort;
if (!isAllowedType)
{
//
string message = "ignore Signal due Type Policy ...";
Log(message);
//
continue;
}
//
// Retrieve Long and Short Signals of same Provider ...
XPosition longs[];
XPosition shorts[];
mTrader.GetPositions(
longs,
shorts,
iSignal.symbol,
iSignal.provider,
iSignal.period //
);
int longsCount = ArraySize(longs);
int shortsCount = ArraySize(shorts);
//
// Check Max Number of Positions ...
bool isMaxNumberOfPositionsPassed = isLong
? mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong <= 0
? true
: longsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong
: mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort <= 0
? true
: shortsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort;
if (!isMaxNumberOfPositionsPassed)
{
//
string message = "ignore Signal due Max Allowed Positions Reached ...";
Log(message);
//
continue;
}
//
// Check Delay Between Two Same Type Signals ...
// Check Open Next Behaviour ...
if (longsCount > 0 || shortsCount > 0)
{
//
// Check if Signaller Configured for Check Delay between Same type Signals ...
if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals > 0)
{
//
bool isDelayPassed = true;
//
if (longsCount > 0)
{
//
XPosition youngestLong;
int youngestLongAge = GetYoungest(
youngestLong,
longs //
);
//
if (isLong)
{
isDelayPassed = youngestLongAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals;
}
}
//
if (shortsCount > 0)
{
//
XPosition youngestShort;
int youngetsShortAge = GetYoungest(
youngestShort,
shorts //
);
//
if (!isLong)
{
isDelayPassed = youngetsShortAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals;
}
}
//
if (!isDelayPassed)
{
//
string message = "ignore Signal due Delay Between Same Type Policy ...";
Log(message);
//
continue;
}
}
//
// Check Next Position ...
if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].openNextPositionOnProfit)
{
//
bool isNextPassed = true;
//
if (isLong && longsCount > 0)
{
//
double profit = SpecifiedCalculatePositionsProfit(longs);
isNextPassed = profit > 0;
}
//
if (!isLong && shortsCount > 0)
{
//
double profit = SpecifiedCalculatePositionsProfit(shorts);
isNextPassed = profit > 0;
}
//
// Check Ignore for Opposit Signals ...
if (!isNextPassed &&
((isLong && shortsCount > longsCount) ||
(!isLong && longsCount > shortsCount)))
{
isNextPassed = mDescriptors[iProviderIDX].signallers[iSignallerIDX].ignoreProfitForOppositeSignals;
}
//
if (!isNextPassed)
{
//
string message = "ignore Signal due Next Must be In Profit Policy ...";
Log(message);
//
continue;
}
}
}
//
// Finally Add Filtered Signals into Result ...
AddRef(
iSignal,
signals //
);
}
//
result = ArraySize(signals);
//
return result;
}
//
// Handle State Management ....
// here we can manage current state ...
// - Check for Long Positions for each Signaller to Close ...
// - Handle Hedging Signaller's Positions if it's enabled ...
// - Force Closing Position when Specified Time reached ...
// - Check Start and End time or Trading Dates ...
// - handle Trailing or Risk free Signals based on several conditions ...
// and etc ...
bool HandleStateManagement(XSignal &signals[]) override
{
//
const bool result = false;
//
// Here we Implement Account Protector ...
HandleAccountProtect();
//
// Check Descriptor ...
int descriptorsCount = CountDescriptors();
if (!IsValidSize(descriptorsCount))
{
return result;
}
//
// Loop Through Descriptors ...
for (int i = 0; i < descriptorsCount; i++)
{
//
// Check Signallers ...
int signallersCount = mDescriptors[i].CountSignallers();
if (!IsValidSize(signallersCount))
{
continue;
}
//
// Loop Through Signallers ...
for (int j = 0; j < signallersCount; j++)
{
//
// Retrieve Specified Signaller's Position ...
XPosition positions[];
int positionsCount = mTrader.GetPositions(
positions,
mDescriptors[i].symbol,
mDescriptors[i].signallers[j].GetName(),
mDescriptors[i].period);
if (!IsValidSize(positionsCount))
{
continue;
}
//
// Check Signaller Enable Hedge or not and Handle it if enabled ...
if (positionsCount > 1 && mDescriptors[i].signallers[j].AllowHedge())
{
//
// Check Profits Enough for Hedge or not ...
double profit = SpecifiedCalculatePositionsProfit(positions);
bool isReadyForHedge =
positionsCount > 1 &&
SpecifiedIsPositionsReadyForHedge(
positions,
mDescriptors[i].signallers[j].minRequiredProfitPerTrade,
mDescriptors[i].signallers[j].minRequiredProfitPerTradeVolumeFactor //
);
if (isReadyForHedge)
{
//
string comment = "Close due Hedge ...";
int closeds = mTrader.Close(
positions,
comment //
);
//
if (closeds > 0)
{
//
string message = "Hedge (" + ToString(closeds) + ") Positions In Profit: " + ToString(profit);
//
Alert(message);
//
break;
}
}
}
//
// Check Closing Long Age Position's Enabled or not ...
if (mDescriptors[i].signallers[j].maxAllowedOpenPositionAge > 0)
{
//
// Handle Close Long Time Trades ...
//
// Find Oldest ...
XPosition oldest;
int oldestAge = GetOldest(
oldest,
positions //
);
if (oldestAge >= mDescriptors[i].signallers[j].maxAllowedOpenPositionAge)
{
//
string comment = "Close due Long Age ...";
bool isClosed = mTrader.Close(
oldest.ticket,
comment //
);
//
if (isClosed)
{
//
string message = "Position (" + ToString(oldest.ticket) + ") Closed due Long Age Policy ...";
Alert(message);
}
}
}
}
}
//
// if Returns true, Signal Execution failed ...
return result;
}
//
void OnStopLossTriggered(const XDeal &deal) override
{
//
// Remove Position Protecting ...
mProtector.Remove(deal.positionId);
}
//
void OnTakeProfitTriggered(const XDeal &deal) override
{
//
// Remove Position Protecting ...
mProtector.Remove(deal.positionId);
}
//
void OnTradeStateChangedHandler(
const XOnTradeHandlerState &state //
) override
{
//
// Calling Protector to Handle State ...
mProtector.HandleState(state);
}
//
void HandleAccountProtect()
{
//
// Position Protector Calls to Process State ...
mProtector.Process();
}
//
// Tools ...
//
// Protected ...
protected:
//
// Tools ...
//
void NotifyX121OnSignalEventHandlers(X121ProviderDescriptor &descriptor)
{
//
int listenersCount = ArraySize(mX121OnSignalEventHandlers);
if (listenersCount <= 0)
{
return;
}
//
for (int i = 0; i < listenersCount; i++)
{
//
TX121OnSignal iListener = mX121OnSignalEventHandlers[i];
//
iListener(descriptor);
}
}
//
// Protect Specified Position ...
void ProtectPosition(XPosition &position)
{
//
// Here i Can Protect Position ...
// This Protect Include One Position in Market Mode ...
// If Provided ...
// the Positions Selection must passed Some conditions ...
//
string message = "For (" + position.symbol + ") Max In Drawdown Position is: (" +
ToString(position.ticket) + "), by Profit: " + ToString(position.profit);
//
mAlert.Alert(message);
}
//
// Private ...
private:
//
// Props ...
//
// Collection of Signal Event Listeners ...
TX121OnSignal mX121OnSignalEventHandlers[];
//
// a Collection of X5 Provider Descriptors ...
X121ProviderDescriptor mDescriptors[];
//
int CountDescriptors()
{
return ArraySize(mDescriptors);
}
//
// Find Specifc Descriptor ...
int FindDescriptorIndex(
string symbol,
ENUM_TIMEFRAMES period //
)
{
//
int result = -1;
//
if (!IsValid(symbol) ||
!IsValid(period))
{
return result;
}
//
int descriptorsCount = CountDescriptors();
if (!IsValidSize(descriptorsCount))
{
return result;
}
//
for (int i = 0; i < descriptorsCount; i++)
{
//
X121ProviderDescriptor iDescriptor = mDescriptors[i];
//
bool isPassed =
//
iDescriptor.symbol == symbol &&
iDescriptor.period == period
//
;
if (isPassed)
{
//
result = i;
break;
}
}
//
return result;
}
template <typename T>
int FindDescriptorIndex(
T &item //
)
{
return FindDescriptorIndex(
item.symbol,
item.period //
);
}
//
// Trails Holding ...
//
XTrail mSLTrails[];
XTrail mTPTrails[];
//
int CountSLTrails()
{
return ArraySize(mSLTrails);
}
int CountTPTrails()
{
return ArraySize(mTPTrails);
}
//
bool RemoveTrail(ulong ticket)
{
//
bool isSLRemoved = RemoveSLTrail(ticket);
bool isTPRemoved = RemoveTPTrail(ticket);
//
bool result = isSLRemoved || isTPRemoved;
//
return result;
}
bool RemoveSLTrail(ulong ticket)
{
//
bool result = false;
//
int idx = FindSLTrailIndex(ticket);
result = idx >= 0;
if (!result)
{
return result;
}
//
result = ArrayRemove(
mSLTrails,
idx,
1 //
);
//
return result;
}
bool RemoveTPTrail(ulong ticket)
{
//
bool result = false;
//
int idx = FindTPTrailIndex(ticket);
result = idx >= 0;
if (!result)
{
return result;
}
//
result = ArrayRemove(
mTPTrails,
idx,
1 //
);
//
return result;
}
//
int FindSLTrailIndex(ulong ticket)
{
//
int result = -1;
//
int trailsCount = CountSLTrails();
if (ticket <= 0 || trailsCount <= 0)
{
return result;
}
//
for (int i = 0; i < trailsCount; i++)
{
//
XTrail iTrail = mSLTrails[i];
//
if (iTrail.ticket == ticket)
{
//
result = i;
break;
}
}
//
return result;
}
int FindTPTrailIndex(ulong ticket)
{
//
int result = -1;
//
int trailsCount = CountTPTrails();
if (ticket <= 0 || trailsCount <= 0)
{
return result;
}
//
for (int i = 0; i < trailsCount; i++)
{
//
XTrail iTrail = mTPTrails[i];
//
if (iTrail.ticket == ticket)
{
//
result = i;
break;
}
}
//
return result;
}
//
bool GetSLTrail(
ulong ticket,
XTrail &trail //
)
{
//
bool result = false;
//
int idx = FindSLTrailIndex(ticket);
result = idx >= 0;
if (!result)
{
return result;
}
//
trail = mSLTrails[idx];
//
return result;
}
bool GetTPTrail(
ulong ticket,
XTrail &trail //
)
{
//
bool result = false;
//
int idx = FindTPTrailIndex(ticket);
result = idx >= 0;
if (!result)
{
return result;
}
//
trail = mTPTrails[idx];
//
return result;
}
//
void AddSLTrail(
XTrail &trail //
)
{
//
if (!trail.IsValid())
{
return;
}
//
AddRef(
trail,
mSLTrails //
);
}
void AddTPTrail(
XTrail &trail //
)
{
//
if (!trail.IsValid())
{
return;
}
//
AddRef(
trail,
mTPTrails //
);
}
//
// Position Protector ...
XSCPositionProtector *mProtector;
//
};
//
// Tools ...
//