4403 lines
98 KiB
Plaintext
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 ...
|
|
};
|
|
|
|
// |