Files
MQL5Data/Classes/x-saherelm.x121.xtrade.handler.class.mq5
T
saherelm 9393c9ecfe Implement Position Protector ...
need to test BE Hedge ...
2024-09-18 21:13:23 +03:30

3273 lines
76 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: X121SCTradeHandler
// Description: provides all Trade Handling requirements ...
//
//
// 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.xtrade.class.mq5"
//
// Definitions ...
//
// an Structure for Holding Positions Data ...
struct X121TradeData
{
//
datetime time; // Issue Time (Open Position)
string symbol; // Trading Symbol
double entry; // Entry Price
double volume; // Volume
string provider; // Signaller
ENUM_TIMEFRAMES period; // Trading Timeframe
ENUM_X_POSITION_TYPES type; // Position Type
//
ulong ticket; // Position Ticket
double swap; // Swap
double profit; // Profit on Close
double commission; // Commission
double maxDrawdown; // Max Position Drawdown
string message; // Close Reason
datetime endTime; // End Time
//
XSignal signal; // Signal Object
int pushers; // Signal Pushers
string conditions; // Signal Conditions
//
int trailStep; // Trail Step
int protectionStep; // Protection Step
double lastProtectedVolume; // Last Protected Volume
bool partiallyClosed; // Partially Closed or not
//
// Constructor ...
void XTradeInfo()
{
Clean();
}
//
// Filling Trade Handler ...
bool Fill(XSignal &_signal)
{
//
bool result = false;
//
Clean();
//
// Check Signal Validation ...
result = _signal.IsValid();
if (!result)
{
return result;
}
//
signal = _signal;
time = _signal.time;
entry = _signal.entry;
symbol = _signal.symbol;
volume = _signal.volume;
period = _signal.period;
pushers = _signal.pushers;
provider = _signal.provider;
conditions = _signal.conditions;
type = ToPositionType(_signal.type);
//
if (_signal.IsExecuted())
{
ticket = _signal.positionId;
}
//
return result;
}
//
// Filling Trade Handler ...
bool Fill(XPosition &_position)
{
//
bool result = false;
//
Clean();
//
// Check Signal Validation ...
result = _position.IsValid();
if (!result)
{
return result;
}
//
time = _position.openAt;
symbol = _position.symbol;
entry = _position.entry;
volume = _position.volume;
provider = _position.provider;
period = _position.period;
type = ToPositionType(_position.type);
//
ticket = _position.ticket;
//
return result;
}
//
// Update Data ...
bool Update(XPosition &_position)
{
//
bool result = false;
//
result = _position.IsValid();
if (!result)
{
return result;
}
//
profit = _position.profit;
swap = _position.swap;
//
if (profit < 0 &&
(maxDrawdown == 0 ||
MathAbs(profit) > MathAbs(maxDrawdown)))
{
maxDrawdown = profit;
}
//
return result;
}
//
// Tools ...
//
// Cleanup ...
void Clean()
{
//
time = NULL;
type = NULL;
symbol = NULL;
period = NULL;
endTime = NULL;
message = NULL;
provider = NULL;
//
swap = 0;
entry = 0;
ticket = 0;
profit = 0;
volume = 0;
commission = 0;
maxDrawdown = 0;
//
trailStep = 0;
protectionStep = 0;
partiallyClosed = false;
lastProtectedVolume = 0;
//
signal.Clean();
}
//
// Validation ...
bool IsValid()
{
//
bool result = false;
//
result =
//
ticket > 0 &&
IsValid(time) &&
IsValid(symbol) &&
IsValid(period)
//
;
//
return result;
}
//
// Retrieve Trade Data Age ...
int GetAge()
{
//
int result = -1;
//
if (!IsValid(symbol) ||
!IsValid(time) ||
!IsValid(endTime) ||
!IsValid(period))
{
return result;
}
//
int startIndex = iBarShift(
symbol,
period,
time,
false //
);
//
int endIndex = iBarShift(
symbol,
period,
endTime,
false //
);
//
result = MathAbs(startIndex - endIndex);
//
return result;
}
//
// Check Own of Trade Data ...
// based on Ticket ...
bool IsOwn(
ulong _ticket //
)
{
//
bool result = false;
//
result = _ticket == ticket;
//
return result;
}
//
// Check Own of Trade Data ...
// based on Symbol/Provider and Period ...
bool IsOwn(
string _symbol,
string _provider,
ENUM_TIMEFRAMES _period,
ENUM_X_POSITION_TYPES _type //
)
{
//
bool result = false;
//
result =
//
IsValid(_symbol) &&
IsValid(_period) &&
IsValid(_provider) &&
type != X_POSITION_TYPE_ALL &&
type != X_POSITION_TYPE_NONE &&
//
type == _type &&
period == _period &&
symbol == _symbol &&
provider == _provider
//
;
//
return result;
}
//
// Data Collector ...
//
// Get Data File Name ...
string GetFileName()
{
//
string result = NULL;
//
if (!IsValid())
{
return result;
}
//
result =
//
symbol + "\\" +
ToString(type) + "\\" +
(profit >= 0 ? "Profit" : "Loss") + "\\" +
ToString(ticket) + "_" +
ToString(period) + "_" +
ToFormatString(time)
//
;
//
return result;
}
//
// Get Signal File Name ...
string GetSignalFileName()
{
//
string result = NULL;
//
result =
//
symbol + "\\" +
ToString(type) + "\\" +
provider + "_" +
ToFormatString(time)
//
;
//
return result;
}
//
// Convert(s) To String Representation(s) ...
string ToString(
bool onlySignals = false //
)
{
//
string result = NULL;
//
int age = GetAge();
//
result =
//
(onlySignals ? "" : ToString("Ticket", ticket)) +
ToString("Symbol", symbol) +
ToString("Period", period) +
ToString("Entry", entry) +
ToString("Provider", provider) +
ToString("Type", ToString(type)) +
ToString("Time", time) +
//
// Attach Trade Info ...
(onlySignals ? "" :
//
"-------------" + "\n" +
ToString("Volume", volume) +
ToString("Profit", profit) +
ToString("Commission", commission) +
ToString("Swap", swap) +
ToString("Max Drawdown", maxDrawdown) +
ToString("End Time", endTime) +
ToString("Age", age) +
ToString("Message", message) +
""
//
) +
//
// Attach Conditions to Signals ...
(!onlySignals ? "" :
//
"-------------" + "\n" +
ToString("Pushers", pushers) +
"Conditions:" + "\n" +
"-------------" + "\n" +
conditions +
""
//
) +
//
""
//
;
//
return result;
}
//
};
//
// Model a Position and it's Protected Positions ...
struct XProtectedPosition
{
//
XPosition main; // Main Position ...
X121TradeData data; // Trade Handler Data ...
//
XPosition supports[]; // Support Positions ...
/**
* Count Supported Positions
*/
int CountSupports()
{
return ArraySize(supports);
}
/**
* Extract All Positions into Specified Array
* of Positions ...
*/
int ExtractPositions(
XPosition &positions[] //
)
{
//
int result = 0;
//
Clean(positions);
//
if (main.IsValid())
{
//
AddRef(
main,
positions //
);
}
//
int supportsCount = CountSupports();
if (IsValidSize(supportsCount))
{
//
Copy(
supports,
positions,
false //
);
}
//
result = ArraySize(positions);
//
return result;
}
};
//
// Implementation ...
//
// a Class For Read and Write Trade Info Data in Files ...
class X121TradeCollector
{
//
// Public ...
public:
//
// Props ...
//
// Constructor(s) ...
void X121TradeCollector(
string _path = NULL // Base Path
)
{
//
mAccount = new XSCAccount();
//
if (IsValid(_path))
{
mPath = _path;
}
else
{
mPath = "X121TradeData" + "\\" + mAccount.GetCompany();
}
}
//
// Deconstructor ...
void ~X121TradeCollector()
{
}
//
bool IsExists(X121TradeData &item)
{
//
bool result = false;
//
int mHandler = GetFileHandlerForRead(item);
result = mHandler != INVALID_HANDLE;
FileClose(mHandler);
//
return result;
}
//
bool Save(X121TradeData &item)
{
//
bool result = false;
//
// Check info is Valid ...
result = item.IsValid();
if (!result)
{
return result;
}
//
string content = item.ToString();
content += "\n" + "-----------" + "\n" + "Signal: " + "\n" + "-----------" + "\n" + item.ToString(true);
//
result = IsValid(content);
if (!result)
{
return result;
}
//
int mHandler = GetFileHandlerForWrite(item);
result = mHandler != INVALID_HANDLE;
if (!result)
{
return result;
}
//
FileWrite(mHandler, content);
FileFlush(mHandler);
FileClose(mHandler);
//
return result;
}
//
bool SaveSignal(X121TradeData &item)
{
//
bool result = false;
//
string content = item.ToString(true);
result = IsValid(content);
if (!result)
{
return result;
}
//
int mHandler = GetSignalFileHandlerForWrite(item);
result = mHandler != INVALID_HANDLE;
if (!result)
{
return result;
}
//
FileWrite(mHandler, content);
FileFlush(mHandler);
FileClose(mHandler);
//
return result;
}
//
// Conditions only save for Loss Signals ...
// this means the profit must be Lower than Zero ...
// ans also message Contains SL ...
bool SaveConditions(X121TradeData &item)
{
//
bool result = false;
//
// Validate Item ...
result =
//
item.profit < 0 &&
Contains("SL", item.message)
//
;
if (!result)
{
return result;
}
//
string content = item.signal.conditions;
result = IsValid(content);
if (!result)
{
return result;
}
//
int mHandler = GetConditionsFileHandlerForWrite(item);
result = mHandler != INVALID_HANDLE;
if (!result)
{
return result;
}
//
FileSeek(mHandler, 0, SEEK_END);
FileWrite(mHandler, content);
FileFlush(mHandler);
FileClose(mHandler);
//
return result;
}
//
// Protected ...
protected:
//
// Private ...
private:
//
// Props ...
//
string mPath; // Base Path ...
//
XSCAccount *mAccount;
//
string GetFilePath(X121TradeData &item)
{
//
string fileName = item.GetFileName();
//
return GetFilePath(fileName);
}
string GetFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
string GetSignalFilePath(X121TradeData &item)
{
//
string fileName = item.GetSignalFileName();
//
return GetSignalFilePath(fileName);
}
string GetSignalFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
string GetConditionsFilePath(X121TradeData &item)
{
//
bool isLong = IsLong(item.type);
//
string fileName =
item.symbol + "\\" +
(isLong ? "Longs" : "Shorts");
//
return GetConditionsFilePath(fileName);
}
string GetConditionsFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
int GetFileHandlerForRead(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_TXT //
);
//
return result;
}
int GetFileHandlerForWrite(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_WRITE | FILE_TXT //
);
//
return result;
}
//
int GetSignalFileHandlerForRead(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetSignalFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_TXT //
);
//
return result;
}
int GetSignalFileHandlerForWrite(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetSignalFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_WRITE | FILE_TXT //
);
//
return result;
}
//
int GetConditionsFileHandlerForRead(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetConditionsFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_TXT //
);
//
return result;
}
int GetConditionsFileHandlerForWrite(X121TradeData &item)
{
//
int result = INVALID_HANDLE;
//
string filePath = GetConditionsFilePath(item);
if (!IsValid(filePath))
{
return result;
}
//
result = FileOpen(
filePath,
FILE_READ | FILE_WRITE | FILE_TXT //
);
//
return result;
}
};
//
// Trade Handler Class ...
class X121SCTradeHandler : public XSCBaseAlert
{
//
// Public ...
public:
//
//
// Constructor(s) ...
void X121SCTradeHandler(XSCTrade *trader)
{
//
mTrader = trader;
mCollector = new X121TradeCollector();
}
//
// Deconstructor ...
void ~X121SCTradeHandler()
{
//
delete mTrader;
delete mCollector;
}
//
// Getter(s) / Setter(s) ...
//
bool SaveSignals()
{
return mSaveSignals;
}
//
void SaveSignals(bool value)
{
mSaveSignals = value;
}
//
bool SaveTrades()
{
return mSaveTrades;
}
//
void SaveTrades(bool value)
{
mSaveTrades = value;
}
//
bool SaveConditions()
{
return mSaveConditions;
}
//
void SaveConditions(bool value)
{
mSaveConditions = value;
}
//
bool AllowLong()
{
return mAllowLong;
}
//
void AllowLong(bool value)
{
mAllowLong = value;
}
//
bool AllowShort()
{
return mAllowShort;
}
//
void AllowShort(bool value)
{
mAllowShort = value;
}
//
int MaxAllowedLongs()
{
return mMaxAllowedLongs;
}
//
void MaxAllowedLongs(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedLongs = value;
}
//
int MaxAllowedShorts()
{
return mMaxAllowedShorts;
}
//
void MaxAllowedShorts(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedShorts = value;
}
//
bool UseMaxAllowedTradesPerSymbol()
{
return mUseMaxAllowedTradesPerSymbol;
}
//
void UseMaxAllowedTradesPerSymbol(bool value)
{
mUseMaxAllowedTradesPerSymbol = value;
}
//
double MaxDrawdownPercentForOpenTrades()
{
return mMaxDrawdownPercentForOpenTrades;
}
//
void MaxDrawdownPercentForOpenTrades(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxDrawdownPercentForOpenTrades = value;
}
//
// Protector Start ...
//
double ProtectorStartDistanceInPoint()
{
return mProtectorStartDistanceInPoint;
}
//
void ProtectorStartDistanceInPoint(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mProtectorStartDistanceInPoint = value;
}
//
int DelayBarBetweenTwoSameSignal()
{
return mDelayBarBetweenTwoSameSignal;
}
//
void DelayBarBetweenTwoSameSignal(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mDelayBarBetweenTwoSameSignal = value;
}
//
// In Profit Position Protecting ...
//
bool AllowTrailStopInProfits()
{
return mAllowTrailStopInProfits;
}
//
void AllowTrailStopInProfits(bool value)
{
mAllowTrailStopInProfits = value;
}
//
bool OnlyTrailUnprotectedPositions()
{
return mOnlyTrailUnprotectedPositions;
}
//
void OnlyTrailUnprotectedPositions(bool value)
{
mOnlyTrailUnprotectedPositions = value;
}
//
double TrailStopStepsInPoint()
{
return mTrailStopStepsInPoint;
}
//
void TrailStopStepsInPoint(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mTrailStopStepsInPoint = value;
}
//
int RmoveTPOnTrailStep()
{
return mRmoveTPOnTrailStep;
}
//
void RmoveTPOnTrailStep(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mRmoveTPOnTrailStep = value;
}
//
double PartialCloseOnSpecificPointOfProfit()
{
return mPartialCloseOnSpecificPointOfProfit;
}
//
void PartialCloseOnSpecificPointOfProfit(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mPartialCloseOnSpecificPointOfProfit = value;
}
//
double PartialCloseVolumeInProfit()
{
return mPartialCloseVolumeInProfit;
}
//
void PartialCloseVolumeInProfit(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mPartialCloseVolumeInProfit = value;
}
//
// In Drawdown Positions Protecting ...
//
bool AllowRecoverInDrawdowns()
{
return mAllowRecoverInDrawdowns;
}
//
void AllowRecoverInDrawdowns(bool value)
{
mAllowRecoverInDrawdowns = value;
}
//
double RecoveryMultiplier()
{
return mRecoveryMultiplier;
}
//
void RecoveryMultiplier(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mRecoveryMultiplier = value;
}
//
double PartialCloseOnSpecificPointOfDrawdown()
{
return mPartialCloseOnSpecificPointOfDrawdown;
}
//
void PartialCloseOnSpecificPointOfDrawdown(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mPartialCloseOnSpecificPointOfDrawdown = value;
}
//
double PartialCloseVolumeInDrawdown()
{
return mPartialCloseVolumeInDrawdown;
}
//
void PartialCloseVolumeInDrawdown(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mPartialCloseVolumeInDrawdown = value;
}
//
// Hedge Props ...
//
bool AllowHedge()
{
return mAllowHedge;
}
//
void AllowHedge(bool value)
{
mAllowHedge = value;
}
//
double HedgeMinVolumeStep()
{
return mHedgeMinVolumeStep;
}
//
// Min: 0.01
// Max 0.1
void HedgeMinVolumeStep(double value)
{
//
if (value < 0.01)
{
value = 0.01;
}
//
if (value > 0.1)
{
value = 0.1;
}
//
mHedgeMinVolumeStep = value;
}
//
int MinimumOpenPositionsForHEHedge()
{
return mMinimumOpenPositionsForHEHedge;
}
//
void MinimumOpenPositionsForHEHedge(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMinimumOpenPositionsForHEHedge = value;
}
//
double HedgeHEMinProfitPerVolumeStep()
{
return mHedgeHEMinProfitPerVolumeStep;
}
//
// Min: 0.01
void HedgeHEMinProfitPerVolumeStep(double value)
{
//
if (value < 0.01)
{
value = 0.01;
}
//
mHedgeHEMinProfitPerVolumeStep = value;
}
//
double HedgeBEMinProfitPerVolumeStep()
{
return mHedgeBEMinProfitPerVolumeStep;
}
//
// Min: 0.01
void HedgeBEMinProfitPerVolumeStep(double value)
{
//
if (value < 0.01)
{
value = 0.01;
}
//
mHedgeBEMinProfitPerVolumeStep = value;
}
//
// Read Only Props ...
//
int MaxSameTimeTrades()
{
return maxSameTimeTrades;
}
//
// Calculate Max Drawdown and it's Percent ...
double GetMaxDrawdown()
{
//
XPosition positions[];
int positionsCount = mTrader.GetPositions(positions);
if (IsValidSize(positionsCount))
{
double mEquity = mTrader.mAccount.GetEquity();
if (mEquity > maxDrawdown)
{
maxDrawdown = mEquity;
}
//
drawdownPercent = (maxDrawdown - mEquity) / (staticBalanceForCalculateDrawdown > 0 ? staticBalanceForCalculateDrawdown : maxDrawdown) * 100;
drawdownPercent = NormalizeDouble(drawdownPercent, 3);
}
else
{
//
maxDrawdown = 0.0;
drawdownPercent = 0.0;
}
//
return drawdownPercent;
}
//
// Position Retrievers ...
/**
* Retrieve Positions as XProtetedPosition structure
* @param positions: Argument 1
* @return ( int )
*/
int GetPositions(
XProtectedPosition &positions[] //
)
{
//
int result = 0;
//
Clean(positions);
//
// Retrieve Positions from Trader Class ...
XPosition allPositions[];
int allPositionsCount = mTrader.GetPositions(
allPositions,
NULL, // All Symbols ...
NULL, // All Providers ...
NULL, // All Periods ...
X_POSITION_TYPE_ALL, // Long and Short ...
false, // Filter By Magic ...
true // Force Clean ...
);
if (!IsValidSize(allPositionsCount))
{
return result;
}
//
// Filter Support Positions ...
XPosition mainPositions[];
XPosition supportPositions[];
for (int i = 0; i < allPositionsCount; i++)
{
//
XPosition iPosition = allPositions[i];
//
// Check Validation ...
if (!iPosition.IsValid())
{
continue;
}
//
// Check Position Support or not ...
bool isSupport = IsSupport(iPosition.comment);
if (isSupport)
{
//
AddRef(
iPosition,
supportPositions //
);
continue;
}
//
AddRef(
iPosition,
mainPositions //
);
}
//
// Check Main Positions Count ...
int mainPositionsCount = ArraySize(mainPositions);
if (!IsValidSize(mainPositionsCount))
{
return result;
}
//
int supportPositionsCount = ArraySize(supportPositions);
//
for (int i = 0; i < mainPositionsCount; i++)
{
//
XPosition iPosition = mainPositions[i];
//
XProtectedPosition iProtected;
iProtected.main = iPosition;
Clean(iProtected.supports);
//
// Loop Through Support Positions To Find iPositions Support ...
if (IsValidSize(supportPositionsCount))
{
//
for (int j = 0; j < supportPositionsCount; j++)
{
//
XPosition jSupport = supportPositions[j];
//
ulong supportParentTicket = ExtractSupportedTicket(jSupport.comment);
bool isSupport = supportParentTicket > 0;
if (isSupport &&
supportParentTicket == iPosition.ticket)
{
//
AddRef(
jSupport,
iProtected.supports //
);
}
//
ulong eqmSupportParentTicket = ExtractEQMSupportedTicket(jSupport.comment);
bool isEQMSupport = eqmSupportParentTicket > 0;
if (isEQMSupport &&
eqmSupportParentTicket == iPosition.ticket)
{
//
AddRef(
jSupport,
iProtected.supports //
);
}
}
}
//
// Findout X121TradeData ...
int iIDX = -1;
bool hasItem = HasItem(
iPosition.ticket,
iIDX //
);
if (hasItem && IsValidIndex(iIDX))
{
iProtected.data = mData[iIDX];
}
//
AddRef(
iProtected,
positions //
);
}
//
Clean(allPositions);
Clean(mainPositions);
Clean(supportPositions);
//
result = ArraySize(positions);
//
return result;
}
//
// Trade Handling Functions ...
//
// First Step of Trade Handling ...
// Since Must Call when a Signal Executed ...
void AddData(
XSignal &signal, // Executed Signal
double commission = 0 // Commission
)
{
//
// Check Signal Valid ...
if (!signal.IsValid())
{
return;
}
//
// Check Signal Support or Recovery ...
bool isSupport = IsSupport(signal.comment);
bool isRecovery = ExtractRecoveredTicket(signal.comment) > 0;
//
X121TradeData item;
bool isFilled = item.Fill(signal);
if (!isFilled)
{
return;
}
//
item.commission = commission;
//
Add(item);
//
SaveSignal(item);
}
//
void Finish(const XDeal &deal)
{
//
int idx = -1;
bool hasItem = HasItem(
deal.positionId,
idx //
);
if (!hasItem)
{
return;
}
//
// Update Item Info ...
mData[idx].swap = deal.swap;
mData[idx].endTime = deal.time;
mData[idx].profit = deal.profit;
//
mData[idx].message =
deal.reason == DEAL_REASON_TP ? "TP" : "SL";
//
Save(idx);
}
//
void Finish(
const ulong ticket,
const XPosition &position,
const string comment //
)
{
//
int idx = -1;
bool hasItem = HasItem(
ticket,
idx //
);
if (!hasItem)
{
return;
}
//
// Update Item Info ...
mData[idx].swap = position.swap;
mData[idx].endTime = TimeCurrent();
mData[idx].profit = position.profit;
//
mData[idx].message = comment;
//
Save(idx);
}
//
// this Method call's by a Timer,
// or in OnTick for Update Positions,
// Data ...
void UpdateData()
{
//
XPosition positions[];
int count = mTrader.GetPositions(positions);
if (!IsValidSize(count))
{
return;
}
//
for (int i = 0; i < count; i++)
{
//
XPosition iPosition = positions[i];
//
int idx = -1;
bool isExists = HasItem(
iPosition.ticket,
idx //
);
if (!isExists)
{
//
// Add New Data ...
//
// Find Executed Signal Deal History ...
XDeal deals[];
double commission = 0;
int dealsCount = mTrader.GetDeals(
deals,
iPosition.symbol,
iPosition.provider,
iPosition.period,
NULL,
DEAL_ENTRY_IN //
);
if (IsValidSize(dealsCount))
{
//
for (int i = 0; i < dealsCount; i++)
{
//
XDeal iDeal = deals[i];
if (iDeal.positionId == iPosition.ticket)
{
//
commission = iDeal.commission;
break;
}
}
}
//
X121TradeData iData;
iData.Fill(
iPosition //
);
//
iData.ticket = iPosition.ticket;
//
Add(iData);
}
else
{
//
// Update Exists ...
mData[idx].Update(iPosition);
}
}
//
count = Count();
if (count > maxSameTimeTrades)
{
maxSameTimeTrades = count;
}
//
GetMaxDrawdown();
}
//
void UpdateSignal(XSignal &signal)
{
//
if (!signal.IsValid())
{
return;
}
//
int count = Count();
if (!IsValidSize(count))
{
return;
}
//
for (int i = 0; i < count; i++)
{
//
bool isOwn = mData[i].IsOwn(
signal.symbol,
signal.provider,
signal.period,
ToPositionType(signal.type) //
);
if (isOwn)
{
mData[i].Fill(signal);
}
}
}
//
void RemoveSignal(XSignal &signal)
{
//
if (!signal.IsValid())
{
return;
}
//
int signalIDX = -1;
ENUM_X_POSITION_TYPES xType = ToPositionType(signal.type);
bool hasSignal = HasItem(
signal.symbol,
signal.provider,
signal.period,
xType,
signalIDX //
);
if (!hasSignal || !IsValidIndex(signalIDX))
{
return;
}
//
// Remove Item From List ...
ArrayRemove(
mData,
signalIDX,
1 //
);
}
//
// Position Execute and Sync Functions ...
/**
* this Method Synchronize
* all Exists Positions and parse them
* and add them into mData Collection if
* their not Exists ...
*/
void Sync()
{
//
// TODO: Implement here ...
}
/**
* Validate Signal For Execution ...
*/
bool CanExecute(XSignal &signal)
{
//
bool result = false;
//
// Validate Signal ...
result = signal.IsValid();
if (!result)
{
return result;
}
//
return result;
}
/**
* Execute Specific Signal using Trade Handler ...
*/
bool ExecuteSignal(
XSignal &signal, // Signal for Execution
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State
bool ignorePolicies = false // Ignore Execution Policies
)
{
//
bool result = false;
//
// Check Signal Validation ...
result = signal.IsValid();
if (!result)
{
return result;
}
//
// Filter Signals if Necessary ...
result = CanExecute(signal);
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
return result;
}
//
// Check Policies ...
if (!ignorePolicies)
{
//
bool isLong = IsLong(signal.type);
//
// Check Allow Trade Type ...
result =
isLong
? mAllowLong
: mAllowShort;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
return result;
}
//
// Check Trades Count ...
if (mMaxAllowedLongs > 0 || mMaxAllowedShorts > 0)
{
//
XPosition longs[];
XPosition shorts[];
//
// Counting Positions ...
if (mUseMaxAllowedTradesPerSymbol)
{
//
mTrader.GetPositions(
longs,
shorts,
signal.symbol //
);
}
else
{
//
mTrader.GetPositions(
longs,
shorts,
NULL // All Symbols ...
);
}
//
int longsCount = ArraySize(longs);
int shortsCount = ArraySize(shorts);
//
result =
//
// Long ...
isLong
? mMaxAllowedLongs <= 0
? true
: longsCount < mMaxAllowedLongs
//
// Short ...
: mMaxAllowedShorts <= 0
? true
: shortsCount < mMaxAllowedShorts
//
;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
return result;
}
//
// Check Position Delays ...
if (result && mDelayBarBetweenTwoSameSignal > 0)
{
//
int youngestAge = 0;
XPosition youngestPosition;
//
// Long Positions when there are Longs ...
if (isLong && longsCount > 0)
{
//
youngestAge = GetYoungest(
youngestPosition,
longs //
);
}
//
// Short Positions when there are Shorts ...
else if (!isLong && shortsCount > 0)
{
//
youngestAge = GetYoungest(
youngestPosition,
shorts //
);
}
//
// Only Check when there is must to check and conditions happens ...
if (youngestAge > 0 && youngestPosition.IsValid())
{
//
result = youngestAge >= mDelayBarBetweenTwoSameSignal;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
return result;
}
}
}
}
//
// Check Drawdown ...
if (mMaxDrawdownPercentForOpenTrades > 0)
{
//
drawdownPercent = GetMaxDrawdown();
//
result =
drawdownPercent <= 0
? true
: drawdownPercent < mMaxDrawdownPercentForOpenTrades;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
return result;
}
}
}
//
// Execute Signal ...
result = mTrader.ExecuteSignal(
signal,
state //
);
if (result)
{
//
double commission = mTrader.GetPositionCommission(signal.positionId);
//
// Add Signal to Trade Handler ...
AddData(
signal,
commission //
);
//
string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") +
" Signal Provided by: " + signal.provider +
(signal.pushers > 1 ? " Pushers: " + ToString(signal.pushers) : "") +
" on: " + signal.symbol +
" in: " + ToString(signal.period) +
" Executed Successfully ...";
Alert(msg);
}
//
return result;
}
//
// Protection Functions ...
/**
* Handle Positiona Protections
*/
void HandleProtection()
{
//
// Retrieve Positions ...
XProtectedPosition pPositions[];
int pPositionsCount = GetPositions(
pPositions //
);
//
// Validate Count ...
if (!IsValidSize(pPositionsCount))
{
return;
}
//
bool isHedged = HandleHedge(pPositions);
if (isHedged)
{
//
// Renew Data ...
Clean(pPositions);
int pPositionsCount = GetPositions(
pPositions //
);
//
// Validate Count ...
if (!IsValidSize(pPositionsCount))
{
return;
}
}
//
// Loop Through Main Positions ...
double requiredDistance = ProtectorStartDistanceInPoint();
if (requiredDistance <= 0)
{
return;
}
//
int protectedPositions = 0;
for (int i = 0; i < pPositionsCount; i++)
{
//
// Check Position is Protectable or not ...
XProtectedPosition iProtected = pPositions[i];
//
XPosition iPosition = iProtected.main;
//
double iDistance = MathAbs(iPosition.GetProfitInPoint());
bool isDistancePassed = iDistance >= requiredDistance;
if (isDistancePassed)
{
//
bool isProtected = HandlePositionProtecting(
iPosition,
iProtected.supports //
);
if (isProtected)
{
protectedPositions++;
}
}
}
//
if (protectedPositions > 0)
{
//
// Renew Data ...
Clean(pPositions);
int pPositionsCount = GetPositions(
pPositions //
);
//
// Validate Count ...
if (!IsValidSize(pPositionsCount))
{
return;
}
}
}
//
// Protected ...
protected:
//
XSCTrade *mTrader; // Instance of Trader Class
X121TradeCollector *mCollector; // Instance of Trade Collector Class
//
X121TradeData mData[]; // Hold Trade Data
//
// Hedge Related Functions ...
/**
* Handle Hedging On Positions ...
*/
bool HandleHedge(XProtectedPosition &positions[])
{
//
bool result = false;
//
// Check Hedging is Enabled Or Not ...
result = AllowHedge();
if (!result)
{
return result;
}
//
// Check Minimum Volume Step is Provided or not ...
double minVolumeStep = HedgeMinVolumeStep();
result = minVolumeStep > 0;
if (!result)
{
return result;
}
//
// Check Positions ...
int positionsCount = ArraySize(positions);
result = IsValidSize(positionsCount);
if (!result)
{
return result;
}
//
// Calculate Required Info ...
double swaps = 0; // Summary of Positions Swaps ...
double profits = 0; // Summary of Positions and Supports Profits ...
double volumes = 0; // Summary of Positions and Supports Volumes ...
int supportsCount = 0; // Number of Protecting Positions ...
double commissions = 0; // Summary of Positions and Supports Commissions ...
XPosition flatPositions[]; // Flat Positions ...
for (int i = 0; i < positionsCount; i++)
{
//
XProtectedPosition iProtected = positions[i];
//
XPosition iPositions[];
int iPositionsCount = iProtected.ExtractPositions(iPositions);
if (IsValidSize(iPositionsCount))
{
//
Copy(
iPositions,
flatPositions,
false //
);
}
//
// Collect Main Positions Data ...
swaps += iProtected.main.swap;
profits += iProtected.main.profit;
volumes += iProtected.main.volume;
commissions += mTrader.GetPositionCommission(iProtected.main.ticket);
//
int iSupportsCount = iProtected.CountSupports();
supportsCount += iSupportsCount;
//
if (IsValidSize(iSupportsCount))
{
//
// Collectiong Supports Positions Data ...
for (int j = 0; j < iSupportsCount; j++)
{
//
XPosition jSupport = iProtected.supports[j];
//
swaps += jSupport.swap;
profits += jSupport.profit;
volumes += jSupport.volume;
//
commissions += mTrader.GetPositionCommission(jSupport.ticket);
}
}
}
//
// First Step is Handle HE Hedge ...
//
int minOpenPositions = MinimumOpenPositionsForHEHedge();
double heMinProfitPerVolumeStep = HedgeHEMinProfitPerVolumeStep();
double beMinProfitPerVolumeStep = HedgeBEMinProfitPerVolumeStep();
//
// Check HE Hedge Conditions ...
bool allowHEHedge = minOpenPositions > 0 && heMinProfitPerVolumeStep > 0;
if (allowHEHedge)
{
//
// Implement HE Hedge ...
//
// Check Main Positions Count and also not Support Positions ...
result = supportsCount == 0 &&
positionsCount >= minOpenPositions;
if (!result)
{
return result;
}
//
// Check Profits ...
result = profits > 0;
if (!result)
{
return result;
}
//
// Calculate Required Profit ...
double volumeSteps = volumes / minVolumeStep;
double minRequiredProfit = (volumeSteps * heMinProfitPerVolumeStep) +
commissions + (-1 * swaps);
//
// Check Profits Satisfied Required Profit's For Hedge or not ...
result = profits >= minRequiredProfit;
if (!result)
{
return result;
}
//
int flatPositionsCount = ArraySize(flatPositions);
result = IsValidSize(flatPositionsCount);
if (!result)
{
return result;
}
//
string comment = "EQM HE Hedge ...";
int closed = mTrader.Close(
flatPositions,
comment //
);
//
result = closed > 0;
if (result)
{
//
string message = "EQM HE Hedge Close (" + ToString(flatPositionsCount) + ") Positions by: " + ToString(profits);
Alert(message);
}
//
return result;
}
//
// Check BE Hedge Conditions ...
bool allowBEHedge = positionsCount >= 1 &&
supportsCount >= 1 &&
beMinProfitPerVolumeStep > 0;
if (allowBEHedge)
{
//
// Implement BE Hedge ...
//
// Check Profits ...
result = profits > 0;
if (!result)
{
return result;
}
//
// Calculate Required Profit ...
double volumeSteps = volumes / minVolumeStep;
double minRequiredProfit = (volumeSteps * beMinProfitPerVolumeStep) +
commissions + (-1 * swaps);
//
// Check Profits Satisfied Required Profit's For Hedge or not ...
result = profits >= minRequiredProfit;
if (!result)
{
return result;
}
//
int flatPositionsCount = ArraySize(flatPositions);
result = IsValidSize(flatPositionsCount);
if (!result)
{
return result;
}
//
string comment = "EQM BE Hedge ...";
int closed = mTrader.Close(
flatPositions,
comment //
);
//
result = closed > 0;
if (result)
{
//
string message = "EQM BE Hedge Close (" + ToString(flatPositionsCount) + ") Positions by: " + ToString(profits);
Alert(message);
}
//
return result;
}
//
return result;
}
/**
* Handle Protecting Position ...
*/
bool HandlePositionProtecting(
XPosition &position, // Main Position
XPosition &supports[] // Supports
)
{
//
bool result = false;
//
// Find Position's Item Data ...
int iDX = -1;
result = HasItem(
position.ticket,
iDX //
);
if (!result)
{
return result;
}
//
// Here we Make Sure this Position has reached Proper distance ...
//
bool isLong = IsLong(position.type);
bool isInProfit = position.profit > 0;
//
int supportsCount = ArraySize(supports);
//
int zIndex = 0;
int cIndex = zIndex + 1;
int pIndex = cIndex + 1;
int ppIndex = pIndex + 1;
//
// Last Bar ...
XOHCL cBar;
result = cBar.Init(
position.symbol,
position.period,
cIndex //
);
if (!result)
{
return result;
}
//
// Previous Bar of Last Bar ...
XOHCL pBar;
result = cBar.GetPreviousBar(pBar);
if (!result)
{
return result;
}
//
// Last Swing Low Bar ...
XOHCL lastSwingLow;
result = cBar.FindLastSwingLow(lastSwingLow);
if (!result)
{
return result;
}
//
// Last Swing High Bar ...
XOHCL lastSwingHigh;
result = cBar.FindLastSwingHigh(lastSwingHigh);
if (!result)
{
return result;
}
//
// Common Conditions For Bullish and Bearish Powers ...
//
// For Trend ...
XOHCL tmpHSW[];
XOHCL tmpLSW[];
//
// Detect Bullish/Bearish Patterns and Trends ...
//
// Bullish ...
bool hasBullishPower = cBar.HasBullishPower();
bool hasBullishPattern = HasBullishPattern(cBar);
bool hasBullishPressure = cBar.HasBullishPressure();
bool isBullishTrend = cBar.HasBullishTrend(
tmpHSW,
tmpLSW,
true //
);
bool isBullishTrend1 = cBar.HasBullishTrend(
tmpHSW,
tmpLSW,
false //
);
bool hasBullishTrend =
//
isBullishTrend
//
||
//
isBullishTrend1
//
;
//
// Bearish ...
bool hasBearishPower = cBar.HasBearishPower();
bool hasBearishPattern = HasBearishPattern(cBar);
bool hasBearishPressure = cBar.HasBearishPressure();
bool isBearishTrend = cBar.HasBearishTrend(
tmpHSW,
tmpLSW,
true //
);
bool isBearishTrend1 = cBar.HasBearishTrend(
tmpHSW,
tmpLSW,
false //
);
bool hasBearishTrend =
//
isBearishTrend
//
||
//
isBearishTrend1
//
;
//
// Define Conditions ...
MqlTick ticks[];
int ticksCount = CopyTicks(
position.symbol,
ticks,
COPY_TICKS_ALL,
0,
5 //
);
//
// TODO: Check Volume if Required ...
//
bool isTicksBullishForLong =
//
GetTickExit(ticks[0], POSITION_TYPE_BUY, position.symbol) > GetTickExit(ticks[1], POSITION_TYPE_BUY, position.symbol) &&
GetTickExit(ticks[1], POSITION_TYPE_BUY, position.symbol) > GetTickExit(ticks[2], POSITION_TYPE_BUY, position.symbol) &&
GetTickExit(ticks[2], POSITION_TYPE_BUY, position.symbol) > GetTickExit(ticks[3], POSITION_TYPE_BUY, position.symbol)
//
;
//
bool isTicksBearishForLong =
//
GetTickExit(ticks[0], POSITION_TYPE_BUY, position.symbol) < GetTickExit(ticks[1], POSITION_TYPE_BUY, position.symbol) &&
GetTickExit(ticks[1], POSITION_TYPE_BUY, position.symbol) < GetTickExit(ticks[2], POSITION_TYPE_BUY, position.symbol) &&
GetTickExit(ticks[2], POSITION_TYPE_BUY, position.symbol) < GetTickExit(ticks[3], POSITION_TYPE_BUY, position.symbol)
//
;
//
bool isTicksBullishForShort =
//
GetTickExit(ticks[0], POSITION_TYPE_SELL, position.symbol) > GetTickExit(ticks[1], POSITION_TYPE_SELL, position.symbol) &&
GetTickExit(ticks[1], POSITION_TYPE_SELL, position.symbol) > GetTickExit(ticks[2], POSITION_TYPE_SELL, position.symbol) &&
GetTickExit(ticks[2], POSITION_TYPE_SELL, position.symbol) > GetTickExit(ticks[3], POSITION_TYPE_SELL, position.symbol)
//
;
//
bool isTicksBearishForShort =
//
GetTickExit(ticks[0], POSITION_TYPE_SELL, position.symbol) < GetTickExit(ticks[1], POSITION_TYPE_SELL, position.symbol) &&
GetTickExit(ticks[1], POSITION_TYPE_SELL, position.symbol) < GetTickExit(ticks[2], POSITION_TYPE_SELL, position.symbol) &&
GetTickExit(ticks[2], POSITION_TYPE_SELL, position.symbol) < GetTickExit(ticks[3], POSITION_TYPE_SELL, position.symbol)
//
;
//
bool hasBullishDecision =
//
cBar.IsBullishDecision()
//
;
//
bool hasBearishDecision =
//
cBar.IsBearishDecision()
//
;
//
bool hasBullishConditions =
//
(
//
hasBullishTrend &&
hasBullishDecision &&
(hasBullishPower ||
hasBullishPattern ||
hasBullishPressure)
//
)
//
;
//
bool hasBearishConditions =
//
(
//
hasBearishTrend &&
hasBearishDecision &&
(hasBearishPower ||
hasBearishPattern ||
hasBearishPressure)
//
)
//
;
//
// In Profit ...
// When a Position running in Profit, we have to look pressures by Same Direction
// of Main Position. for eaxmple if it is long, we look for Bullish Pressures and
// if it is short we look for Bearish Pressures.
// when pressure happens we must Trail position Stop ...
if (isInProfit)
{
//
// Do In Profit Protection Mechanism ...
//
// Trail Stops ...
// Remove Trailed Positions TP ...
bool allowTrailStop = AllowTrailStopInProfits();
double trailStep = TrailStopStepsInPoint();
bool allowOnlyUnprotecteds = OnlyTrailUnprotectedPositions();
bool isProtectionTrailPassed = !allowOnlyUnprotecteds
? true
: supportsCount == 0;
if (allowTrailStop && trailStep > 0 && isProtectionTrailPassed)
{
//
// Temp Vaiables ...
bool canTrailStop = false;
//
if (isLong)
{
//
// Check Conditions ...
canTrailStop = isTicksBullishForLong &&
hasBullishConditions;
}
else
{
//
// Check Conditions ...
canTrailStop = isTicksBearishForShort &&
hasBearishConditions;
}
//
// Do Stop Trailling ...
if (canTrailStop)
{
//
int lastTrailedStep = mData[iDX].trailStep;
//
double entry = position.entry;
double pointValue = position.GetPointsValue();
double profitInPoint = position.GetProfitInPoint();
//
double requiredProfit =
lastTrailedStep == 0
? ProtectorStartDistanceInPoint()
: ProtectorStartDistanceInPoint() + (lastTrailedStep * trailStep);
//
bool isProfitsPassed = profitInPoint > requiredProfit;
if (isProfitsPassed)
{
//
double delta = (requiredProfit * pointValue);
//
double sl =
isLong
? entry + delta
: entry - delta;
//
string comment = "EQM Trail Stop ...";
//
// Try to Modify Position ...
result = mTrader.Modify(
position.ticket,
sl,
position.tp,
comment //
);
if (result)
{
//
mData[iDX].trailStep++;
//
string message = "EQM Trail (" + ToString(position.ticket) + ") Stop ...";
Alert(message);
//
XPosition trailedPosition;
bool hasPosition = mTrader.GetPosition(
position.ticket,
trailedPosition //
);
//
// Check Remove Trailed Positions TP Conditions ...
int removeTPOnTrailStep = RmoveTPOnTrailStep();
if (removeTPOnTrailStep > 0 && mData[iDX].trailStep == removeTPOnTrailStep && trailedPosition.tp != 0)
{
//
string comment = "EQM Removes TP ...";
//
// Remove Position's TP ...
result = mTrader.Modify(
trailedPosition.ticket,
trailedPosition.sl,
0,
comment //
);
if (result)
{
//
string message = "EQM Removes (" + ToString(trailedPosition.ticket) + ") TP ...";
Alert(message);
}
}
}
}
}
}
//
// Check Points of Profit and Do Partial Closing ...
double partialCloseVolume = PartialCloseVolumeInProfit();
double partialCloseProfitPoint = PartialCloseOnSpecificPointOfProfit();
if (partialCloseVolume > 0 && partialCloseProfitPoint > 0)
{
//
double profitPoints = position.GetProfitInPoint();
if (profitPoints >= partialCloseProfitPoint && !mData[iDX].partiallyClosed)
{
//
string comment = "EQM Partial Close ...";
//
// Do Partial Closing ...
result = mTrader.ClosePartial(
position.ticket,
partialCloseVolume,
comment //
);
if (result)
{
//
mData[iDX].partiallyClosed = true;
//
string message = "EQM Partially Closed " + ToString(partialCloseVolume) + " of (" + ToString(position.ticket) + ")";
Alert(message);
}
}
}
}
//
// In Drawdown ...
// When a Position running in Drawdown, we have to look for pressures by Indirection
// of Main Position. for example if it is long, we Look For Bearish Pressures and if
// it is short we Look for Bullish Pressure.
// when indirectional pressure found we must Open a Recovery Position.
// also if Same Direction Pressure Found we try to Recover Same Direction using Grid ...
else
{
//
bool allowRecover = AllowRecoverInDrawdowns();
double volumeMultiplier = RecoveryMultiplier();
if (allowRecover && volumeMultiplier > 0)
{
//
bool canRecover = false;
//
// Temparory Requirement for Recover Signal ...
double sl = 0;
double tp = 0;
double entry = 0;
ENUM_POSITION_TYPE type = NULL;
ENUM_X_ORDER_MODES mode = X_ORDER_MODE_MARKET;
double lastStep = mData[iDX].protectionStep;
double volume = lastStep <= 0
? position.volume * volumeMultiplier
: position.volume * ((lastStep + 1) * volumeMultiplier);
//
// Long Recovery ...
if (isLong)
{
//
// Opposit Direction ...
canRecover =
//
isTicksBearishForShort &&
hasBearishConditions
//
;
if (canRecover)
{
//
sl = position.entry;
tp = position.sl;
type = POSITION_TYPE_SELL;
}
//
// Same Direction ...
if (!canRecover)
{
//
canRecover =
//
isTicksBullishForLong &&
hasBullishConditions
//
;
if (canRecover)
{
//
sl = position.sl;
tp = position.entry;
type = POSITION_TYPE_BUY;
}
}
}
//
// Short Recovery ...
else
{
//
// Opposit Direction ...
canRecover =
//
isTicksBullishForLong &&
hasBullishConditions
//
;
if (canRecover)
{
//
sl = position.entry;
tp = position.sl;
type = POSITION_TYPE_BUY;
}
//
// Same Direction ...
if (!canRecover)
{
//
canRecover =
//
isTicksBearishForShort &&
hasBearishConditions
//
;
if (canRecover)
{
//
sl = position.sl;
tp = position.entry;
type = POSITION_TYPE_SELL;
}
}
}
//
if (canRecover)
{
//
XSignal signal;
entry = GetEntry(
position.symbol,
type //
);
//
// Prepare Signal ...
result = signal.Prepare(
position.symbol,
position.provider,
position.period,
type,
mode,
entry,
volume,
sl,
tp //
);
//
if (result)
{
//
// Generate Comment for Recover and Support ...
string comment = GenerateSupportTag(position.ticket);
//
signal.comment = comment;
//
ENUM_X_SIGNAL_EXECUTION_RESULT state;
result = ExecuteSignal(
signal,
state,
true // Support Signal need to Ignore Policies ...
);
if (result)
{
//
mData[iDX].protectionStep++;
mData[iDX].lastProtectedVolume = volume;
//
string message = "EQM Open " + (IsLong(type) ? "Long" : "Short") + " Recovery Position in Level: " +
ToString(lastStep + 1) +
" for (" + ToString(position.ticket) + ")";
Alert(message);
}
}
}
}
//
// Check Points of Drawdown and Do Partial Closing ...
double partialCloseVolume = PartialCloseVolumeInDrawdown();
double partialCloseDrawdownPoint = PartialCloseOnSpecificPointOfDrawdown();
if (partialCloseVolume > 0 && partialCloseDrawdownPoint > 0)
{
//
double profitPoints = position.GetProfitInPoint();
if (profitPoints < 0 && MathAbs(profitPoints) >= partialCloseDrawdownPoint && !mData[iDX].partiallyClosed)
{
//
string comment = "EQM Partial Close ...";
//
// Do Partial Closing ...
result = mTrader.ClosePartial(
position.ticket,
partialCloseVolume,
comment //
);
if (result)
{
//
mData[iDX].partiallyClosed = true;
//
string message = "EQM Partially Closed " + ToString(partialCloseVolume) + " of (" + ToString(position.ticket) + ")";
Alert(message);
}
}
}
}
//
return result;
}
//
// Private ...
private:
//
// Props ...
//
// Read Only ...
int maxSameTimeTrades; // Max Same Time Trades
double maxDrawdown; // Max Drawdown
double drawdownPercent; // Drawdown Percent
double staticBalanceForCalculateDrawdown; // Static Balance for Max Drawdown Calculation
//
bool mSaveSignals; // Save Signals
bool mSaveTrades; // Save Trades
bool mSaveConditions; // Save SL Conditions
//
bool mAllowLong; // Allow Long/Buy Trade Type
bool mAllowShort; // Allow Short/Sell Trade Type
int mMaxAllowedLongs; // Max Allowe Long/Buy Trades
int mMaxAllowedShorts; // Max Allowe Short/Sell Trades
bool mUseMaxAllowedTradesPerSymbol; // Use Max Allowed Trades Per Symbol
double mMaxDrawdownPercentForOpenTrades; // Max Drawdown Percent for Open Trades
//
int mDelayBarBetweenTwoSameSignal; // Delay Bars Between Two Same Type Signal
double mProtectorStartDistanceInPoint; // Protector Start Distance in Point
//
// In Profit Positions Protecting ...
bool mAllowTrailStopInProfits; // Allow Trail Stops for In Profit Positions
bool mOnlyTrailUnprotectedPositions; // Only Trail UnProtected Positions
double mTrailStopStepsInPoint; // Trail Stop Steps in Point
int mRmoveTPOnTrailStep; // Remove Position TP if Trailed specific times
double mPartialCloseOnSpecificPointOfProfit; // Partial Close Position on Specific Points of Profit
double mPartialCloseVolumeInProfit; // Partial Close Volume
//
// In Drawdown Positions Protecting ...
bool mAllowRecoverInDrawdowns; // Allow Recovery For In Drawdown Positions
double mRecoveryMultiplier; // Recovery Volume Multiplier
double mPartialCloseOnSpecificPointOfDrawdown; // Partial Close Position on Specific Points of Drawdown
double mPartialCloseVolumeInDrawdown; // Partial Close Volume
//
// Hedging Properties ...
bool mAllowHedge; // Allow Protector to Hedge Positions
double mHedgeMinVolumeStep; // Minimum Volume Step For Hedge
int mMinimumOpenPositionsForHEHedge; // Minimum Open Positions For HE Hedge
double mHedgeHEMinProfitPerVolumeStep; // Minimum Required Profit For HE Hedge Per Volume Step
double mHedgeBEMinProfitPerVolumeStep; // Minimum Required Profit For BE Hedge Per Volume Step
//
// Common Functions ...
//
// Count Data ...
int Count()
{
return ArraySize(mData);
}
//
// Add Item ...
bool Add(X121TradeData &item)
{
//
bool result = false;
//
result = item.IsValid();
if (!result)
{
return result;
}
//
int index = -1;
if (item.ticket > 0)
{
//
result = !HasItem(
item.ticket,
index //
);
}
else
{
//
result = !HasItem(
item.symbol,
item.provider,
item.period,
item.type,
index //
);
}
if (!result || IsValidIndex(index))
{
//
result = false;
return result;
}
//
AddRef(
item,
mData //
);
//
return result;
}
//
// Find Item Index ...
bool HasItem(
ulong ticket,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
int count = Count();
result = IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
result = mData[i].IsOwn(ticket);
if (result)
{
//
index = i;
break;
}
}
//
return result;
}
//
// Find Item Index ...
bool HasItem(
string symbol,
string provider,
ENUM_TIMEFRAMES period,
ENUM_X_POSITION_TYPES type,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
// Validate ...
result =
//
IsValid(symbol) &&
IsValid(period) &&
IsValid(provider) &&
type != X_POSITION_TYPE_ALL &&
type != X_POSITION_TYPE_NONE
//
;
if (!result)
{
return result;
}
//
int count = Count();
result = IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
result = mData[i].IsOwn(
symbol,
provider,
period,
type //
);
if (result)
{
//
index = i;
break;
}
}
//
return result;
}
//
void Save(int index)
{
//
int count = Count();
if (!IsValidIndex(index) || index > count - 1)
{
return;
}
//
// Save Trade ...
if (mSaveTrades)
{
mCollector.Save(mData[index]);
}
//
if (mSaveConditions)
{
mCollector.SaveConditions(mData[index]);
}
//
// Remove Item From List ...
ArrayRemove(
mData,
index,
1 //
);
}
//
void SaveSignal(X121TradeData &item)
{
//
if (!mSaveSignals)
{
return;
}
//
// Save Signal ...
mCollector.SaveSignal(item);
}
//
};
//