Files
MQL5Data/Classes/x-saherelm.x121.xtrade.handler.class.mq5
T
2024-09-16 17:13:16 +03:30

2395 lines
50 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
//
// 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;
//
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 mProtectorStartDistanceInPointl
}
//
void ProtectorStartDistanceInPoint(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mProtectorStartDistanceInPoint = 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;
}
//
// TODO:
// Here we can apply Same type or Opposit Type
// Signals behaviour ...
// also check For Market Open ...
//
// Check Market ...
// TODO: Fix this ...
// result = mSymbolSession.CanTrade(signal.symbol);
// 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 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 ...
XPosition iProtected = pPositions[i];
//
XPosition iPosition = iProtected.main;
//
double iSymbolPoint = GetPoints(iPosition.symbol);
double iDistance = MathAbs(iPosition.profit) / iSymbolPoint;
//
bool isDistancePassed = iDistance >= requiredDistance;
bool isProtected = HandlePositionProtecting(iPosition);
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();
//
// 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;
if (allowBEHedge)
{
//
// Implement BE Hedge ...
//
// Check BE Conditions ...
result = supportsCount > 0;
if (!result)
{
return result;
}
//
//
return result;
}
//
return result;
}
/**
* Handle Protecting Position ...
*/
bool HandlePositionProtecting(XPosition &position)
{
//
bool result = false;
//
// Here we Make Sure this Position has ...
//
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
//
double mProtectorStartDistanceInPoint; // Protector Start Distance in Point
//
// 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);
}
//
};
//