Files
MQL5Data/X121SMCEA/Classes/x-121.smc.x-tradehandler.class.mq5
T
2025-03-22 15:32:23 +03:30

4403 lines
98 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCX121SMCTradeHandler
// Description: provides all based classes for use ...
//
//
// 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.x-alert.class.mq5"
#include "../../Classes/x-saherelm.x-bar.analyser.class.mq5"
#include "../../Classes/x-saherelm.x-trade.class.mq5"
#include "../Libraries/x-121.smc.lib.mq5"
//
// Definitions ...
//
// Model a Trade Data ...
struct X121SMCTradeData
{
//
double ask; // Ask Price on Start Time
double bid; // bid Price on Start Time
ulong ticket; // Position Ticket
double swap; // Swap
double profit; // Profit on Close
XSignal signal; // Signal Object
string message; // Message
datetime endTime; // End Time
datetime startTime; // Start Time
double commission; // Commission
string conditions; // Signal Conditions
double maxDrawdown; // Max Position Drawdown
double lastTarget; // Check Last Targetted
double lastTrailedTPLevel; // Hold Last Trailed TP Level
//
// Constructor ...
X121SMCTradeData()
{
Clean();
}
/**
* Initialize Item ...
*
* @param _signal: XSignal instance Reference, Provides Source For Initializaion ...
*
* @return ( bool )
*/
bool Init(XSignal &_signal)
{
//
bool result = false;
//
Clean();
//
result = _signal.IsValid();
if (!result)
{
return result;
}
//
signal = _signal;
//
result = IsValid();
//
return result;
}
//
// Tools ...
/**
* Cleaning Up Model ...
*/
void Clean()
{
//
ask = 0;
bid = 0;
swap = 0;
profit = 0;
ticket = 0;
commission = 0;
maxDrawdown = 0;
lastTrailedTPLevel = 0;
//
endTime = NULL;
message = NULL;
startTime = NULL;
conditions = NULL;
//
lastTarget = 0;
//
signal.Clean();
//
ZeroMemory(this);
}
/**
* Validate Model ...
*
* @return ( bool )
*/
bool IsValid()
{
//
bool result = false;
//
result =
//
signal.IsValid()
//
;
//
return result;
}
/**
* Calculate Model Age ...
*
* @return ( int )
*/
int GetAge()
{
//
int result = -1;
//
if (!IsValid())
{
return result;
}
//
int startIndex = iBarShift(
GetSymbol(),
GetPeriod(),
startTime,
false //
);
//
datetime mEndTime = IsValid(endTime)
? endTime
: TimeCurrent();
int endIndex = iBarShift(
GetSymbol(),
GetPeriod(),
mEndTime,
false //
);
//
result = MathAbs(startIndex - endIndex);
//
return result;
}
//
// Owner Functions ...
/**
* Check a Ticket is Own to Model or not ...
*
* @param _ticket: ULONG, Position Ticket ...
*
* @return ( bool )
*/
bool IsOwn(
ulong _ticket //
)
{
//
bool result = false;
//
result = IsValid() &&
_ticket == ticket;
//
return result;
}
/**
* Check Specified Conditions is Own to Model or not ...
*
* @param _symbol: String, Specified Symbol ...
* @param _provider: String, Specified Provider ...
* @param _period: ENUM_TIMEFRAMES member, Specified Signal Period ...
* @param _type: ENUM_X_POSITION_TYPES member, Specified Signal Type ...
*
* @return ( bool )
*/
bool IsOwnSignalTime(
string _symbol,
string _provider,
ENUM_TIMEFRAMES _period,
ENUM_X_POSITION_TYPES _type,
datetime _time //
)
{
//
bool result = false;
//
result =
//
IsValid(_time) &&
Time() == _time &&
IsValid(_symbol) &&
IsValid(_period) &&
XType() == _type &&
IsValid(_provider) &&
GetPeriod() == _period &&
GetSymbol() == _symbol &&
Provider() == _provider &&
XType() != X_POSITION_TYPE_ALL &&
XType() != X_POSITION_TYPE_NONE
//
;
//
return result;
}
/**
* Check Specified Conditions is Own to Model or not ...
*
* @param _symbol: String, Specified Symbol ...
* @param _provider: String, Specified Provider ...
* @param _period: ENUM_TIMEFRAMES member, Specified Signal Period ...
* @param _type: ENUM_X_POSITION_TYPES member, Specified Signal Type ...
*
* @return ( bool )
*/
bool IsOwnStartTime(
string _symbol,
string _provider,
ENUM_TIMEFRAMES _period,
ENUM_X_POSITION_TYPES _type,
datetime _time //
)
{
//
bool result = false;
//
result =
//
IsValid(_time) &&
IsValid(_symbol) &&
IsValid(_period) &&
XType() == _type &&
IsValid(startTime) &&
startTime == _time &&
IsValid(_provider) &&
GetPeriod() == _period &&
GetSymbol() == _symbol &&
Provider() == _provider &&
XType() != X_POSITION_TYPE_ALL &&
XType() != X_POSITION_TYPE_NONE
//
;
//
return result;
}
/**
* Check Specified Signal is Own to Model or not ...
*
* @param _signal: XSignal instance Reference ...
*
* @return ( bool )
*/
bool IsOwn(XSignal &_signal)
{
//
bool result = false;
//
result =
//
IsValid() &&
_signal.IsValid() &&
IsOwnSignalTime(
_signal.symbol,
_signal.provider,
_signal.period,
ToPositionType(_signal.type),
_signal.time)
//
;
//
return result;
}
/**
* Check Specified Position is Own to Model or not ...
*
* @param _position: XPosition instance Reference ...
*
* @return ( bool )
*/
bool IsOwn(XPosition &_position)
{
//
bool result = false;
//
result =
//
IsValid() &&
_position.IsValid() &&
IsOwnStartTime(
_position.symbol,
_position.provider,
_position.period,
ToPositionType(_position.type),
_position.openAt)
//
;
//
return result;
}
/**
* Check Specified Position is Own to Model or not ...
*
* @param _position: XPosition instance Reference ...
*
* @return ( bool )
*/
bool IsOwnPosition(XPosition &_position)
{
//
bool result = false;
//
double tp = TP();
double sl = SL();
double entry = Entry();
double volume = Volume();
string symbol = GetSymbol();
string provider = Provider();
ENUM_POSITION_TYPE type = Type();
ENUM_TIMEFRAMES period = GetPeriod();
//
double pVolume = _position.volume;
double pSL = NormalizePrice(_position.sl, _position.symbol);
double pTP = NormalizePrice(_position.tp, _position.symbol);
double pEntry = NormalizePrice(_position.entry, _position.symbol);
//
result =
//
IsValid() &&
(sl == pSL ||
tp == pTP) &&
entry == pEntry &&
pVolume == volume &&
_position.IsValid() &&
type == _position.type &&
symbol == _position.symbol &&
period == _position.period &&
provider == _position.provider
//
;
//
if (!result)
{
//
result =
//
IsValid() &&
_position.IsValid() &&
_position.ticket == signal.positionId;
}
//
return result;
}
/**
* Update Model Data by Providing Specified Signal ...
*
* @param _signal: XSignal instance Reference ...
*
* @return ( bool )
*/
bool Update(XSignal &_signal)
{
//
bool result = false;
//
result =
//
IsValid() &&
_signal.IsValid() &&
IsOwn(_signal)
//
;
if (!result)
{
return result;
}
//
signal = _signal;
//
return result;
}
/**
* Update Model Data by Providing Specified Position ...
*
* @param _position: XPosition instance Reference ...
*
* @return ( bool )
*/
bool Update(XPosition &_position)
{
//
bool result = false;
//
result =
//
IsValid() &&
_position.IsValid() &&
IsOwn(_position)
//
;
if (!result)
{
return result;
}
//
swap = _position.swap;
ticket = _position.ticket;
profit = _position.profit;
message = _position.comment;
startTime = _position.openAt;
//
// Set Once ...
if (commission == 0 &&
_position.commission != 0)
{
commission = _position.commission;
}
//
// Set Once ...
if (ask == 0)
{
ask = GetAsk(_position.symbol);
}
//
// Set Once ...
if (bid == 0)
{
bid = GetBid(_position.symbol);
}
//
if (profit < 0 && profit < maxDrawdown)
{
maxDrawdown = profit;
}
//
return result;
}
/**
* Update Model Data by Providing Specified Position ...
*
* @param _position: XPosition instance Reference ...
*
* @return ( bool )
*/
bool UpdatePosition(XPosition &_position)
{
//
bool result = false;
//
result =
//
IsValid() &&
_position.IsValid() &&
IsOwnPosition(_position)
//
;
if (!result)
{
return result;
}
//
swap = _position.swap;
ticket = _position.ticket;
profit = _position.profit;
message = _position.comment;
startTime = _position.openAt;
//
// Set Once ...
if (commission == 0 &&
_position.commission != 0)
{
commission = _position.commission;
}
//
// Set Once ...
if (ask == 0)
{
ask = GetAsk(_position.symbol);
}
//
// Set Once ...
if (bid == 0)
{
bid = GetBid(_position.symbol);
}
//
if (profit < 0 && profit < maxDrawdown)
{
maxDrawdown = profit;
}
//
return result;
}
//
// Tools Extensions ...
/**
* Get Model Symbol ...
*
* @return ( string )
*/
string GetSymbol()
{
return signal.symbol;
}
/**
* Get Model Period ...
*
* @return ( ENUM_TIMEFRAMES )
*/
ENUM_TIMEFRAMES GetPeriod()
{
return signal.period;
}
/**
* Get Model Provider ...
*
* @return ( string )
*/
string Provider()
{
return signal.provider;
}
/**
* Get Model Entry ...
*
* @return ( double )
*/
double Entry()
{
return signal.entry;
}
/**
* Get Model Stop Loss ...
*
* @return ( double )
*/
double SL()
{
return signal.sl;
}
/**
* Get Model Take Profit ...
*
* @return ( double )
*/
double TP()
{
return signal.tp;
}
/**
* Get Model Target ...
*
* @return ( double )
*/
int Targets(double &targets[])
{
//
int result = 0;
//
Copy(
signal.targets,
targets //
);
//
result = ArraySize(targets);
//
return result;
}
/**
* Get Position Spread on Open Time ...
*
* @return ( double )
*/
double Spread()
{
return MathAbs(ask - bid);
}
/**
* Get Model Volume ...
*
* @return ( double )
*/
double Volume()
{
return signal.volume;
}
/**
* Get Model Point Value ...
*
* @return ( double )
*/
double Points()
{
return GetPoints(GetSymbol());
}
/**
* Get Model Type ...
*
* @return ( ENUM_POSITION_TYPE )
*/
ENUM_POSITION_TYPE Type()
{
return signal.type;
}
/**
* Get Model Type ...
*
* @return ( ENUM_X_POSITION_TYPES )
*/
ENUM_X_POSITION_TYPES XType()
{
//
ENUM_X_POSITION_TYPES result = X_POSITION_TYPE_NONE;
//
if (!IsValid())
{
return result;
}
//
result =
IsLong(Type())
? X_POSITION_TYPE_LONG
: X_POSITION_TYPE_SHORT;
//
return result;
}
/**
* Get Model Signal Mode ...
*
* @return ( ENUM_X_ORDER_MODES )
*/
ENUM_X_ORDER_MODES Mode()
{
return signal.mode;
}
/**
* Get Model Signal Time ...
*
* @return ( datetime )
*/
datetime Time()
{
return signal.time;
}
//
// Additional Helpers Extensions ...
/**
* Get Signal Full TP Level ...
*
* @return ( double )
*/
double FullTPLevel()
{
return signal.fullTPLevel;
}
/**
* Check Can Partial Close Position or not ...
*
* @return ( bool )
*/
bool CanPartialClose()
{
return signal.CanPartialClose();
}
/**
* Retrieve Partial Close Volume Multiplier ...
*
* @return ( double )
*/
double PartialCloseMultiplier()
{
return signal.partialCloseMultiplier;
}
/**
* Get Partial Close On TP Level ...
*
* @return ( double )
*/
double PartialCloseOnTPLevel()
{
return signal.partialCloseOnTPLevel;
}
/**
* Check Can Risk Free Position on Break Even Point or not ...
*
* @return ( bool )
*/
bool CanRiskFreeOnBreakEvenPoint()
{
return signal.CanRiskFreeOnBreakEvenPoint();
}
/**
* Get Minimum Required Profit after Break Even Point for Risk Free ...
*
* @return ( double )
*/
double TPLevelForBreakEven()
{
return signal.tpLevelForBreakEven;
}
/**
* Calculate Break Even Point ...
*
* @return ( double )
*/
double CalculateBreakEvenPoint()
{
//
double result = 0;
//
bool isValid =
//
ask > 0 &&
bid > 0 &&
IsValid() &&
ticket > 0 &&
signal.IsValid()
//
;
if (!isValid)
{
return result;
}
//
// Calculate Spread ...
double spread = Spread();
if (spread <= 0)
{
return result;
}
//
// Calculate BEP ...
result =
//
spread + (-1 * swap) + MathAbs(commission)
//
;
//
return result;
}
/**
* Check Can Trail Position Stop Loss or not ...
*
* @return ( bool )
*/
bool CanTrailSL()
{
return signal.CanTrailSL();
}
/**
* Get Start Position SL Trailling on TP Level ...
*
* @return ( double )
*/
double TrailSLStartOnReachTPLevel()
{
return signal.trailSLStartOnReachTPLevel;
}
//
// Data Collector Extensions ...
/**
* Get Data Collection File Name ...
*
* @return ( string )
*/
string GetFileName()
{
//
string result = NULL;
//
if (!IsValid())
{
return result;
}
//
result =
//
GetSymbol() + "\\" +
ToString(XType()) + "\\" +
(profit >= 0 ? "Profit" : "Loss") + "\\" +
ToString(ticket) + "_" +
ToString(GetPeriod()) + "_" +
ToFormatString(startTime)
//
;
//
return result;
}
/**
* Get Signal Collectiong File Name ...
*
* @return ( string )
*/
string GetSignalFileName()
{
//
string result = NULL;
//
result =
//
GetSymbol() + "\\" +
ToString(XType()) + "\\" +
Provider() + "_" +
ToFormatString(startTime)
//
;
//
return result;
}
/**
* Converts Model to String Representation ...
*
* @param onlySignals: Boolean, Specified Represent Only Signal or not ...
*
* @return ( string )
*/
string ToString(
bool onlySignals = false //
)
{
//
string result = NULL;
//
int age = GetAge();
//
result =
//
(onlySignals ? "" : ToString("Ticket", ticket)) +
ToString("Symbol", GetSymbol()) +
ToString("Period", GetPeriod()) +
ToString("Entry", Entry()) +
ToString("Provider", Provider()) +
ToString("Type", ToString(XType())) +
ToString("Time", startTime) +
//
// 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", signal.pushers) +
"Conditions:" + "\n" +
"-------------" + "\n" +
conditions +
""
//
) +
//
""
//
;
//
return result;
}
//
// End Of X121SMCTradeData Model ...
};
//
// Model Specified Symbol's Positions States ...
struct X121SMCSymbolPositionInfo
{
//
string symbol;
//
datetime enableAt;
//
int longSLs;
int longTPs;
//
int shortSLs;
int shortTPs;
//
int countedSLs;
//
// Constructor ...
X121SMCSymbolPositionInfo()
{
Clean();
}
//
// Tools ...
/**
* Cleanup ...
*/
void Clean()
{
//
longSLs = 0;
longTPs = 0;
//
shortSLs = 0;
shortTPs = 0;
//
countedSLs = 0;
//
enableAt = NULL;
//
ZeroMemory(this);
}
/**
* Check is own Symbol Info ...
*
* @param _symbol: String ...
*
* @return ( bool )
*/
bool IsOwn(string _symbol)
{
//
bool result = false;
//
result =
//
IsValid(symbol) &&
_symbol == symbol
//
;
//
return result;
}
/**
* Check Symbol Trading is Enable or not ...
*
* @param time: DateTime ...
*
* @return ( bool )
*/
bool IsEnable(datetime time = NULL)
{
//
bool result = false;
//
time = NormalizeTime(time);
//
bool isEnableAtValid = IsValid(enableAt);
//
result =
!isEnableAtValid
? true
: time > enableAt;
if (result &&
isEnableAtValid)
{
Reset();
}
//
return result;
}
/**
* Pause Symbol Trading ...
*
* @param seconds: Integer ...
*/
void Pause(int seconds)
{
//
if (seconds <= 0)
{
return;
}
//
datetime _enableAt = ((datetime)((int)TimeCurrent() + seconds));
enableAt = _enableAt;
}
/**
* Handle Stop Loss ...
*
* @param _type: ENUM_POSITION_TYPE member ...
*/
void HandleSL(ENUM_POSITION_TYPE _type)
{
//
bool isLong = IsLong(_type);
if (isLong)
{
longSLs++;
}
else
{
shortSLs++;
}
//
countedSLs++;
}
/**
* Handle Take Profit ...
*
* @param _type: ENUM_POSITION_TYPE member ...
*/
void HandleTP(ENUM_POSITION_TYPE _type)
{
//
bool isLong = IsLong(_type);
if (isLong)
{
longTPs++;
}
else
{
shortTPs++;
}
//
countedSLs--;
if (countedSLs < 0)
{
countedSLs = 0;
}
}
/**
* Reset Counter ...
*/
void Reset()
{
//
countedSLs = 0;
enableAt = NULL;
}
//
// End of X121SMCSymbolPositionInfo Model ...
};
//
// Implementations ...
//
// a Class For Read and Write Trade Info Data in Files ...
class X121SMCTradeCollector
{
//
// Public ...
public:
//
// Props ...
//
// Constructor(s) ...
X121SMCTradeCollector(
string _path = NULL // Base Path
)
{
//
mAccount = new XCAccount();
//
if (IsValid(_path))
{
mPath = _path;
}
else
{
mPath = "X121SMCTradeData" + "\\" + mAccount.GetCompany();
}
}
//
// Deconstructor ...
~X121SMCTradeCollector()
{
}
/**
* Check Specified Model is Exists or not ...
*
* @param item: X121SMCTradeData instance Reference, Provides Source Model ...
*
* @return ( bool )
*/
bool IsExists(X121SMCTradeData &item)
{
//
bool result = false;
//
int mHandler = GetFileHandlerForRead(item);
result = mHandler != INVALID_HANDLE;
FileClose(mHandler);
//
return result;
}
/**
* Save Specified Model as Data ...
*
* @param item: X121SMCTradeData instance Reference, Provides Source Model ...
*
* @return ( bool )
*/
bool Save(X121SMCTradeData &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;
}
/**
* Save Specified Model as Signal ...
*
* @param item: X121SMCTradeData instance Reference, Provides Source Model ...
*
* @return ( bool )
*/
bool SaveSignal(X121SMCTradeData &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;
}
/**
* Save Specified Model Conditions ...
* Conditions only save for Loss Signals ...
* this means the profit must be Lower than Zero ...
* ans also message Contains SL ...
*
* @param item: X121SMCTradeData instance Reference, Provides Source Model ...
*
* @return ( bool )
*/
bool SaveConditions(X121SMCTradeData &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 ...
//
XCAccount *mAccount;
//
string GetFilePath(X121SMCTradeData &item)
{
//
string fileName = item.GetFileName();
//
return GetFilePath(fileName);
}
string GetFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
string GetSignalFilePath(X121SMCTradeData &item)
{
//
string fileName = item.GetSignalFileName();
//
return GetSignalFilePath(fileName);
}
string GetSignalFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
string GetConditionsFilePath(X121SMCTradeData &item)
{
//
bool isLong = IsLong(item.Type());
//
string fileName =
item.GetSymbol() + "\\" +
(isLong ? "Longs" : "Shorts");
//
return GetConditionsFilePath(fileName);
}
string GetConditionsFilePath(string fileName)
{
//
string result = "";
//
result =
//
mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log"
//
;
//
return result;
}
//
int GetFileHandlerForRead(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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(X121SMCTradeData &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;
}
//
// End of X121SMCTradeCollector Class ...
};
//
// Trade Handler Class ...
// a Class For Handling All Trade Requirements ...
class XC121SMCTradeHandler : public XCBaseAlert
{
//
// Public ...
public:
//
//
// Constructor(s) ...
XC121SMCTradeHandler(XCTrade *trader)
{
//
mTrader = trader;
mAdditionalVolume = 0;
mCollector = new X121SMCTradeCollector();
}
//
// Deconstructor ...
~XC121SMCTradeHandler()
{
//
Clean(mData);
Clean(mSymbolInfos);
Clean(mLastExecutedSignals);
//
delete mTrader;
delete mCollector;
}
//
// ReadOnly Props ...
/**
* Get Max Same Time Trades ...
*
* @return ( double )
*/
double GetMaxSameTimeTrades()
{
return maxSameTimeTrades;
}
/**
* Get Max Drawdown Happens ...
*
* @return ( double )
*/
double GetMaxDrawdown()
{
return maxDrawdown;
}
/**
* Get Currently Drawdown of Account ...
*
* @return ( double )
*/
double GetCurrentDrawdown()
{
return currentDrawdown;
}
/**
* Get Account Current Drawdown Percent ...
*
* @return ( double )
*/
double GetDrawdownPercent()
{
return drawdownPercent;
}
//
// Properties Getter(s) / Setter(s) ...
//
// Saving Properties ...
/**
* Get Save Signals State ...
*
* @return ( bool )
*/
bool SaveSignals()
{
return mSaveSignals;
}
/**
* Set Save Signals State ...
*
* @param value: Boolean ...
*/
void SaveSignals(bool value)
{
mSaveSignals = value;
}
/**
* Get Save Trades State ...
*
* @return ( bool )
*/
bool SaveTrades()
{
return mSaveTrades;
}
/**
* Set Save Trades State ...
*
* @param value: Boolean ...
*/
void SaveTrades(bool value)
{
mSaveTrades = value;
}
/**
* Get Save Conditions State ...
*
* @return ( bool )
*/
bool SaveConditions()
{
return mSaveConditions;
}
/**
* Set Save Conditions State ...
*
* @param value: Boolean ...
*/
void SaveConditions(bool value)
{
mSaveConditions = value;
}
//
// Signalling Props ...
/**
* Get Allow Long Signals State ...
*
* @return ( bool )
*/
bool AllowLong()
{
return mAllowLong;
}
/**
* Set Allow Long Signals State ...
*
* @param value: Boolean ...
*/
void AllowLong(bool value)
{
mAllowLong = value;
}
/**
* Get Allow Short Signals State ...
*
* @return ( bool )
*/
bool AllowShort()
{
return mAllowShort;
}
/**
* Set Allow Short Signals State ...
*
* @param value: Boolean ...
*/
void AllowShort(bool value)
{
mAllowShort = value;
}
/**
* Get Max Allowed Long Signals ...
* 0 => Unlimited ...
*
* @return ( int )
*/
int MaxAllowedLongs()
{
return mMaxAllowedLongs;
}
/**
* Set Max Allowed Long Signals ...
*
* @param value: Integer ...
* 0 => Unlimited ...
*/
void MaxAllowedLongs(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedLongs = value;
}
/**
* Get Max Allowed Short Signals ...
* 0 => Unlimited ...
*
* @return ( int )
*/
int MaxAllowedShorts()
{
return mMaxAllowedShorts;
}
/**
* Set Max Allowed Short Signals ...
*
* @param value: Integer ...
* 0 => Unlimited ...
*/
void MaxAllowedShorts(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedShorts = value;
}
/**
* Get Use Max Allowed Signal Types Per Symbol State ...
*
* @return ( bool )
*/
bool UseMaxAllowedSignalsPerSymbol()
{
return mUseMaxAllowedSignalsPerSymbol;
}
/**
* Set Use Max Allowed Signal Types Per Symbol State ...
*
* @param value: Boolean ...
*/
void UseMaxAllowedSignalsPerSymbol(bool value)
{
mUseMaxAllowedSignalsPerSymbol = false;
}
/**
* Get Last Position Profit In Points for Accept new Signal ...
* 0 => Accept All ...
*
* @return ( double )
*/
double LastPositionProfitForAcceptNextInPoint()
{
return mLastPositionProfitForAcceptNextInPoint;
}
/**
* Set Last Position Profit In Points for Accept new Signal ...
*
* @param value: Double ...
* 0 => Accept All ...
*/
void LastPositionProfitForAcceptNextInPoint(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mLastPositionProfitForAcceptNextInPoint = value;
}
/**
* Get Delay between two Signals in Bars ...
* 0 => Accept All ...
*
* @return ( int )
*/
int DelaysBetweenTwoSignalsInBar()
{
return mDelaysBetweenTwoSignalsInBar;
}
/**
* Set Delay between two Signals in Bars ...
*
* @param value: Integer ...
* 0 => Accept All ...
*/
void DelaysBetweenTwoSignalsInBar(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mDelaysBetweenTwoSignalsInBar = value;
}
/**
* Get Max Allowed Spread for Signalling ...
* 0 => Accept All ...
*
* @return ( double )
*/
double MaxAllowedSpread()
{
return mMaxAllowedSpread;
}
/**
* Set Max Allowed Spread for Signalling ...
*
* @param value: Double ...
* 0 => Accept All ...
*/
void MaxAllowedSpread(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedSpread = value;
}
/**
* Get Additional Signal Volume ...
*
* @return ( double )
*/
double AdditionalVolume()
{
return mAdditionalVolume;
}
/**
* Set Additional Signal Volume ...
*
* @param value: Double ...
* min = 0.01
*/
void AdditionalVolume(double value)
{
//
if (value < 0)
{
value = 0;
}
//
if (value > 0 && value < 0.01)
{
value = 0.01;
}
//
mAdditionalVolume = value;
}
//
// Risk Management Props ...
/**
* Get Max Allowed Drawdown Percent for Open Trades ...
* 0 => Ignore ...
* min => 0 ...
* max => 100 ...
*
* @return ( double )
*/
double MaxAllowedDrawdownToOpenTrades()
{
return mMaxAllowedDrawdownToOpenTrades;
}
/**
* Set Max Allowed Drawdown Percent for Open Trades ...
*
* @param value: Double ...
* 0 => Ignore ...
* min => 0 ...
* max => 100 ...
*/
void MaxAllowedDrawdownToOpenTrades(double value)
{
//
if (value < 0)
{
value = 0;
}
//
if (value > 50)
{
value = 50;
}
//
mMaxAllowedDrawdownToOpenTrades = value;
}
/**
* Get Max Allowed SL for Pause Signalling Per Symbol ...
* 0 => Unlimited ...
*
* @return ( int )
*/
int MaxAllowedSLToPauseSignallingPerSymbol()
{
return mMaxAllowedSLToPauseSignallingPerSymbol;
}
/**
* Set Max Allowed SL for Pause Signalling Per Symbol ...
*
* @param value: Integer ...
* 0 => Unlimited ...
*/
void MaxAllowedSLToPauseSignallingPerSymbol(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMaxAllowedSLToPauseSignallingPerSymbol = value;
}
/**
* Get Pause Signalling Per Symbol afte Max Allowed SL Reached In Seconds ...
* 0 => Ignore ...
*
* @return ( int )
*/
int PauseSignallingAfterReachesMaxAllowedSLInSecconds()
{
return mPauseSignallingAfterReachesMaxAllowedSLInSecconds;
}
/**
* Set Pause Signalling Per Symbol afte Max Allowed SL Reached In Seconds ...
*
* @param value: Integer ...
* 0 => Ignore ...
*/
void PauseSignallingAfterReachesMaxAllowedSLInSecconds(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mPauseSignallingAfterReachesMaxAllowedSLInSecconds = value;
}
//
// Protection Props ...
/**
* Get Allow Hedge Positions State ...
*
* @return ( bool )
*/
bool AllowHedging()
{
return mAllowHedging;
}
/**
* Set Allow Hedge Positions State ...
*
* @param value: Boolean ...
*/
void AllowHedging(bool value)
{
mAllowHedging = value;
}
/**
* Get Minimum Open Positions for Hedging ...
* 0 => Ignore ...
*
* @return ( int )
*/
int MinOpenTradesFroHedging()
{
return mMinOpenTradesFroHedging;
}
/**
* Set Minimum Open Positions for Hedging ...
*
* @param value: Integer ...
* 0 => Ignore ...
*/
void MinOpenTradesFroHedging(int value)
{
//
if (value < 0)
{
value = 0;
}
//
mMinOpenTradesFroHedging = value;
}
/**
* Get Minimum Volume Step for Hedging ...
* 0 => Ignore ...
*
* @return ( double )
*/
double HedgingMinVolumeStep()
{
return mHedgingMinVolumeStep;
}
/**
* Set Minimum Volume Step for Hedging ...
*
* @param value: Double ...
* 0 => Ignore ...
*/
void HedgingMinVolumeStep(double value)
{
//
if (value < 0)
{
value = 0;
}
//
if (value > 0 && value < 0.01)
{
value = 0.01;
}
//
if (value > 0 && value > 0.1)
{
value = 0.1;
}
//
mHedgingMinVolumeStep = value;
}
/**
* Get Minimum Required Profit Per Volume Step for Hedging ...
* 0 => Ignore ...
*
* @return ( double )
*/
double HedgeingMinRequiredProfitPerVolumeStep()
{
return mHedgeingMinRequiredProfitPerVolumeStep;
}
/**
* Set Minimum Required Profit Per Volume Step for Hedging ...
*
* @param value: Double ...
* 0 => Ignore ...
*/
void HedgeingMinRequiredProfitPerVolumeStep(double value)
{
//
if (value < 0)
{
value = 0;
}
//
mHedgeingMinRequiredProfitPerVolumeStep = value;
}
//
// Actions ...
//
// Data Collection Actions ...
/**
* Add Executed Signal Data Model to Trade Datas ...
*
* @param signal: XSignal instance Reference, Issued Signal ...
*/
void Add(XSignal &signal)
{
//
// Check Signal is Valid and also not Exists
// in Datas ...
int idx = -1;
bool hasItem = HasItem(
signal,
idx //
);
if (hasItem)
{
return;
}
//
// Initialize Data Item and Add it to Collection of Datas
// if Everythings OK ...
X121SMCTradeData data;
bool isInited = data.Init(signal);
if (isInited)
{
//
Add(data);
SaveSignal(data);
}
}
/**
* Update a Registered Data Model by Providing Specified Signal ...
* usually used When a signal Modified after Executed for Conditionally Signals ...
*
* @param signal: XSignal instance Reference, Issued Signal ...
*/
void Update(XSignal &signal)
{
//
// Validate Signal ...
if (!signal.IsValid())
{
return;
}
//
// Check Signal Registered Before ...
int idx = -1;
bool hasItem = HasItem(
signal,
idx //
);
bool isUpdated = false;
if (!hasItem)
{
//
// Add New Item if not Exists ...
Add(signal);
isUpdated = true;
}
else
{
//
// Update Data Model in Datas Collection ...
isUpdated = mData[idx].Update(signal);
}
//
if (isUpdated)
{
SaveSignal(mData[idx]);
}
}
/**
* Update a Registered Data Model by Providing Specified Position ...
* used for Updating Positions Data or When a Conditional Order Executed as Position ...
*
* @param position: XPosition instance Referece ...
*/
void Update(XPosition &position)
{
//
// Validate Position ...
if (!position.IsValid())
{
return;
}
//
// Check Position Registered Before ...
int idx = -1;
bool hasItem = HasItem(
position,
idx //
);
if (!hasItem)
{
return;
}
//
// Update Data Model in Datas Collection ...
mData[idx].Update(position);
}
/**
* Remove Specified Registered Signal from Data Collection ...
*
* @param signal: XSignal instance Reference ...
*/
void Remove(XSignal &signal)
{
//
// Validate Signal ...
if (!signal.IsValid())
{
return;
}
//
// Check Signal is Registered or not ...
int idx = -1;
bool hasItem = HasItem(
signal,
idx //
);
if (!hasItem)
{
return;
}
//
// Remove Data From Collection ...
ArrayRemove(
mData,
idx,
1 //
);
}
/**
* Handle Position Execution Finished by Providing a Deal ...
* usually used when a Position TP/SL Triggered ...
*
* @param deal: XDeal instance Reference ...
*/
void Finish(const XDeal &deal)
{
//
// Validate Deal ...
// Check Specified Data Registered or not ...
int idx = -1;
bool hasItem = HasItem(
deal.positionId,
idx //
);
if (!hasItem)
{
return;
}
//
// Update Data Info ...
mData[idx].swap = deal.swap;
mData[idx].endTime = deal.time;
mData[idx].profit = deal.profit;
//
// Check Deal Reason ...
bool isTP = deal.reason == DEAL_REASON_TP ||
(deal.reason == DEAL_REASON_SL && deal.profit > 0);
bool isSL = deal.reason == DEAL_REASON_SL && deal.profit < 0;
//
// Prepare Specified Message Based on Deal Reason ...
mData[idx].message =
isTP
? "TP"
: isSL
? "SL"
: "";
//
// Handle Symbol Positions TP or SL Actions ...
if (isTP || isSL)
{
//
// Check Symbol Position Info Model Exists ...
int symbolIDX = -1;
bool hasInfo = HasSymbol(
mData[idx].GetSymbol(),
symbolIDX //
);
//
// Try to Update or Add Symbol Position Info ...
X121SMCSymbolPositionInfo info;
if (!hasInfo)
{
//
// Add New One ...
info.symbol = mData[idx].GetSymbol();
}
else
{
//
// Update Exists ...
info = mSymbolInfos[symbolIDX];
//
// Remove Exists ...
ArrayRemove(
mSymbolInfos,
symbolIDX,
1 //
);
}
//
ENUM_POSITION_TYPE xType = IsLong(mData[idx].Type())
? POSITION_TYPE_BUY
: POSITION_TYPE_SELL;
//
// Call Propper TP / SL Handlers on Symbol Position Info Model ...
if (isTP)
{
info.HandleTP(xType);
}
else if (isSL)
{
info.HandleSL(xType);
}
//
// Add Model to Symbol Positions Collection ...
AddRef(
info,
mSymbolInfos //
);
//
// Try to Check Symbol Positions Info Collection for Pause Specified Symbol ...
HandlePauseSymbol(mData[idx].GetSymbol());
}
//
// Save Collector Calss Data Model ...
Save(idx);
}
/**
* Handle Position Execution Finished by Providing some Data ...
* usually used when a Position Force Closed Triggered ...
*
* @param ticket: ULong, Position ticket ...
* @param position: XPosition instance Reference ...
* @param comment: String, Force Closing Comment ...
*/
void Finish(
const ulong ticket,
const XPosition &position,
const string comment //
)
{
//
// Validate and Check Data Registered or not ...
int idx = -1;
bool hasItem = HasItem(
ticket,
idx //
);
if (!hasItem)
{
return;
}
//
// Update Data Info ...
mData[idx].swap = position.swap;
mData[idx].endTime = TimeCurrent();
mData[idx].profit = position.profit;
//
mData[idx].message = comment;
//
// Handle Force Closes as TP/SL for Updating
// Symbol Info Positions ...
bool handleForceClose = true;
if (handleForceClose)
{
//
// Simulate TP/SL based on Closing Profit ...
bool isTP = position.profit > 0;
bool isSL = position.profit <= 0;
//
if (isTP || isSL)
{
//
// Check Symbol Position Info Model Exists ...
int symbolIDX = -1;
bool hasInfo = HasSymbol(
mData[idx].GetSymbol(),
symbolIDX //
);
//
// Try to Update or Add Symbol Position Info ...
X121SMCSymbolPositionInfo info;
if (!hasInfo)
{
//
// Add New One ...
info.symbol = mData[idx].GetSymbol();
}
else
{
//
// Update Exists ...
info = mSymbolInfos[symbolIDX];
//
// Remove Exists ...
ArrayRemove(
mSymbolInfos,
symbolIDX,
1 //
);
}
//
ENUM_POSITION_TYPE xType = IsLong(mData[idx].Type())
? POSITION_TYPE_BUY
: POSITION_TYPE_SELL;
//
// Call Propper TP / SL Handlers on Symbol Position Info Model ...
if (isTP)
{
info.HandleTP(xType);
}
else if (isSL)
{
info.HandleSL(xType);
}
//
// Add Model to Symbol Positions Collection ...
AddRef(
info,
mSymbolInfos //
);
//
// Try to Check Symbol Positions Info Collection for Pause Specified Symbol ...
HandlePauseSymbol(mData[idx].GetSymbol());
}
//
// Save Collector Calss Data Model ...
Save(idx);
}
}
/**
* Update New Open Positions Data on Collection ...
*/
void HandleNewPosition()
{
//
// Retrieve Last Open Position ...
ulong lastOpenPositionTicket = mTrader.GetLastOpenPositionTicket();
if (lastOpenPositionTicket == 0)
{
return;
}
//
XPosition position;
bool hasPosition = mTrader.GetPosition(
lastOpenPositionTicket,
position //
);
if (!hasPosition)
{
return;
}
//
// Calculate Position Commission ...
double commission = mTrader.GetPositionCommission(position.ticket);
position.commission = commission;
//
// Apply Updates on Data Collection ...
int idx = -1;
bool hasItem = HasItemByPosition(
position,
idx //
);
if (!hasItem)
{
return;
}
//
mData[idx].UpdatePosition(position);
}
//
// Symbol Position Info Actions ...
/**
* Retrieve Specified Symbols Info ...
*
* @param symbol: String ...
* @param info: X121SMCSymbolPositionInfo instance ...
*
* @return ( bool )
*/
bool GetSymbolInfo(
string symbol,
X121SMCSymbolPositionInfo &info //
)
{
//
bool result = false;
//
info.Clean();
//
int symbolIDX = -1;
result = HasSymbol(
symbol,
symbolIDX //
);
if (!result)
{
return result;
}
//
info = mSymbolInfos[symbolIDX];
//
return result;
}
/**
* Pause Specified Symbol Trading ...
*
* @param symbol: String ...
*/
void HandlePauseSymbol(string symbol)
{
//
if (!IsValid(symbol))
{
return;
}
//
int idx = -1;
bool hasItem = HasSymbol(
symbol,
idx //
);
if (!hasItem)
{
return;
}
//
int maxAllowedSLToPause = MaxAllowedSLToPauseSignallingPerSymbol();
int pauseDelay = PauseSignallingAfterReachesMaxAllowedSLInSecconds();
if (maxAllowedSLToPause <= 0 || pauseDelay <= 0)
{
return;
}
//
bool canPauseSymbol =
mSymbolInfos[idx].IsEnable() &&
mSymbolInfos[idx].countedSLs >= maxAllowedSLToPause;
if (!canPauseSymbol)
{
return;
}
//
mSymbolInfos[idx].Pause(pauseDelay);
//
string message = "Pause (" + symbol + ") until: (" + ToString(mSymbolInfos[idx].enableAt) + ") ...";
Alert(message);
//
}
/**
* Reset Specified Symbol Info ...
*
* @param symbol: String ...
*/
void ResetSymbolInfo(string symbol)
{
//
if (!IsValid(symbol))
{
return;
}
//
int symbolIDX = -1;
bool hasSymbol = HasSymbol(
symbol,
symbolIDX //
);
if (!hasSymbol)
{
return;
}
//
mSymbolInfos[symbolIDX].Reset();
//
string message = "Reset " + symbol + ", Pause State ...";
Alert(message);
}
/**
* Reset All Paused Symbols ...
*/
void ResumePausedSymbols()
{
//
int count = ArraySize(mSymbolInfos);
if (!IsValidSize(count))
{
return;
}
//
for (int i = 0; i < count; i++)
{
//
mSymbolInfos[i].Reset();
}
//
string message = "Force Resume Paused Symbols ...";
Alert(message);
}
//
// Signal Execution Actions ...
/**
* Validate Signal For Execution ...
*
* @param signal: XSignal instance Reference ...
* @param state: ENUM_X_SIGNAL_EXECUTION_RESULT Reference for olding Signal Execution Result Reason ...
*
* @return ( bool )
*/
bool CanExecute(
XSignal &signal,
ENUM_X_SIGNAL_EXECUTION_RESULT &state // Execution State
)
{
//
bool result = false;
//
state = X_SIGNAL_EXECUTION_UNKNOWN;
//
// Validate Signal ...
result = signal.IsValid();
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS;
return result;
}
//
int symbolIDX = -1;
bool hasSymbolInfo = HasSymbol(
signal.symbol,
symbolIDX //
);
//
// Check rading Not Paused ...
result =
!hasSymbolInfo ||
mSymbolInfos[symbolIDX].IsEnable();
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
return result;
}
//
bool isLong = IsLong(signal.type);
//
// Chekc Signalling Enable or not ...
result =
isLong
? mAllowLong
: mAllowShort;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
return result;
}
//
// Check Signal is Support or not ...
bool isSupport = IsSupport(signal.comment);
result = !isSupport;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
return result;
}
//
ENUM_X_POSITION_TYPES pType = ToPositionType(signal.type);
double requiredProfitForAcceptNext = LastPositionProfitForAcceptNextInPoint();
//
// Implement Same Signal Conditions ...
XPosition smaeSymbolPositions[];
int sameSymbolPositionsCount = mTrader.GetPositions(
smaeSymbolPositions,
signal.symbol,
signal.provider,
signal.period,
pType,
true, // Filter By Magic ...
true // Force Clean ...
);
XPosition youngestSame;
int youngestSameAge = GetYoungest(
youngestSame,
smaeSymbolPositions //
);
//
double youngestSameProfitInPoint = youngestSame.GetProfitInPoint();
//
result =
sameSymbolPositionsCount == 0 ||
(sameSymbolPositionsCount <= 0
? true
: requiredProfitForAcceptNext <= 0
? true
: youngestSameAge >= 0 &&
youngestSameProfitInPoint >= requiredProfitForAcceptNext);
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_PREV_PROFIT_ERROR;
return result;
}
//
// Retrieve Longs and Shorts ...
XPosition longs[];
XPosition shorts[];
bool useMaxAllowedPositionsPerSymbol = UseMaxAllowedSignalsPerSymbol();
//
// Retrieve Positions ...
if (useMaxAllowedPositionsPerSymbol)
{
//
mTrader.GetPositions(
longs,
shorts,
signal.symbol,
signal.provider //
);
}
else
{
//
mTrader.GetPositions(
longs,
shorts,
NULL, // All Symbols ...
signal.provider //
);
}
//
// Counting ...
int longsCount = ArraySize(longs);
int shortsCount = ArraySize(shorts);
//
int maxAllowedLongs = MaxAllowedLongs();
int maxAllowedShorts = MaxAllowedShorts();
//
// Check Max Allowed Positions ...
if (maxAllowedLongs > 0 ||
maxAllowedShorts > 0)
{
//
if (isLong && maxAllowedLongs > 0 && longsCount > 0)
{
result = longsCount < maxAllowedLongs;
}
else if (!isLong && maxAllowedShorts > 0 && shortsCount > 0)
{
result = shortsCount < maxAllowedShorts;
}
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
return result;
}
}
//
// Check Delays Bar if Provided ...
int delaysBarBetweenTwoSignal = DelaysBetweenTwoSignalsInBar();
if (delaysBarBetweenTwoSignal > 0)
{
//
// Try To Detect Youngest Position ...
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 //
);
}
//
// Check Delays When Youngest Position Exists ...
if (youngestAge > 0 && youngestPosition.IsValid())
{
//
result = youngestAge >= delaysBarBetweenTwoSignal;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED;
return result;
}
}
//
// Check Delays When Youngest Position is not Exists ...
if (!youngestPosition.IsValid())
{
//
// Try to Detect Last Issued Signal in Model Collections ...
int symbolIDX = FindLastExecutedSignalItem(signal);
if (IsValidIndex(symbolIDX))
{
//
// Calculate Requirements ...
datetime cTime = TimeCurrent();
int signalPeriodSeconds = PeriodSeconds(signal.period);
datetime lastExecutedTime = mLastExecutedSignals[symbolIDX].time;
datetime passedTime = ((datetime)lastExecutedTime +
((delaysBarBetweenTwoSignal * signalPeriodSeconds)));
//
// Check Conditions ...
result = cTime >= passedTime;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED;
return result;
}
}
}
}
//
// Check Max Drawdown Percent for Open Trades ...
double maxAllowedDrawdownPercent = MaxAllowedDrawdownToOpenTrades();
if (maxAllowedDrawdownPercent > 0)
{
//
// Calculate and Update Drawdown Percent ...
double cdPercent = CalculateMaxDrawdownPercent();
//
result = cdPercent < maxAllowedDrawdownPercent;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY;
return result;
}
}
//
// Check Spread ...
double spread = GetSpread(signal.symbol);
double maxAllowedSpread = MaxAllowedSpread();
if (maxAllowedSpread > 0)
{
//
result = spread <= maxAllowedSpread;
if (!result)
{
//
state = X_SIGNAL_EXECUTION_FAILED_SPREAD;
return result;
}
}
//
return result;
}
/**
* Execute Specific Signal using Trade Handler ...
*
* @param signal: XSignal instance Reference ...
* @param state: ENUM_X_SIGNAL_EXECUTION_RESULT Reference for olding Signal Execution Result Reason ...
* @param ignorePolicies: Boolean ...
*
* @return ( bool )
*/
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;
}
//
// Check Policies ...
if (!ignorePolicies)
{
//
// Filter Signals if Necessary ...
result = CanExecute(
signal,
state //
);
if (!result)
{
//
AlertSignalExecutionFailed(
signal,
state //
);
//
return result;
}
}
//
// Apply Additional Volume to Signal if Provided ...
double additionalVolume = AdditionalVolume();
if (additionalVolume > 0)
{
signal.volume += mAdditionalVolume;
}
//
// Execute Signal ...
result = mTrader.ExecuteSignal(
signal,
state //
);
if (result)
{
//
// Add XTradeHandler Data ...
Add(signal);
//
// Handle Last Executed Signal ...
HandleSignalExecuted(signal);
//
// Alert Executed Signal ...
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);
}
//
if (!result)
{
//
AlertSignalExecutionFailed(
signal,
state //
);
}
//
return result;
}
/**
* Execute Specific Signal using Trade Handler ...
*
* @param signal: XSignal instance Reference ...
* @param state: ENUM_X_SIGNAL_EXECUTION_RESULT Reference for olding Signal Execution Result Reason ...
* @param ignorePolicies: Boolean ...
*
* @return ( bool )
*/
bool ExecuteSignal(
XSignal &signal, // Signal for Execution
X121SMCStrategyConditions &conditions, // Signal Conditions
ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State
bool ignorePolicies = false // Ignore Execution Policies
)
{
//
bool result = false;
//
// Check Signal Validation ...
result = signal.IsValid() &&
conditions.IsValid();
if (!result)
{
return result;
}
//
// Check Policies ...
if (!ignorePolicies)
{
//
// Filter Signals if Necessary ...
result = CanExecute(
signal,
state //
);
if (!result)
{
//
AlertSignalExecutionFailed(
signal,
state //
);
//
return result;
}
}
//
// Apply Additional Volume to Signal if Provided ...
double additionalVolume = AdditionalVolume();
if (additionalVolume > 0)
{
signal.volume += mAdditionalVolume;
}
//
// Execute Signal ...
result = mTrader.ExecuteSignal(
signal,
state //
);
if (result)
{
//
// Add XTradeHandler Data ...
Add(signal);
//
// Handle Last Executed Signal ...
HandleSignalExecuted(signal);
//
// Alert Executed Signal ...
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);
}
//
if (!result)
{
//
AlertSignalExecutionFailed(
signal,
state //
);
}
//
return result;
}
/**
* Force Close Specified Positions ...
*
* @param symbol: String ...
* @param provider: String ...
* @param period: ENUM_TIMEFRAMES member ...
* @param type: ENUM_X_POSITION_TYPES member ...
*
* @return ( bool )
*/
bool ForceClose(
string symbol = NULL,
string provider = NULL,
ENUM_TIMEFRAMES period = NULL,
ENUM_X_POSITION_TYPES type = NULL //
)
{
//
bool result = false;
//
XPosition positions[];
int count = mTrader.GetPositions(
positions,
symbol,
provider,
period,
type //
);
result = IsValidSize(count);
if (!result)
{
return result;
}
//
datetime cTime = TimeCurrent();
//
string comment = "Force Close (" + ToString(count) + ") Positions At: " + ToFormatString(cTime) + "...";
int closedPositionsCount = mTrader.Close(
positions,
comment //
);
result = IsValidSize(closedPositionsCount);
if (result)
{
//
// Finish Positions and Clear Data ...
for (int i = 0; i < ArraySize(positions); i++)
{
//
XPosition iPosition = positions[i];
Finish(
iPosition.ticket,
iPosition,
comment //
);
}
//
ResetProtections();
//
string message = "Force Close (" + ToString(count) + ") Positions At: " +
ToFormatString(cTime) + " Successfully ...";
Alert(message);
}
//
return result;
}
/**
* Force Close Positions ...
*
* @return ( bool )
*/
bool ForceClose(
XPosition &positions[],
string comment,
string notificationMessage //
)
{
//
bool result = false;
//
int count = ArraySize(positions);
result = IsValidSize(count);
if (!result)
{
return result;
}
//
int closedPositionsCount = mTrader.Close(
positions,
comment //
);
result = IsValidSize(closedPositionsCount);
if (result)
{
//
// Finish Positions and Clear Data ...
for (int i = 0; i < ArraySize(positions); i++)
{
//
XPosition iPosition = positions[i];
Finish(
iPosition.ticket,
iPosition,
comment //
);
}
//
ResetProtections();
//
if (IsValid(notificationMessage))
{
Alert(notificationMessage);
}
}
//
return result;
//
return result;
}
//
// Timing Actions ...
/**
* Update Currently Open Positions Info on Data Collection ...
*/
void UpdateData()
{
//
// Retrieve Positions ...
XPosition positions[];
int count = mTrader.GetPositions(positions);
if (!IsValidSize(count))
{
return;
}
//
// Loop Through Positions ...
for (int i = 0; i < count; i++)
{
//
XPosition iPosition = positions[i];
//
// Check Position Registered as Data or not ...
int idx = -1;
bool isExists = HasItem(
iPosition.ticket,
idx //
);
if (isExists)
{
//
// Update Position Data ...
mData[idx].Update(iPosition);
}
}
}
/**
* Handle Positiona Protections
*/
void HandleProtection()
{
//
string prefix = "Protector: ";
//
int targetDistance = 5;
//
// Implement Protection Senario Here ...
int count = Count();
if (!IsValidSize(count))
{
//
currentDrawdown = 0;
return;
}
//
// Calculate Max Same Time Trades ...
maxSameTimeTrades =
maxSameTimeTrades == 0 ||
maxSameTimeTrades < count
? count
: maxSameTimeTrades;
//
double bepSummary = 0;
double volumeSummary = 0;
double profitSummary = 0;
XPosition positions[];
bool isProtected = false;
//
// Loop through Open Positions ...
for (int i = 0; i < count; i++)
{
//
X121SMCTradeData iData = mData[i];
//
XPosition iPosition;
bool hasPosition = mTrader.GetPosition(
iData.ticket,
iPosition //
);
if (!hasPosition)
{
continue;
}
AddRef(
iPosition,
positions //
);
//
// Calculate Break Even Point ...
double entry = iPosition.entry;
double points = iData.Points();
double volume = iPosition.volume;
bool isLong = IsLong(iData.Type());
double spread = GetSpread(iData.GetSymbol());
double bep = iData.CalculateBreakEvenPoint();
double exitPrice = GetExit(
iPosition.symbol,
iPosition.type //
);
//
double tps[];
int reachedTPIndex = -1;
double reachedTPPrice = 0;
double reachedTPLevel = -1;
int tpLevelsCount = iPosition.CalculateTPLevels(tps);
iPosition.CalculateReahedTP(
reachedTPLevel,
reachedTPPrice //
);
if (reachedTPLevel > 0)
{
//
reachedTPIndex = FindIndex(
reachedTPPrice,
tps //
);
}
//
bepSummary += bep;
volumeSummary += volume;
profitSummary += iPosition.profit;
//
// All Protections Done for InProfit Positions ...
if (iData.profit > 0)
{
//
// Check Break Even ...
bool canRFOnBEP = iData.CanRiskFreeOnBreakEvenPoint();
if (canRFOnBEP && reachedTPLevel > 0)
{
//
double tpLevel = iData.TPLevelForBreakEven();
int tpIDX = (int)tpLevel - 1;
double rfTPPrice = tps[tpIDX];
bool isSLReady = isLong
? iPosition.sl < rfTPPrice
: iPosition.sl > rfTPPrice;
if (isSLReady && tpLevel == reachedTPLevel - 1)
{
//
double sl = rfTPPrice;
double tp = iPosition.tp;
string comment = "RF on BEP ...";
bool isModified = mTrader.Modify(
iData.ticket,
sl,
tp,
comment //
);
if (isModified)
{
//
isProtected = true;
//
string message = prefix +
ToString(iData.Type()) +
" Position: " +
ToString(iData.ticket) +
" RF On BEP Successfully ...";
Alert(message);
}
}
}
//
// Check Partial Close ...
bool canPartialClose = iData.CanPartialClose();
if (canPartialClose && reachedTPLevel > 0)
{
//
double mainVolume = iData.Volume();
double currentVolume = iPosition.volume;
double partialCloseOnTP = iData.PartialCloseOnTPLevel();
double partialCloseVolumeMultiplier = iData.PartialCloseMultiplier();
double closeVolume = currentVolume * partialCloseVolumeMultiplier;
closeVolume = NormalizeVolume(closeVolume, iPosition.symbol);
canPartialClose =
currentVolume == mainVolume &&
reachedTPLevel == partialCloseOnTP;
if (canPartialClose)
{
//
string comment = "PC On TP Level: " + ToString(partialCloseOnTP) + " ...";
bool isModified = mTrader.ClosePartial(
iData.ticket,
closeVolume,
comment //
);
if (isModified)
{
//
isProtected = true;
//
string message = prefix +
ToString(iData.Type()) +
" Position: " +
ToString(iData.ticket) +
" PC (" + ToString(closeVolume) + ") On TP Level: " +
ToString(partialCloseOnTP) + " Successfully ...";
Alert(message);
//
// Finish Position ...
if (closeVolume == mainVolume)
{
//
Finish(
iPosition.ticket,
iPosition,
comment //
);
//
break;
}
}
}
}
//
// Check for SL Trail ...
bool canTrailSL = iData.CanTrailSL();
if (canTrailSL && reachedTPLevel > 0)
{
//
double tSLTPPrice = 0;
//
double startTrailTPLevel = iData.TrailSLStartOnReachTPLevel();
if (iData.lastTrailedTPLevel > 0)
{
startTrailTPLevel = iData.lastTrailedTPLevel + 1;
}
//
canTrailSL = IsValidIndex(reachedTPIndex) &&
reachedTPLevel >= startTrailTPLevel;
if (canTrailSL)
{
//
tSLTPPrice =
reachedTPIndex == 0
? tps[reachedTPIndex]
: tps[reachedTPIndex - 1];
//
canTrailSL =
tSLTPPrice > 0 &&
iPosition.sl == 0
? (isLong
? tSLTPPrice > iPosition.entry &&
tSLTPPrice < iPosition.price
: tSLTPPrice < iPosition.entry &&
tSLTPPrice > iPosition.price)
: (isLong
? tSLTPPrice > iPosition.sl &&
tSLTPPrice > iPosition.entry &&
tSLTPPrice < iPosition.price
: tSLTPPrice < iPosition.sl &&
tSLTPPrice < iPosition.entry &&
tSLTPPrice > iPosition.price);
}
//
if (canTrailSL)
{
//
string comment = "Trail SL on Reached TP: " + ToString(reachedTPLevel) + " ...";
double sl = tSLTPPrice;
double tp = iPosition.tp;
bool isModified = mTrader.Modify(
iData.ticket,
sl,
tp,
comment //
);
if (isModified)
{
//
isProtected = true;
//
// Update Last Trailed TP Level ...
mData[i].lastTrailedTPLevel = reachedTPLevel;
//
string message = prefix +
ToString(iData.Type()) +
" Position: " +
ToString(iData.ticket) +
" Trailed SL On Reached TP: " + ToString(reachedTPLevel) +
" Successfully ...";
Alert(message);
}
}
}
//
// Handle RF On Target ...
double targets[];
int targetsCount = iData.Targets(targets);
bool hasTargets = IsValidSize(targetsCount);
if (hasTargets)
{
//
for (int k = 0; k < targetsCount; k++)
{
//
double target = targets[k];
bool isValidTarget =
iData.lastTarget == 0
? true
: isLong
? target > iData.lastTarget
: target < iData.lastTarget;
if (!isValidTarget)
{
continue;
}
//
double targetDelta = isLong
? target + (targetDistance * points)
: target - (targetDistance * points);
bool canRF =
target > 0 &&
(isLong ? target > entry
: target < entry) &&
(isLong
? iPosition.sl < target
: iPosition.sl > target) &&
(isLong
? iPosition.price > targetDelta
: iPosition.price < targetDelta);
if (canRF)
{
//
double sl = target;
double tp = iPosition.tp;
string comment = "RF On Target ...";
bool isModified = mTrader.Modify(
iData.ticket,
sl,
tp,
comment //
);
if (isModified)
{
//
isProtected = true;
//
// Update mData ...
mData[i].lastTarget = target;
//
string message = prefix +
ToString(iData.Type()) +
" Position: " +
ToString(iData.ticket) +
" RF On Traget: " + ToString(target) +
" Successfully ...";
Alert(message);
}
}
}
}
}
}
//
// Calculate Max Draw Down ...
maxDrawdown =
profitSummary < 0 &&
(maxDrawdown == 0 ||
maxDrawdown < profitSummary)
? profitSummary
: maxDrawdown;
//
// Calculate Current Drawdown ...
currentDrawdown =
profitSummary < 0
? profitSummary
: 0;
//
// Checking Hedge ...
bool allowHedge = AllowHedging() &&
!isProtected;
if (allowHedge)
{
//
double minHedgeVolumeStep = HedgingMinVolumeStep();
int minRequiredPositionsForHedge = MinOpenTradesFroHedging();
double minRequiredProfitPerVolumeStepForHedge = HedgeingMinRequiredProfitPerVolumeStep();
//
double minRequiredAdditionalProfit =
bepSummary +
((volumeSummary / minHedgeVolumeStep) * minRequiredProfitPerVolumeStepForHedge);
//
bool canHedge = AllowHedging() &&
profitSummary > 0 &&
minHedgeVolumeStep > 0 &&
minRequiredProfitPerVolumeStepForHedge > 0 &&
profitSummary >= minRequiredAdditionalProfit &&
(minRequiredPositionsForHedge <= 0
? true
: count >= minRequiredPositionsForHedge);
if (canHedge)
{
//
string comment = "Hedge (" + ToString(ArraySize(positions)) + ") Positions By: " + ToString(profitSummary) + "...";
int closedPositionsCount = mTrader.Close(
positions,
comment //
);
if (IsValidSize(closedPositionsCount))
{
//
// Finish Positions and Clear Data ...
for (int i = 0; i < ArraySize(positions); i++)
{
//
XPosition iPosition = positions[i];
Finish(
iPosition.ticket,
iPosition,
comment //
);
}
//
ResetProtections();
//
string message = "Hedge (" + ToString(ArraySize(positions)) + ") Positions By: " +
ToString(profitSummary) + " Successfully ...";
Alert(message);
}
}
}
}
//
// Protected ...
protected:
//
XCTrade *mTrader; // Instance of Trader Class
X121SMCTradeCollector *mCollector; // Instance of Trade Collector Class
//
X121SMCTradeData mData[]; // Hold Trade Data
X121SMCSymbolPositionInfo mSymbolInfos[]; // Symbol Positions Info
XSymbolLastPosition mLastExecutedSignals[]; // Last Executed Signals
//
void AlertSignalExecutionFailed(
XSignal &signal,
ENUM_X_SIGNAL_EXECUTION_RESULT state //
)
{
//
// Alert Signal Execution Failed ...
string msg = "Failed To Execute " +
(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) +
" due Reason: " + ToString(state) + " ...";
Alert(msg);
}
//
// Private ...
private:
//
// Props ...
//
// Read Only ...
int maxSameTimeTrades; // Holds Max Same Time Trades
double maxDrawdown; // Max Drawdown
double currentDrawdown; // Current 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
//
// Signalling Props ...
bool mAllowLong; // Allow Long Signals ...
bool mAllowShort; // Allow Short Signals ...
int mMaxAllowedLongs; // Max Allowed Long Signals ...
int mMaxAllowedShorts; // Max Allowed Short Signals ...
bool mUseMaxAllowedSignalsPerSymbol; // Use Max Allowed Signal Types Per Symbol ...
double mLastPositionProfitForAcceptNextInPoint; // Last Position Profit In Points for Accept new Signal ...
int mDelaysBetweenTwoSignalsInBar; // Delay between two Signals in Bars ...
double mMaxAllowedSpread; // Max Allowed Spread for Signalling ...
double mAdditionalVolume; // Volume Additional ...
//
// Risk Management Props ...
double mMaxAllowedDrawdownToOpenTrades; // Max Allowed Drawdown for Open Trades ...
int mMaxAllowedSLToPauseSignallingPerSymbol; // Max Allowed SL for Pause Signalling Per Symbol ...
int mPauseSignallingAfterReachesMaxAllowedSLInSecconds; // Pause Signalling Per Symbol afte Max Allowed SL Reached In Seconds ...
//
// Protection Props ...
bool mAllowHedging; // Allow Hedge Positions ...
int mMinOpenTradesFroHedging; // Minimum Open Positions for Hedging ...
double mHedgingMinVolumeStep; // Minimum Volume Step for Hedging ...
double mHedgeingMinRequiredProfitPerVolumeStep; // Minimum Required Profit Per Volume Step for Hedging ...
//
// Common Functions ...
//
// Count Data ...
int Count()
{
return ArraySize(mData);
}
//
// Add Item ...
bool Add(X121SMCTradeData &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.signal,
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;
}
//
bool HasItem(
XSignal &signal,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
int count = Count();
result = signal.IsValid() &&
IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
bool isOwn = mData[i].IsOwn(signal);
if (isOwn)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
return result;
}
//
bool HasItem(
XPosition &position,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
int count = Count();
result = position.IsValid() &&
IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
bool isOwn = mData[i].IsOwn(position);
if (isOwn)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
return result;
}
//
bool HasItemByPosition(
XPosition &position,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
int count = Count();
result = position.IsValid() &&
IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
bool isOwn = mData[i].IsOwnPosition(position);
if (isOwn)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
return result;
}
/**
* Find Specific Symbo Indo Item index ...
*
* @param symbol: String ...
* @param index: Integer ...
*
* @return ( bool )
*/
bool HasSymbol(
string symbol,
int &index //
)
{
//
bool result = false;
//
index = -1;
//
result = IsValid(symbol);
if (!result)
{
return result;
}
//
int count = ArraySize(mSymbolInfos);
result = IsValidSize(count);
if (!result)
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
bool isOwn = mSymbolInfos[i]
.IsOwn(symbol);
if (isOwn)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
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(X121SMCTradeData &item)
{
//
if (!mSaveSignals)
{
return;
}
//
// Save Signal ...
mCollector.SaveSignal(item);
}
//
// Find Last Signal Execution ...
int FindLastExecutedSignalItem(XSignal &signal)
{
//
int result = -1;
//
if (!signal.IsValid())
{
return result;
}
//
int count = ArraySize(mLastExecutedSignals);
if (!IsValidSize(count))
{
return result;
}
//
for (int i = 0; i < count; i++)
{
//
XSymbolLastPosition iLast = mLastExecutedSignals[i];
if (iLast.symbol == signal.symbol &&
iLast.provider == signal.provider)
{
//
result = i;
break;
}
}
//
return result;
}
//
// Handle Last Signal Execution ...
void HandleSignalExecuted(XSignal &signal)
{
//
if (!signal.IsValid())
{
return;
}
//
datetime cTime = TimeCurrent();
int symbolIDX = FindLastExecutedSignalItem(signal);
bool hasItem = IsValidIndex(symbolIDX);
if (hasItem)
{
mLastExecutedSignals[symbolIDX].time = cTime;
}
else
{
//
XSymbolLastPosition item;
//
item.time = cTime;
item.symbol = signal.symbol;
item.provider = signal.provider;
//
AddRef(
item,
mLastExecutedSignals //
);
}
}
//
double CalculateMaxDrawdownPercent()
{
//
double result = 0;
//
double mEquity = mTrader.mAccount.GetEquity();
if (mEquity > currentDrawdown)
{
currentDrawdown = mEquity;
}
//
if (currentDrawdown >= 0)
{
//
drawdownPercent = 0;
return result;
}
//
drawdownPercent = (currentDrawdown - mEquity) /
(staticBalanceForCalculateDrawdown > 0
? staticBalanceForCalculateDrawdown
: currentDrawdown) *
100;
drawdownPercent = NormalizeDouble(drawdownPercent, 3);
result = drawdownPercent;
//
return result;
}
//
void ResetProtections()
{
//
ResumePausedSymbols();
Clean(mLastExecutedSignals);
}
//
// End of XC121SMCTradeHandler Class ...
};
//