1676 lines
44 KiB
Plaintext
1676 lines
44 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);
|
|
|
|
//
|
|
// Class Definition ...
|
|
|
|
class XSCX121EA : public XSCBaseEA
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
// Constructors ...
|
|
XSCX121EA(
|
|
//
|
|
// XTrade Class Requirements ...
|
|
int slippage, // Specify Slippage
|
|
ulong magicNumber, // Specify Magic Number
|
|
int manageInterval, // Manager Check Intervals Seconds
|
|
double maxAllowedSpread, // Max Allowed Spred for Opening Trades
|
|
int maxAllowedPositions, // Max Allowed Positions
|
|
double maxAllowedDrawdownFactor, // Max Allowed Drawdown Factor
|
|
double staticVolume, // Static Volume for Positions
|
|
//
|
|
// Position Management ...
|
|
bool allowLong = true, // Allow Long Trades
|
|
bool allowShort = true, // Allow Short Trades
|
|
double minProfitPerTrade = 0, // Min Profit Per Trade based On Volume Factor (Hedge)
|
|
double minProfitPerVolumeFactor = 0, // Min Volume Factor for Calculating Profit (Hedge)
|
|
//
|
|
// 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
|
|
//
|
|
TOnSignal onSignalHandler = NULL, // On Signal Event Handler
|
|
//
|
|
// Log Handler ...
|
|
bool enableAlerts = true, // Enable Alerts
|
|
bool logAlerts = true, // Log Alerts
|
|
bool terminalAlerts = false, // Terminal Alerts
|
|
bool mailAlerts = false, // Mail Alerts
|
|
bool pushAlerts = false // Push Alerts
|
|
) : XSCBaseEA(slippage,
|
|
magicNumber,
|
|
manageInterval,
|
|
maxAllowedSpread,
|
|
maxAllowedPositions,
|
|
maxAllowedDrawdownFactor,
|
|
staticVolume,
|
|
allowLong,
|
|
allowShort,
|
|
minProfitPerTrade,
|
|
minProfitPerVolumeFactor,
|
|
onStopLossTriggered,
|
|
onTakeProfitTriggered,
|
|
onDealsChangedHandler,
|
|
onOrdersChangedHandler,
|
|
onPositionsChangedHandler,
|
|
onTradeStateChangedHandler,
|
|
enableAlerts,
|
|
logAlerts,
|
|
terminalAlerts,
|
|
mailAlerts,
|
|
pushAlerts //
|
|
)
|
|
{
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XSCX121EA() {}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < descriptorsCount; i++)
|
|
{
|
|
//
|
|
X121ProviderDescriptor iDescriptor = mDescriptors[i];
|
|
|
|
//
|
|
XGuard iGuards[];
|
|
bool iHasGuard = iDescriptor.provider.HasGuard(iGuards);
|
|
if (iHasGuard)
|
|
{
|
|
//
|
|
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];
|
|
|
|
//
|
|
bool iHasSignal = iDescriptor.HasSignal();
|
|
if (iHasSignal)
|
|
{
|
|
//
|
|
// Here we Can double check Signals by Conditions
|
|
// for Score Base Filtering ...
|
|
|
|
//
|
|
AddRef(
|
|
iDescriptor.signal,
|
|
signals
|
|
//
|
|
);
|
|
|
|
//
|
|
NotifyX121OnSignalEventHandlers(iDescriptor);
|
|
}
|
|
}
|
|
|
|
//
|
|
result = ArraySize(signals);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Handl Management Actions ...
|
|
// if returns true, it is going to prevent for processing forward ...
|
|
bool HandleStateManagement(XSignal &signals[]) override
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Synch Position Tracker ...
|
|
SyncPositionTracker();
|
|
|
|
//
|
|
HandleEQMState(signals);
|
|
|
|
//
|
|
HandleEQMHedge(positions);
|
|
|
|
//
|
|
HandleTrailManagement(positions);
|
|
|
|
//
|
|
// Close all Positions on 22 ...
|
|
// TODO: Make it Configurable ...
|
|
int closeHour = -1;
|
|
MqlDateTime cTime = GetCurrentTime();
|
|
if (closeHour >= 0 && cTime.hour == closeHour)
|
|
{
|
|
//
|
|
string comment = "EQM Close on Time ...";
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int closed = mTrader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
if (closed > 0)
|
|
{
|
|
mAlert.Alert(comment);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Must false ...
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void HandleEQMHedge(XPosition &positions[])
|
|
{
|
|
//
|
|
// TODO:
|
|
// Make them Configurable ...
|
|
int numberOfPositionsForHege = 3;
|
|
|
|
//
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Hedge ...
|
|
if (positionsCount >= numberOfPositionsForHege)
|
|
{
|
|
//
|
|
int maxAllowedPositions = GetMaxAllowedPositions();
|
|
|
|
//
|
|
double profit = CalculatePositionsProfit(positions);
|
|
double requiredProfit = CalculateRequiredProfitForHedge(positions);
|
|
|
|
//
|
|
bool isReadyForHedge =
|
|
positionsCount < maxAllowedPositions
|
|
? profit >= requiredProfit
|
|
: positionsCount >= maxAllowedPositions && profit >= (requiredProfit * 0.50)
|
|
? true
|
|
: false;
|
|
if (!isReadyForHedge)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
string comment = "EQM Hedge ...";
|
|
int closed = mTrader.Close(
|
|
positions,
|
|
comment);
|
|
if (closed > 0)
|
|
{
|
|
//
|
|
string message = "EQM Hedge Positions on Profit: " + ToString(profit);
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
|
|
//
|
|
RemoveEQMOrders();
|
|
|
|
//
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
RemoveTrail(positions[i].ticket);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void HandleEQMState(XSignal &signals[])
|
|
{
|
|
//
|
|
bool equityReached = !mTrader.CheckEquityForTrade();
|
|
bool maxAllowedReached = !mTrader.CheckPositionsForTrade();
|
|
bool enableEQM = equityReached || maxAllowedReached;
|
|
// if (!enableEQM)
|
|
// {
|
|
// //
|
|
// return;
|
|
// }
|
|
|
|
//
|
|
string mSymbols[];
|
|
XSCPositionHolder *mHolders[];
|
|
int symbolsCount = FilterSymbolBasedPositions(
|
|
mSymbols,
|
|
mHolders //
|
|
);
|
|
|
|
//
|
|
for (int i = 0; i < symbolsCount; i++)
|
|
{
|
|
//
|
|
string iSymbol = mSymbols[i];
|
|
XSCPositionHolder *iHolder = mHolders[i];
|
|
|
|
//
|
|
// We Can Select Multiple Positions for Place EQM Supports ...
|
|
|
|
//
|
|
// Select Max In Drawdown Position and Protect it ...
|
|
XPosition iSelectedPosition;
|
|
bool iHasPosition = iHolder.GetMaxInDrawdown(iSelectedPosition);
|
|
if (iHasPosition)
|
|
{
|
|
//
|
|
// Try to Protect Max In Drawdown Positions Position ...
|
|
PlaceEQMOrders(iSelectedPosition);
|
|
}
|
|
}
|
|
|
|
//
|
|
}
|
|
|
|
//
|
|
// Add Or Update EQM Orders ...
|
|
void PlaceEQMOrders(XPosition &position)
|
|
{
|
|
//
|
|
// TODO: Make It Configurable ...
|
|
int maxEQMCount = 5;
|
|
return;
|
|
|
|
//
|
|
// Position Protection Senarioos ...
|
|
if (!position.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int descriptorIDX = FindDescriptorIndex(position);
|
|
if (descriptorIDX <= -1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
X121ProviderDescriptor descriptor = mDescriptors[descriptorIDX];
|
|
|
|
//
|
|
XOHCLSupRes slSupRes;
|
|
bool hasSupRes = descriptor
|
|
.provider
|
|
.GenerateSupRes(
|
|
slSupRes,
|
|
position.type,
|
|
5 //
|
|
);
|
|
|
|
//
|
|
XDrawSpecifications supDrawSpecs;
|
|
supDrawSpecs.draw = true;
|
|
supDrawSpecs.rayRight = true;
|
|
supDrawSpecs.clr = clrLightGreen;
|
|
|
|
//,
|
|
XDrawSpecifications resDrawSpecs;
|
|
resDrawSpecs.draw = true;
|
|
resDrawSpecs.rayRight = true;
|
|
resDrawSpecs.clr = clrLightCoral;
|
|
|
|
//
|
|
// DrawSupRes(
|
|
// slSupRes,
|
|
// supDrawSpecs,
|
|
// resDrawSpecs //
|
|
// );
|
|
|
|
//
|
|
XSignal supports[];
|
|
int supportsCount = GenerateEQMOrders(
|
|
supports,
|
|
position,
|
|
slSupRes //
|
|
);
|
|
if (!IsValidSize(supportsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XOrder untriggeredOrders[];
|
|
int untriggeredOrdersCount = mTrader.GetOrders(
|
|
untriggeredOrders,
|
|
position.symbol,
|
|
XEQMSupportToken,
|
|
position.period,
|
|
NULL, // All Types ...
|
|
ORDER_STATE_PLACED,
|
|
true //
|
|
);
|
|
|
|
//
|
|
XPosition triggereSupports[];
|
|
int tiggeredSupportsCount = mTrader.GetPositions(
|
|
triggereSupports,
|
|
position.symbol,
|
|
XEQMSupportToken,
|
|
position.period,
|
|
NULL, // All Types ...
|
|
true //
|
|
);
|
|
|
|
//
|
|
if (untriggeredOrdersCount +
|
|
tiggeredSupportsCount >=
|
|
maxEQMCount)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XSignal filteredSignals[];
|
|
for (int i = 0; i < supportsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSupport = supports[i];
|
|
|
|
//
|
|
double sl;
|
|
iSupport.RegularSL(sl);
|
|
|
|
//
|
|
double tp;
|
|
iSupport.RegularTP(tp);
|
|
|
|
//
|
|
if (!IsValidSize(untriggeredOrdersCount) && !IsValidSize(tiggeredSupportsCount))
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
filteredSignals //
|
|
);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isOrdersPassed = true;
|
|
if (!IsValidSize(untriggeredOrdersCount))
|
|
{
|
|
isOrdersPassed = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
for (int j = 0; j < untriggeredOrdersCount; j++)
|
|
{
|
|
//
|
|
XOrder jOrder = untriggeredOrders[j];
|
|
|
|
//
|
|
bool isSame =
|
|
//
|
|
jOrder.symbol == iSupport.symbol &&
|
|
jOrder.period == iSupport.period &&
|
|
jOrder.openPrice == iSupport.entry &&
|
|
jOrder.sl == sl &&
|
|
jOrder.tp == tp
|
|
//
|
|
;
|
|
if (isSame)
|
|
{
|
|
//
|
|
isOrdersPassed = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
bool isPositionsPassed = true;
|
|
if (!IsValidSize(tiggeredSupportsCount))
|
|
{
|
|
isPositionsPassed = true;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
for (int j = 0; j < tiggeredSupportsCount; j++)
|
|
{
|
|
//
|
|
XPosition jPosition = triggereSupports[j];
|
|
|
|
//
|
|
bool isSame =
|
|
//
|
|
jPosition.symbol == iSupport.symbol &&
|
|
jPosition.period == iSupport.period &&
|
|
jPosition.entry == iSupport.entry &&
|
|
jPosition.sl == sl &&
|
|
jPosition.tp == tp
|
|
//
|
|
;
|
|
if (isSame)
|
|
{
|
|
//
|
|
isPositionsPassed = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
if (isOrdersPassed &&
|
|
isPositionsPassed)
|
|
{
|
|
//
|
|
AddRef(
|
|
iSupport,
|
|
filteredSignals //
|
|
);
|
|
}
|
|
}
|
|
int filteredSignalsCount = ArraySize(filteredSignals);
|
|
if (!IsValidSize(filteredSignalsCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT states[];
|
|
int placedOrders = mTrader.ExecuteSignals(
|
|
filteredSignals,
|
|
states,
|
|
ORDER_TIME_GTC,
|
|
NULL,
|
|
false // Ignore Policies ...
|
|
);
|
|
if (placedOrders > 0)
|
|
{
|
|
//
|
|
string message = "EQM Placed (" + ToString(placedOrders) + ") Support Orders ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
void RemoveEQMOrders()
|
|
{
|
|
//
|
|
XOrder untriggeredOrders[];
|
|
int untriggeredOrdersCount = mTrader.GetOrders(
|
|
untriggeredOrders,
|
|
NULL,
|
|
XEQMSupportToken,
|
|
NULL,
|
|
NULL, // All Types ...
|
|
ORDER_STATE_PLACED,
|
|
true //
|
|
);
|
|
if (!IsValidSize(untriggeredOrdersCount))
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int canceleds = mTrader.CancelOrders(
|
|
untriggeredOrders //
|
|
);
|
|
if (canceleds > 0)
|
|
{
|
|
//
|
|
string message = "EQM Cancel Support ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
void HandleTrailManagement(XPosition &positions[])
|
|
{
|
|
//
|
|
// TODO:
|
|
// Make them Configurable ...
|
|
int maxTrailTPs = 5;
|
|
int levelsOnClose = 6;
|
|
int riskFreeLevel = 2;
|
|
int requiredAgeForTrail = 0;
|
|
int requiredDistanceForTrail = 20;
|
|
|
|
//
|
|
int positionsCount = ArraySize(positions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double profit = CalculatePositionsProfit(positions);
|
|
|
|
//
|
|
// Trail Positions Stop ...
|
|
for (int i = 0; i < positionsCount; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = positions[i];
|
|
|
|
//
|
|
bool canManageState =
|
|
//
|
|
iPosition.provider != XEQMSupportToken
|
|
//
|
|
;
|
|
|
|
//
|
|
// Position Management for X786 ...
|
|
if (canManageState)
|
|
{
|
|
//
|
|
// When Position in Profit, Trail SL ...
|
|
if (profit > 0)
|
|
{
|
|
//
|
|
if (iPosition.profit > 0)
|
|
{
|
|
//
|
|
XTrail trail;
|
|
bool isTrailedBefore = GetSLTrail(
|
|
iPosition.ticket,
|
|
trail //
|
|
);
|
|
|
|
//
|
|
if (!isTrailedBefore)
|
|
{
|
|
//
|
|
// Add First Time ...
|
|
|
|
//
|
|
// Check Position Age ...
|
|
int iPositionAge = iPosition.GetAge(iPosition.period);
|
|
if (iPositionAge < requiredAgeForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Position Profit Distance ...
|
|
double priceDistanceInPoint = PriceToPoint(iPosition.profit / iPosition.volume, iPosition.symbol);
|
|
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
|
|
if (priceDistance < requiredDistanceForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double slDiff = PointToPrice(priceDistance, iPosition.symbol);
|
|
double appliedSL = slDiff;
|
|
|
|
//
|
|
double sl =
|
|
IsLong(iPosition.type)
|
|
? iPosition.sl + appliedSL
|
|
: iPosition.sl - appliedSL;
|
|
|
|
//
|
|
// double sl =
|
|
// IsLong(iPosition.type)
|
|
// ? iPosition.entry + appliedSL
|
|
// : iPosition.entry - appliedSL;
|
|
|
|
//
|
|
bool isInit = trail.Init(
|
|
iPosition.ticket,
|
|
iPosition.type,
|
|
iPosition.profit,
|
|
iPosition.sl,
|
|
sl //
|
|
);
|
|
if (!isInit)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
|
|
//
|
|
// Check Profit ...
|
|
bool isProfitPassed = iPosition.profit > trail.profit;
|
|
if (!isProfitPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Age Distance from Last ...
|
|
int age = iBarShift(
|
|
iPosition.symbol,
|
|
iPosition.period,
|
|
trail.time,
|
|
false //
|
|
);
|
|
if (age < requiredAgeForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double priceDistanceInPoint = PriceToPoint((iPosition.profit - trail.profit) / iPosition.volume, iPosition.symbol);
|
|
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
|
|
if (priceDistance < requiredDistanceForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
|
|
//
|
|
double slDiff = PointToPrice(priceDistance, iPosition.symbol);
|
|
double appliedSL = (slDiff / 2);
|
|
|
|
//
|
|
double selectedValue =
|
|
//
|
|
(riskFreeLevel > 0 &&
|
|
trail.level == riskFreeLevel) ||
|
|
(isLong
|
|
? iPosition.sl < iPosition.entry
|
|
: iPosition.sl > iPosition.entry)
|
|
? iPosition.entry
|
|
: iPosition.sl
|
|
//
|
|
;
|
|
|
|
//
|
|
double sl =
|
|
isLong
|
|
? selectedValue + appliedSL
|
|
: selectedValue - appliedSL;
|
|
|
|
//
|
|
// Update Trail Values ...
|
|
bool isUpdated = trail.Update(
|
|
sl,
|
|
iPosition.profit //
|
|
);
|
|
if (!isUpdated)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (trail.IsValid())
|
|
{
|
|
//
|
|
string comment = trail.GenerateTag();
|
|
bool isModified = mTrader.Modify(
|
|
trail.ticket,
|
|
trail.after,
|
|
iPosition.tp,
|
|
comment //
|
|
);
|
|
if (isModified)
|
|
{
|
|
//
|
|
RemoveTrail(trail.ticket);
|
|
|
|
//
|
|
AddSLTrail(trail);
|
|
|
|
//
|
|
string message = "Position (" + ToString(trail.ticket) + ") Trailed SL on Level: " + ToString(trail.level);
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// When Position in Drawdown Trail TP ...
|
|
if (profit < 0)
|
|
{
|
|
//
|
|
if (iPosition.profit < 0)
|
|
{
|
|
//
|
|
XTrail trail;
|
|
bool isTrailedBefore = GetTPTrail(
|
|
iPosition.ticket,
|
|
trail //
|
|
);
|
|
|
|
//
|
|
if (!isTrailedBefore)
|
|
{
|
|
//
|
|
// Add First Time ...
|
|
|
|
//
|
|
// Check Position Age ...
|
|
int iPositionAge = iPosition.GetAge(iPosition.period);
|
|
if (iPositionAge < requiredAgeForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Position Profit Distance ...
|
|
double priceDistanceInPoint = PriceToPoint(MathAbs(iPosition.profit) / iPosition.volume, iPosition.symbol);
|
|
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
|
|
if (priceDistance < requiredDistanceForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double tpDiff = PointToPrice(priceDistance, iPosition.symbol);
|
|
double appliedTP = tpDiff / 2;
|
|
|
|
//
|
|
double tp =
|
|
IsLong(iPosition.type)
|
|
? iPosition.tp - appliedTP
|
|
: iPosition.tp + appliedTP;
|
|
|
|
//
|
|
bool isInit = trail.Init(
|
|
iPosition.ticket,
|
|
iPosition.type,
|
|
iPosition.profit,
|
|
iPosition.tp,
|
|
tp //
|
|
);
|
|
if (!isInit)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
|
|
//
|
|
if (trail.level >= maxTrailTPs)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Profit ...
|
|
bool isProfitPassed = MathAbs(iPosition.profit) > MathAbs(trail.profit);
|
|
if (!isProfitPassed)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Age Distance from Last ...
|
|
int age = iBarShift(
|
|
iPosition.symbol,
|
|
iPosition.period,
|
|
trail.time,
|
|
false //
|
|
);
|
|
if (age < requiredAgeForTrail)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double priceDistanceInPoint = PriceToPoint((MathAbs(iPosition.profit) - MathAbs(trail.profit)) / iPosition.volume, iPosition.symbol);
|
|
double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol);
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
|
|
//
|
|
double tpDiff = PointToPrice(priceDistance, iPosition.symbol);
|
|
double appliedTP = tpDiff / 2;
|
|
|
|
//
|
|
double tp =
|
|
isLong
|
|
? iPosition.tp - appliedTP
|
|
: iPosition.tp + appliedTP;
|
|
|
|
//
|
|
// Update Trail Values ...
|
|
bool isUpdated = trail.Update(
|
|
tp,
|
|
iPosition.profit //
|
|
);
|
|
if (!isUpdated)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
//
|
|
if (trail.IsValid())
|
|
{
|
|
//
|
|
string comment = trail.GenerateTag();
|
|
bool isModified = mTrader.Modify(
|
|
trail.ticket,
|
|
iPosition.sl,
|
|
trail.after,
|
|
comment //
|
|
);
|
|
if (isModified)
|
|
{
|
|
//
|
|
RemoveTrail(trail.ticket);
|
|
|
|
//
|
|
AddTPTrail(trail);
|
|
|
|
//
|
|
string message = "Position (" + ToString(trail.ticket) + ") Trailed TP on Level: " + ToString(trail.level);
|
|
mAlert.Alert(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Here we Can Filter Signals before Sending them to Execution ...
|
|
// based on Providers and etc ...
|
|
// for Volumes and Benefits ...
|
|
int HandleSignalManagement(XSignal &signals[]) override
|
|
{
|
|
//
|
|
// TODO: Make this Configurable ...
|
|
int minDistanceBetweenTwoSameProviderSignal = 7;
|
|
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
if (ArraySize(signals) <= 0)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Create temp Array for Filtering ...
|
|
XSignal tmps[];
|
|
Copy(
|
|
signals,
|
|
tmps //
|
|
);
|
|
int tmpsCount = ArraySize(tmps);
|
|
|
|
//
|
|
// Cleaning Source ...
|
|
Clean(signals);
|
|
|
|
//
|
|
// Loop through Signals for Management ...
|
|
for (int i = 0; i < tmpsCount; i++)
|
|
{
|
|
//
|
|
XSignal iSignal = tmps[i];
|
|
|
|
//
|
|
// dont Manage NON Signal Provider's signals ...
|
|
string provider = iSignal.provider;
|
|
if (!IsValid(provider))
|
|
{
|
|
//
|
|
AddRef(
|
|
iSignal,
|
|
signals //
|
|
);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition positions[];
|
|
int positionsCount = mTrader.GetPositions(
|
|
positions,
|
|
iSignal.symbol,
|
|
iSignal.provider,
|
|
NULL, // All Periods ...
|
|
iSignal.type //
|
|
);
|
|
XPosition symbolPositions[];
|
|
int symbolPositionsCount = mTrader.GetPositions(
|
|
positions,
|
|
iSignal.symbol,
|
|
NULL, // All Providers ...
|
|
NULL, // All Periods ...
|
|
iSignal.type //
|
|
);
|
|
double profit = CalculatePositionsProfit(positions);
|
|
double symbolProfit = CalculatePositionsProfit(symbolPositions);
|
|
if (positionsCount <= 0)
|
|
{
|
|
//
|
|
// First Signal in a While of Specific Provider ...
|
|
|
|
//
|
|
// iSignal.volume *= 2;
|
|
|
|
//
|
|
AddRef(
|
|
iSignal,
|
|
signals //
|
|
);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
else if (positionsCount < 2)
|
|
{
|
|
//
|
|
// Next Signal in a While of Specific Provider ...
|
|
|
|
//
|
|
// Find Longest Position and Check it's Age ...
|
|
// Compare it by min age for same provider signals ...
|
|
XPosition youngest;
|
|
int youngestAge = GetYoungest(
|
|
youngest,
|
|
positions //
|
|
);
|
|
if (youngestAge == -1 || youngestAge < minDistanceBetweenTwoSameProviderSignal)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iSignal,
|
|
signals //
|
|
);
|
|
|
|
//
|
|
continue;
|
|
}
|
|
// else if (profit > 0 && symbolProfit > 0)
|
|
// {
|
|
// //
|
|
// AddRef(
|
|
// iSignal,
|
|
// signals //
|
|
// );
|
|
// }
|
|
}
|
|
|
|
//
|
|
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_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;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
// Management Tracker/Profit etc ...
|
|
|
|
//
|
|
// Handle Position Changed Event ...
|
|
void OnPositionsChangedHandler(int count) override
|
|
{
|
|
//
|
|
// Synchronize Tracker ...
|
|
SyncPositionTracker();
|
|
}
|
|
|
|
//
|
|
// Called when a Position Going in Profit ...
|
|
void OnPositionGoingInProfit(XPositionTrack &track) override
|
|
{
|
|
//
|
|
string message = "Position (" + ToString(track.ticket) + ", " + track.symbol + ") Dropped in Profit ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
|
|
//
|
|
// Called when a Position Going to Drawdown ...
|
|
void OnPositionGoingToDrawdown(XPositionTrack &track) override
|
|
{
|
|
//
|
|
string message = "Position (" + ToString(track.ticket) + ", " + track.symbol + ") Dropped in Drawdown ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
}
|
|
|
|
//
|
|
// Called when Account Going in Profit ...
|
|
void OnGoingToProfit(XProfitTrack &track) override
|
|
{
|
|
//
|
|
string message = "Account In Profit ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
|
|
//
|
|
RemoveEQMOrders();
|
|
}
|
|
|
|
//
|
|
// Called when Account Going in Drawdown ...
|
|
void OnGoingToDrawdown(XProfitTrack &track) override
|
|
{
|
|
//
|
|
string message = "Account In Drawdown ...";
|
|
|
|
//
|
|
mAlert.Alert(message);
|
|
|
|
//
|
|
// Here i Have to Detect In DrawDown Positions ...
|
|
|
|
//
|
|
RemoveEQMOrders();
|
|
|
|
//
|
|
string mSymbols[];
|
|
XSCPositionHolder *mHolders[];
|
|
int symbolsCount = FilterSymbolBasedPositions(
|
|
mSymbols,
|
|
mHolders //
|
|
);
|
|
|
|
//
|
|
for (int i = 0; i < symbolsCount; i++)
|
|
{
|
|
//
|
|
string iSymbol = mSymbols[i];
|
|
XSCPositionHolder *iHolder = mHolders[i];
|
|
|
|
//
|
|
XPosition iMaxInDP;
|
|
bool hasMaxInDP = iHolder.GetMaxInDrawdown(iMaxInDP);
|
|
if (!hasMaxInDP)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Try to Protect a Position ...
|
|
ProtectPosition(iMaxInDP);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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 //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
};
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
// |