2235 lines
50 KiB
Plaintext
2235 lines
50 KiB
Plaintext
///////////////////////////////////////////////////////
|
|
//
|
|
// SaherElm IT Center MQL5 Class Library
|
|
// ----------------------------------------------
|
|
// Name: XCXTradeManager
|
|
// Description: provides all Trade Management requirements ...
|
|
//
|
|
//
|
|
// Maintainer:
|
|
// ------------
|
|
// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com)
|
|
//
|
|
//////////////////////////////////////////////////////
|
|
//
|
|
// Global Properties ...
|
|
#property library
|
|
#property copyright "Copyright 2023, SaherElm IT Center"
|
|
#property link "https://www.saherelm.ir"
|
|
#property version "1.00"
|
|
#property strict
|
|
|
|
//
|
|
// Imports ...
|
|
#include "../Classes/x-saherelm.x-alert.class.mq5"
|
|
#include "../Classes/x-saherelm.x-data.collector.class.mq5"
|
|
#include "../Classes/x-saherelm.x-guard.class.mq5"
|
|
#include "../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
|
#include "../Classes/x-saherelm.x-trade.class.mq5"
|
|
|
|
//
|
|
// Implementation ...
|
|
class XCTradeManager : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
XCAlert *alert;
|
|
XCGuard *guard;
|
|
XCTrade *trader;
|
|
XTimeTracker timeTracker;
|
|
XCDataCollector *collector;
|
|
|
|
//
|
|
XCPOIDrawer *drawer;
|
|
|
|
//
|
|
// Constructor ...
|
|
XCTradeManager(
|
|
XCAlert *_alert,
|
|
XCTrade *_trader,
|
|
XCGuard *_guard //
|
|
)
|
|
{
|
|
//
|
|
alert = _alert;
|
|
guard = _guard;
|
|
trader = _trader;
|
|
|
|
//
|
|
drawer = new XCPOIDrawer();
|
|
|
|
//
|
|
allowTrade = true;
|
|
allowLongs = true;
|
|
allowShorts = true;
|
|
|
|
//
|
|
forceCloseAt = NULL;
|
|
isForceCloseAtTime = false;
|
|
|
|
//
|
|
maxAllowedTPs = 0;
|
|
maxAllowedSLs = 0;
|
|
maxAllowedTrades = 0;
|
|
restrictionsPeriod = XTRADE_RESTRICATION_NONE;
|
|
|
|
//
|
|
reportTrades = true;
|
|
reportSignals = true;
|
|
reportProtector = true;
|
|
reportAfterTradesBalance = true;
|
|
|
|
//
|
|
SaveTrades(false);
|
|
|
|
//
|
|
collector = new XCDataCollector();
|
|
|
|
//
|
|
// Setting Collector Path ...
|
|
string mPath = (IsValid(alert.GetPrefix())
|
|
? alert.GetPrefix() + "\\"
|
|
: "") +
|
|
trader.mAccount.GetCompany();
|
|
collector.Path(mPath);
|
|
}
|
|
|
|
//
|
|
// Deconstructor ...
|
|
~XCTradeManager()
|
|
{
|
|
//
|
|
Clean(trades);
|
|
|
|
//
|
|
timeTracker.Clean();
|
|
|
|
//
|
|
ZeroMemory(alert);
|
|
ZeroMemory(trader);
|
|
|
|
//
|
|
delete drawer;
|
|
ZeroMemory(drawer);
|
|
|
|
//
|
|
delete collector;
|
|
ZeroMemory(collector);
|
|
}
|
|
|
|
//
|
|
// Properties ...
|
|
|
|
//
|
|
bool SaveTrades()
|
|
{
|
|
return mSaveTrades;
|
|
}
|
|
|
|
//
|
|
void SaveTrades(bool value)
|
|
{
|
|
mSaveTrades = value;
|
|
}
|
|
|
|
//
|
|
bool SaveSignals()
|
|
{
|
|
return mSaveSignals;
|
|
}
|
|
|
|
//
|
|
void SaveSignals(bool value)
|
|
{
|
|
mSaveSignals = value;
|
|
}
|
|
|
|
//
|
|
bool SaveWins()
|
|
{
|
|
return mSaveWins;
|
|
}
|
|
|
|
//
|
|
void SaveWins(bool value)
|
|
{
|
|
mSaveWins = value;
|
|
}
|
|
|
|
//
|
|
bool SaveLosts()
|
|
{
|
|
return mSaveLosts;
|
|
}
|
|
|
|
//
|
|
void SaveLosts(bool value)
|
|
{
|
|
mSaveLosts = value;
|
|
}
|
|
|
|
//
|
|
bool SaveRestrictions()
|
|
{
|
|
return mSaveRestrictions;
|
|
}
|
|
|
|
//
|
|
void SaveRestrictions(bool value)
|
|
{
|
|
mSaveRestrictions = value;
|
|
}
|
|
|
|
//
|
|
// Setter(s) ...
|
|
|
|
//
|
|
void SetForceCloseTradesAt(
|
|
string _forceCloseAt //
|
|
)
|
|
{
|
|
forceCloseAt = _forceCloseAt;
|
|
}
|
|
|
|
//
|
|
void SetTradeSession(
|
|
string symbol,
|
|
string start,
|
|
string end,
|
|
string name = NULL //
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
bool has = IsValid(end) &&
|
|
IsValid(start) &&
|
|
IsValid(symbol);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Create Session ...
|
|
|
|
//
|
|
XTradeSession session;
|
|
|
|
//
|
|
session.end = end;
|
|
session.name = name;
|
|
session.start = start;
|
|
|
|
//
|
|
int sessionIDX = -1;
|
|
has = FindSymbolSession(
|
|
sessionIDX,
|
|
symbol //
|
|
);
|
|
|
|
//
|
|
if (!has)
|
|
{
|
|
//
|
|
// Add New Symbol Session ...
|
|
|
|
//
|
|
XSymbolSessions iSymbolSession;
|
|
iSymbolSession.symbol = symbol;
|
|
|
|
//
|
|
AddTradingSession(
|
|
session,
|
|
iSymbolSession //
|
|
);
|
|
|
|
//
|
|
AddRef(
|
|
iSymbolSession,
|
|
sessions //
|
|
);
|
|
|
|
//
|
|
iSymbolSession.Clean();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Update Trading Sessions of Exists Symbol Session ...
|
|
AddTradingSession(
|
|
session,
|
|
sessions[sessionIDX] //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
session.Clean();
|
|
}
|
|
|
|
//
|
|
void SetTradeReports(
|
|
bool _reportTrades,
|
|
bool _reportSignals,
|
|
bool _reportProtector,
|
|
bool _reportRestrictions,
|
|
bool _reportAfterTradesBalance //
|
|
)
|
|
{
|
|
//
|
|
reportTrades = _reportTrades;
|
|
reportSignals = _reportSignals;
|
|
reportProtector = _reportProtector;
|
|
reportRestrictions = _reportRestrictions;
|
|
reportAfterTradesBalance = _reportAfterTradesBalance;
|
|
}
|
|
|
|
//
|
|
void SetTradePermissions(
|
|
bool _allowTrade,
|
|
bool _allowLongs,
|
|
bool _allowShorts //
|
|
)
|
|
{
|
|
//
|
|
allowTrade = _allowTrade;
|
|
allowLongs = _allowLongs;
|
|
allowShorts = _allowShorts;
|
|
}
|
|
|
|
//
|
|
void SetTradeRestrictions(
|
|
ENUM_XTRADE_RESTRICTION_PERIOD _period,
|
|
int _maxAllowedTPs,
|
|
int _maxAllowedSLs,
|
|
int _maxAllowedTrades //
|
|
)
|
|
{
|
|
//
|
|
// Normalizing Args ...
|
|
_maxAllowedTPs = NormalizeInt(_maxAllowedTPs, 0);
|
|
_maxAllowedSLs = NormalizeInt(_maxAllowedSLs, 0);
|
|
_maxAllowedTrades = NormalizeInt(_maxAllowedTrades, 0);
|
|
|
|
//
|
|
restrictionsPeriod = _period;
|
|
maxAllowedTPs = _maxAllowedTPs;
|
|
maxAllowedSLs = _maxAllowedSLs;
|
|
maxAllowedTrades = _maxAllowedTrades;
|
|
}
|
|
|
|
//
|
|
// Tools and Helpers ...
|
|
|
|
/**
|
|
* Do All Position Managing Senaros ...
|
|
*/
|
|
void Manage()
|
|
{
|
|
//
|
|
// Handle Restrictions ...
|
|
HandleRestrictions();
|
|
|
|
//
|
|
// Check Contains Trades for Managing ...
|
|
bool has = HasChild(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Update Required Infos of Trades ...
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
has = trades[i].IsExecuted() &&
|
|
!trades[i].IsFinalized();
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition iPosition;
|
|
has = trader.GetPosition(
|
|
trades[i].positionID,
|
|
iPosition //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Update Trade Data ...
|
|
trades[i].swap = iPosition.swap;
|
|
trades[i].profit = iPosition.profit;
|
|
}
|
|
|
|
//
|
|
// Do Trade Protection ...
|
|
Protect();
|
|
}
|
|
|
|
/**
|
|
* Do All Position Protection Here ...
|
|
*/
|
|
void Protect()
|
|
{
|
|
//
|
|
string prefix = "Protector: ";
|
|
int targetDistance = 5;
|
|
|
|
//
|
|
bool has = HasChild(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
XPosition ownPosition[];
|
|
XPosition ownLongPosition[];
|
|
XPosition ownShortPosition[];
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
has =
|
|
trades[i].IsExecuted() &&
|
|
!trades[i].IsFinalized();
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
XPosition iPosition;
|
|
has = trader.GetPosition(
|
|
trades[i].positionID,
|
|
iPosition //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownPosition //
|
|
);
|
|
|
|
//
|
|
if (IsLong(iPosition.type))
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownLongPosition //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
ownShortPosition //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
has = HasChild(ownPosition);
|
|
if (!has)
|
|
{
|
|
//
|
|
Clean(ownPosition);
|
|
Clean(ownLongPosition);
|
|
Clean(ownShortPosition);
|
|
|
|
//
|
|
return;
|
|
}
|
|
|
|
//
|
|
double swapSum = 0;
|
|
double profitSum = 0;
|
|
double commissionSum = 0;
|
|
|
|
//
|
|
// Calculate Required Data On Collection ...
|
|
count = ArraySize(ownPosition);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition = ownPosition[i];
|
|
|
|
//
|
|
swapSum += iPosition.swap;
|
|
profitSum += iPosition.profit;
|
|
commissionSum += iPosition.commission;
|
|
}
|
|
|
|
//
|
|
// Do Collection Protectiong ...
|
|
|
|
//
|
|
// Check Force Close ...
|
|
if (IsValid(forceCloseAt))
|
|
{
|
|
//
|
|
datetime forceCloseTime = ParseDateTimeFromTimeString(forceCloseAt);
|
|
bool canForceClose = IsTimeInRange(
|
|
TimeCurrent(),
|
|
forceCloseTime //
|
|
) &&
|
|
!isForceCloseAtTime;
|
|
if (canForceClose)
|
|
{
|
|
//
|
|
isForceCloseAtTime = HandleForceCloseTrades(ownPosition);
|
|
|
|
//
|
|
// Prevent Moving Forward ...
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Do Signle Protections ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Select Position ...
|
|
XPosition iPosition = ownPosition[i];
|
|
int idx = -1;
|
|
has = HasTrade(
|
|
idx,
|
|
iPosition.ticket //
|
|
);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
bool isLong = IsLong(iPosition.type);
|
|
double points = GetPoints(iPosition.symbol);
|
|
|
|
//
|
|
// Handle RF On Targets ...
|
|
if (iPosition.profit > 0)
|
|
{
|
|
//
|
|
// Handle Risk Free On Targets ...
|
|
XTarget targets[];
|
|
Copy(
|
|
trades[idx].signal.targets,
|
|
targets //
|
|
);
|
|
int targetsCount = ArraySize(targets);
|
|
has = IsValidSize(targetsCount);
|
|
if (has)
|
|
{
|
|
//
|
|
for (int j = 0; j < targetsCount; j++)
|
|
{
|
|
//
|
|
XTarget jTarget = targets[j];
|
|
|
|
//
|
|
int appliedTargetIDX = -1;
|
|
|
|
//
|
|
// Check Target is Applied Before or Not ...
|
|
has =
|
|
!trades[idx].signal.isTargetApplied
|
|
? true
|
|
: trades[idx].signal.appliedTargetIDX < j;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Check Traget Validation ...
|
|
has =
|
|
iPosition.sl == 0
|
|
? true
|
|
: isLong
|
|
? iPosition.sl < jTarget.target
|
|
: iPosition.sl > jTarget.target;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
double targetDelta = isLong
|
|
? jTarget.target + (targetDistance * points)
|
|
: jTarget.target - (targetDistance * points);
|
|
|
|
//
|
|
// Can Risk Free ...
|
|
bool canRF =
|
|
jTarget.doRF &&
|
|
jTarget.target > 0 &&
|
|
(isLong
|
|
? jTarget.target > iPosition.entry
|
|
: jTarget.target < iPosition.entry) &&
|
|
(isLong
|
|
? iPosition.price > targetDelta
|
|
: iPosition.price < targetDelta);
|
|
|
|
//
|
|
// Can Partial Close ...
|
|
bool canPC =
|
|
jTarget.volumeMultiplier > 0 &&
|
|
jTarget.target > 0 &&
|
|
(isLong
|
|
? jTarget.target > iPosition.entry
|
|
: jTarget.target < iPosition.entry) &&
|
|
(isLong
|
|
? iPosition.price > targetDelta
|
|
: iPosition.price < targetDelta);
|
|
bool canRFPC = canRF ||
|
|
canPC;
|
|
if (canRFPC)
|
|
{
|
|
//
|
|
// Handle Risk Free ...
|
|
bool isRFDone = false;
|
|
if (canRF)
|
|
{
|
|
//
|
|
double tp = iPosition.tp;
|
|
double sl = jTarget.target;
|
|
string comment = "RF On Target ...";
|
|
isRFDone = trader.Modify(
|
|
iPosition.ticket,
|
|
sl,
|
|
tp,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Handle Partail Close ...
|
|
bool isPCDone = false;
|
|
if (canPC)
|
|
{
|
|
//
|
|
double volume = jTarget.volumeMultiplier * iPosition.volume;
|
|
volume = NormalizeVolume(volume, iPosition.symbol);
|
|
string comment = "RF On Target ...";
|
|
isPCDone = trader.ClosePartial(
|
|
iPosition.ticket,
|
|
volume,
|
|
comment //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Handle Report Action ...
|
|
bool isRFPCDone = isRFDone ||
|
|
isPCDone;
|
|
if (isRFPCDone)
|
|
{
|
|
//
|
|
trades[idx].signal.appliedTargetIDX = j;
|
|
trades[idx].signal.isTargetApplied = true;
|
|
|
|
//
|
|
string message = prefix +
|
|
ToXString(iPosition.type) +
|
|
" Position: " +
|
|
ToXString(iPosition.ticket) +
|
|
(isRFDone ? " RF" : "") +
|
|
(isPCDone ? " PC" : "") +
|
|
" On Traget: " + ToXString(jTarget.target) +
|
|
" Successfully ...";
|
|
HandleReportProtector(message);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check Specified Symbol is Active for
|
|
* Analysing or Open Trades ...
|
|
*/
|
|
bool IsActiveSession(string symbol)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Validate Args ...
|
|
result = IsValid(symbol);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Symbol Session Exists or not ...
|
|
int symbolSessionIDX = -1;
|
|
bool isSymbolSessionExists = FindSymbolSession(
|
|
symbolSessionIDX,
|
|
symbol //
|
|
);
|
|
|
|
//
|
|
// Chack Trade Session Validating ...
|
|
if (isSymbolSessionExists)
|
|
{
|
|
result = sessions[symbolSessionIDX].CanTrade();
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Signal Execution Conditions ...
|
|
*
|
|
* @param signal: XSignal instance reference ...
|
|
* @param executionResult: ENUM_X_SIGNAL_EXECUTION_RESULT member reference ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool CheckSignal(
|
|
XSignal &signal,
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT &executionResult //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Cleanup ...
|
|
executionResult = X_SIGNAL_EXECUTION_UNKNOWN;
|
|
|
|
//
|
|
// Validate Signal ...
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Reporting Signal ...
|
|
if (reportSignals)
|
|
{
|
|
//
|
|
string message = signal.GetMessage("Provided");
|
|
alert.SendAlert(message);
|
|
}
|
|
|
|
//
|
|
SaveSignal(signal);
|
|
|
|
//
|
|
// Check Trade Permission ...
|
|
if (!allowTrade)
|
|
{
|
|
//
|
|
result = false;
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool isLong = IsLong(signal.type);
|
|
bool isShort = IsShort(signal.type);
|
|
|
|
//
|
|
bool isLongAndAllowed =
|
|
isLong &&
|
|
allowLongs;
|
|
|
|
//
|
|
bool isShortAndAllowed =
|
|
isShort &&
|
|
allowShorts;
|
|
|
|
//
|
|
result = isLongAndAllowed ||
|
|
isShortAndAllowed;
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Symbol Info Exists or not ...
|
|
int symbolInfoIDX = -1;
|
|
bool isSymbolInfoExists = FindSymbolIndex(
|
|
symbolInfoIDX,
|
|
signal.symbol //
|
|
);
|
|
|
|
//
|
|
// Check Symbol Session Exists or not ...
|
|
int symbolSessionIDX = -1;
|
|
bool isSymbolSessionExists = FindSymbolSession(
|
|
symbolSessionIDX,
|
|
signal.symbol //
|
|
);
|
|
|
|
//
|
|
// Chack Trade Session Validating ...
|
|
if (isSymbolSessionExists)
|
|
{
|
|
//
|
|
result = sessions[symbolSessionIDX].CanTrade();
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check Trade Permissions Based on SL(s) Count ...
|
|
bool hasRestrictions = restrictionsPeriod != XTRADE_RESTRICATION_NONE;
|
|
if (hasRestrictions)
|
|
{
|
|
//
|
|
// Only Can Check Restrictions when
|
|
// Symbol Info Exists ...
|
|
if (isSymbolInfoExists)
|
|
{
|
|
//
|
|
bool isTPAllowed = maxAllowedTPs == 0
|
|
? true
|
|
: symbols[symbolInfoIDX].managedTPs < maxAllowedTPs;
|
|
bool isSLAllowed = maxAllowedSLs == 0
|
|
? true
|
|
: symbols[symbolInfoIDX].managedSLs < maxAllowedSLs;
|
|
bool isTradeAllowed = maxAllowedTrades == 0
|
|
? true
|
|
: symbols[symbolInfoIDX].managedTrades < maxAllowedTrades;
|
|
|
|
//
|
|
result = isTPAllowed &&
|
|
isSLAllowed &&
|
|
isTradeAllowed;
|
|
}
|
|
|
|
//
|
|
// Report Error ...
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Execute Specified Signal ...
|
|
*
|
|
* @param signal: XSignal instance reference ...
|
|
* @param conditions: XCATBEAStrategyConditions instance reference ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool Execute(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = signal.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
signal //
|
|
);
|
|
result = !isExists;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT executionResult;
|
|
|
|
//
|
|
// Checking Signal For Execution ...
|
|
result = CheckSignal(
|
|
signal,
|
|
executionResult //
|
|
);
|
|
if (result)
|
|
{
|
|
//
|
|
result = trader.ExecuteSignal(
|
|
signal,
|
|
executionResult //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Alert Signal Execution ...
|
|
if (reportTrades)
|
|
{
|
|
//
|
|
string executionMessage = ToString(executionResult);
|
|
|
|
//
|
|
string message = NULL;
|
|
if (result)
|
|
{
|
|
message = signal.GetMessage("Executed");
|
|
}
|
|
else
|
|
{
|
|
message = signal.GetMessage("Failed due (" + executionMessage + ")");
|
|
}
|
|
|
|
//
|
|
alert.SendAlert(message);
|
|
}
|
|
|
|
//
|
|
if (result)
|
|
{
|
|
//
|
|
// Create Symbol Info Struct ...
|
|
XTradeSymbolInfo iSymbolInfo;
|
|
|
|
//
|
|
iSymbolInfo.trades = 1;
|
|
iSymbolInfo.managedTrades = 1;
|
|
iSymbolInfo.symbol = signal.symbol;
|
|
|
|
//
|
|
bool isLong = IsLong(signal.type);
|
|
if (isLong)
|
|
{
|
|
iSymbolInfo.longs = 1;
|
|
}
|
|
else
|
|
{
|
|
iSymbolInfo.shorts = 1;
|
|
}
|
|
|
|
//
|
|
// Fill Balance for Restrications ...
|
|
bool hasRestrictions = HasRestrictions(restrictionsPeriod);
|
|
if (hasRestrictions)
|
|
{
|
|
//
|
|
// Read Balance ...
|
|
double balance = trader.mAccount.GetBalance();
|
|
iSymbolInfo.managedBalance = balance;
|
|
iSymbolInfo.managedStart = TimeCurrent();
|
|
}
|
|
|
|
//
|
|
AddOrUpdateSymbolInfo(iSymbolInfo);
|
|
|
|
//
|
|
// Create Trade Struct ...
|
|
XTradeInfo iTrade;
|
|
|
|
//
|
|
ulong positionID = trader.GetLastOpenPositionTicket();
|
|
double commission = trader.GetPositionCommission(positionID);
|
|
|
|
//
|
|
iTrade.signal = signal;
|
|
iTrade.positionID = positionID;
|
|
iTrade.commission = commission;
|
|
iTrade.signaledAt = signal.time;
|
|
iTrade.executedAt = TimeCurrent();
|
|
iTrade.conditions = signal.conditions;
|
|
|
|
//
|
|
AddRef(
|
|
iTrade,
|
|
trades //
|
|
);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Retrieve Specified Positions ...
|
|
*
|
|
* @param positions: XPosition instance Reference Array, which holds Founded Positions ...
|
|
* @param symbol: string, Specified Symbol Name ...
|
|
* @param provider: string, Specified Provider Name ...
|
|
* @param period: ENUM_TIMEFRAMES, Specified Period ...
|
|
*
|
|
* @return ( int )
|
|
*/
|
|
int GetPositions(
|
|
XPosition &positions[],
|
|
string symbol,
|
|
string provider,
|
|
ENUM_TIMEFRAMES period //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
Clean(positions);
|
|
|
|
//
|
|
bool has =
|
|
IsValid(symbol) &&
|
|
IsValid(period) &&
|
|
IsValid(provider);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = trader.GetPositions(
|
|
positions,
|
|
symbol,
|
|
provider,
|
|
period,
|
|
NULL, // All Types ...
|
|
true, // Filter By Magic ...
|
|
true // Force Clean ...
|
|
);
|
|
|
|
//
|
|
result = ArraySize(positions);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Handle Signal Recieved ...
|
|
*
|
|
* @param signal: XSignal
|
|
*/
|
|
void HandleSignal(XSignal &signal)
|
|
{
|
|
//
|
|
// Validate Signal ...
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Signal ...
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT signalExecutionResult;
|
|
bool canExecute = CheckSignal(
|
|
signal,
|
|
signalExecutionResult //
|
|
);
|
|
if (canExecute) {
|
|
bool isExecuted = Execute(signal);
|
|
}
|
|
|
|
//
|
|
// Draw Signal Object ...
|
|
XCSignalObject *signalObj;
|
|
bool has = drawer.DrawSignal(
|
|
signal,
|
|
signalObj,
|
|
4 //
|
|
);
|
|
if (has)
|
|
{
|
|
mObjects.Add(signalObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Handle Position Take Profit ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleTP(const XDeal &deal)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
deal.positionId //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = deal.swap;
|
|
trades[idx].profit = deal.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].finalize = XTRADE_FINAL_TP;
|
|
|
|
//
|
|
// Save Trade ...
|
|
SaveWin(trades[idx]);
|
|
SaveTrade(trades[idx]);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "TP: " + ToXString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
// Handle Symbol Info ...
|
|
|
|
//
|
|
XTradeSymbolInfo iSymbolInfo;
|
|
|
|
//
|
|
iSymbolInfo.profits = 1;
|
|
iSymbolInfo.managedTPs = 1;
|
|
iSymbolInfo.symbol = trades[idx].signal.symbol;
|
|
iSymbolInfo.managedProfits = trades[idx].profit;
|
|
|
|
//
|
|
AddOrUpdateSymbolInfo(iSymbolInfo);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
/**
|
|
* Handle Position Stop Loss ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleSL(const XDeal &deal)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
deal.positionId //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = deal.swap;
|
|
trades[idx].profit = deal.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].finalize = XTRADE_FINAL_SL;
|
|
|
|
//
|
|
// Save Trade ...
|
|
SaveTrade(trades[idx]);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "SL: " + ToXString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
// Handle Symbol Info ...
|
|
|
|
//
|
|
bool isInProfit = trades[idx].profit > 0;
|
|
if (isInProfit)
|
|
{
|
|
SaveWin(trades[idx]);
|
|
}
|
|
else
|
|
{
|
|
SaveLost(trades[idx]);
|
|
}
|
|
|
|
//
|
|
XTradeSymbolInfo iSymbolInfo;
|
|
|
|
//
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
iSymbolInfo.profits = 1;
|
|
iSymbolInfo.managedTPs = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
iSymbolInfo.losses = 1;
|
|
iSymbolInfo.managedSLs = 1;
|
|
}
|
|
iSymbolInfo.symbol = trades[idx].signal.symbol;
|
|
iSymbolInfo.managedProfits = trades[idx].profit;
|
|
|
|
//
|
|
AddOrUpdateSymbolInfo(iSymbolInfo);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
/**
|
|
* Handle Position Force Close ...
|
|
*
|
|
* @param deal: XDeal instance reference ...
|
|
*/
|
|
void HandleForceClose(const XPosition &position)
|
|
{
|
|
//
|
|
// Check Ticket Exists or not ...
|
|
int idx = -1;
|
|
bool isExists = HasTrade(
|
|
idx,
|
|
position.ticket //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
bool isInProfit = position.profit > 0;
|
|
|
|
//
|
|
// Apply Required Data ...
|
|
trades[idx].swap = position.swap;
|
|
trades[idx].profit = position.profit;
|
|
trades[idx].finalizedAt = TimeCurrent();
|
|
trades[idx].commission = position.commission;
|
|
trades[idx].finalize = isInProfit
|
|
? XTRADE_FINAL_CLOSE_IN_PROFIT
|
|
: XTRADE_FINAL_CLOSE_IN_LOSE;
|
|
|
|
//
|
|
// Save Trade ...
|
|
SaveTrade(trades[idx]);
|
|
if (isInProfit)
|
|
{
|
|
SaveWin(trades[idx]);
|
|
}
|
|
else
|
|
{
|
|
SaveLost(trades[idx]);
|
|
}
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "Force Close " +
|
|
(isInProfit
|
|
? "Profit"
|
|
: "Loss") +
|
|
": " +
|
|
ToXString(trades[idx].profit);
|
|
HandleReportBalance(prefix);
|
|
|
|
//
|
|
// Handle Symbol Info ...
|
|
|
|
//
|
|
XTradeSymbolInfo iSymbolInfo;
|
|
|
|
//
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
iSymbolInfo.profits = 1;
|
|
iSymbolInfo.managedTPs = 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
iSymbolInfo.losses = 1;
|
|
iSymbolInfo.managedSLs = 1;
|
|
}
|
|
iSymbolInfo.symbol = trades[idx].signal.symbol;
|
|
iSymbolInfo.managedProfits = trades[idx].profit;
|
|
|
|
//
|
|
AddOrUpdateSymbolInfo(iSymbolInfo);
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
}
|
|
|
|
/**
|
|
* Handle Position Partially Closed Event ...
|
|
*
|
|
* @param ticket: ULONG ...
|
|
* @param profit: double ...
|
|
* @param comment: string ...
|
|
*/
|
|
void HandlePartiallyClosed(
|
|
const ulong ticket,
|
|
const double profit,
|
|
const string comment //
|
|
)
|
|
{
|
|
//
|
|
// Handle Balance Reporting ...
|
|
string prefix = "Partially Closed: " + ToXString(ticket);
|
|
|
|
//
|
|
// Handle Balance Reporting ...
|
|
HandleReportBalance(prefix);
|
|
}
|
|
|
|
/**
|
|
* Handle Force Close Trades ...
|
|
*/
|
|
bool HandleForceCloseTrades(
|
|
XPosition &positions[] //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = HasChild(positions);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string prefix = "Protector: ";
|
|
string comment = "Force Close ...";
|
|
|
|
//
|
|
int closedsCount = trader.Close(
|
|
positions,
|
|
comment //
|
|
);
|
|
result = IsValidSize(closedsCount);
|
|
if (result)
|
|
{
|
|
//
|
|
string message = prefix + "Force Close (" + ToXString(closedsCount) + ") Trades at Specified Time ...";
|
|
HandleReportProtector(message);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// TODO: Complete this ...
|
|
void HandleGuard()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Find Executed Trade by Providing Position Ticket ...
|
|
*
|
|
* @param index: int reference, holding founded item index ...
|
|
* @param ticket: ulong, Specified Position Ticket ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasTrade(
|
|
int &index,
|
|
ulong ticket //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result =
|
|
ticket > 0 &&
|
|
HasChild(trades);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XTradeInfo iTrade = trades[i];
|
|
|
|
//
|
|
result =
|
|
iTrade.HasSignal() &&
|
|
iTrade.IsExecuted() &&
|
|
iTrade.positionID == ticket;
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
|
|
//
|
|
iTrade.Clean();
|
|
break;
|
|
}
|
|
|
|
//
|
|
iTrade.Clean();
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Find Trade Item By Signal ...
|
|
*
|
|
* @param index: int reference, holding founded item index ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HasTrade(
|
|
int &index,
|
|
XSignal &signal //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result =
|
|
signal.IsValid() &&
|
|
HasChild(trades);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(trades);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XTradeInfo iTrade = trades[i];
|
|
|
|
//
|
|
result =
|
|
iTrade.HasSignal() &&
|
|
iTrade.signal.IsSameAs(signal);
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
|
|
//
|
|
iTrade.Clean();
|
|
break;
|
|
}
|
|
|
|
//
|
|
iTrade.Clean();
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Remove a Trade From List ...
|
|
*
|
|
* @param index: int ...
|
|
*/
|
|
void RemoveTrade(int index)
|
|
{
|
|
//
|
|
bool has =
|
|
HasChild(trades) &&
|
|
IsValidIndex(index) &&
|
|
index < ArraySize(trades);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
ArrayRemove(
|
|
trades,
|
|
index,
|
|
1 //
|
|
);
|
|
}
|
|
|
|
//
|
|
// Protected ...
|
|
protected:
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
string GetTradeFilePath(XTradeInfo &trade)
|
|
{
|
|
//
|
|
string fileName = trade.GetFileName();
|
|
|
|
//
|
|
return GetTradeFilePath(fileName);
|
|
}
|
|
|
|
//
|
|
string GetTradeFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result = collector
|
|
.GetFilePath("Trades" + "\\" + fileName);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetSignalFilePath(string fileName)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result = collector
|
|
.GetFilePath("Signals" + "\\" + fileName);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetWinsFilePath(XTradeInfo &trade)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result = collector
|
|
.GetFilePath("Conditions" + "\\" +
|
|
trade.signal.symbol + "\\" +
|
|
ToXString(trade.signal.period) + "_wins");
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetLostsFilePath(XTradeInfo &trade)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
result = collector
|
|
.GetFilePath("Conditions" + "\\" +
|
|
trade.signal.symbol + "\\" +
|
|
ToXString(trade.signal.period) + "_losts");
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string GetRestrictionsFilePath(string symbol)
|
|
{
|
|
//
|
|
string result = NULL;
|
|
|
|
//
|
|
if (!IsValid(symbol))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = collector
|
|
.GetFilePath("Restrictions" + "\\" + symbol);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void HandleReportBalance(string prefix = NULL)
|
|
{
|
|
//
|
|
if (!reportAfterTradesBalance)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
double balance = trader.mAccount.GetBalance();
|
|
string currency = trader.mAccount.GetCurrency();
|
|
|
|
//
|
|
string message =
|
|
(IsValid(prefix)
|
|
? prefix + " | "
|
|
: "") +
|
|
"Account Balance: " + ToXString(balance) + currency;
|
|
alert.SendAlert(message);
|
|
}
|
|
|
|
//
|
|
void HandleReportProtector(string message = NULL)
|
|
{
|
|
//
|
|
if (!reportProtector)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
alert.SendAlert(message);
|
|
}
|
|
|
|
//
|
|
// Save Trades ...
|
|
bool SaveTrade(
|
|
XTradeInfo &trade,
|
|
bool includeSummary = true,
|
|
bool includeSignal = true,
|
|
bool includeConditions = true //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mSaveTrades)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string filePath = GetTradeFilePath(trade);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = trade.ToString(
|
|
includeSummary,
|
|
includeSignal,
|
|
includeConditions //
|
|
);
|
|
|
|
//
|
|
result = collector.Save(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Save Signals ...
|
|
bool SaveSignal(XSignal &signal)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Signal Save is Enabled ...
|
|
if (!mSaveSignals)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Validation ...
|
|
if (!signal.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retrieve Signal File Name ...
|
|
string signalFileName = signal.GetFileName();
|
|
result = IsValid(signalFileName);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Retriev and Validate File Name ...
|
|
string filePath = GetSignalFilePath(signalFileName);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Generate Content ...
|
|
string content = "";
|
|
content +=
|
|
ToXString("----------------") + "\n" +
|
|
ToXString("Signal: ") + "\n" +
|
|
ToXString("----------------") + "\n" +
|
|
signal.ToString() + "\n";
|
|
content +=
|
|
ToXString("----------------") + "\n" +
|
|
ToXString("Conditions: ") + "\n" +
|
|
ToXString("----------------") + "\n" +
|
|
signal.conditions + "\n";
|
|
|
|
//
|
|
// Save Content to File ...
|
|
result = collector.Save(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
// Return Result ...
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool SaveWin(XTradeInfo &trade)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mSaveWins)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string filePath = GetWinsFilePath(trade);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Preparing Conditions Content ...
|
|
string content = trade.GetConditionsString();
|
|
|
|
//
|
|
result = collector.Append(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool SaveLost(XTradeInfo &trade)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mSaveLosts)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string filePath = GetLostsFilePath(trade);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Preparing Conditions Content ...
|
|
string content = trade.GetConditionsString();
|
|
|
|
//
|
|
result = collector.Append(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool SaveRestrictions(XTradeSymbolInfo &info)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
if (!mSaveRestrictions)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = info.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string filePath = GetRestrictionsFilePath(info.symbol);
|
|
result = IsValid(filePath);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string content = info.GetRestrictionDescription(restrictionsPeriod);
|
|
|
|
//
|
|
result = collector.Append(
|
|
filePath,
|
|
content //
|
|
);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Symbol Info ...
|
|
|
|
//
|
|
bool FindSymbolIndex(
|
|
int &index,
|
|
XTradeSymbolInfo &item //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result = item.IsValid();
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = HasChild(symbols);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
index = FindIndex(
|
|
item,
|
|
symbols //
|
|
);
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool FindSymbolIndex(
|
|
int &index,
|
|
string symbol //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result = IsValid(symbol) &&
|
|
HasChild(symbols);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(symbols);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = symbols[i].symbol == symbol;
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void AddOrUpdateSymbolInfo(XTradeSymbolInfo &item)
|
|
{
|
|
//
|
|
bool has = item.IsValid();
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
int index = -1;
|
|
bool isExists = FindSymbolIndex(
|
|
index,
|
|
item //
|
|
);
|
|
if (!isExists)
|
|
{
|
|
//
|
|
AddRef(
|
|
item,
|
|
symbols //
|
|
);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
symbols[index].longs += item.longs;
|
|
symbols[index].shorts += item.shorts;
|
|
symbols[index].trades += item.trades;
|
|
symbols[index].losses += item.losses;
|
|
symbols[index].profits += item.profits;
|
|
symbols[index].managedSLs += item.managedSLs;
|
|
symbols[index].managedTPs += item.managedTPs;
|
|
symbols[index].managedTrades += item.managedTrades;
|
|
symbols[index].managedProfits += item.managedProfits;
|
|
|
|
//
|
|
// Handle Remove on Managed SL based On Managed TP ...
|
|
if (item.managedTPs > 0)
|
|
{
|
|
//
|
|
symbols[index].managedSLs -= item.managedTPs;
|
|
if (symbols[index].managedSLs < 0)
|
|
{
|
|
symbols[index].managedSLs = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handle Remove on Managed TP based On Managed SL ...
|
|
if (item.managedSLs > 0)
|
|
{
|
|
//
|
|
symbols[index].managedTPs -= item.managedSLs;
|
|
if (symbols[index].managedTPs < 0)
|
|
{
|
|
symbols[index].managedTPs = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
void ResetSymbolsRestrictions()
|
|
{
|
|
//
|
|
bool has = HasChild(symbols);
|
|
if (has)
|
|
{
|
|
//
|
|
string prefix = "Protector: ";
|
|
|
|
//
|
|
datetime start = TimeCurrent();
|
|
double balance = trader.mAccount.GetBalance();
|
|
bool hasRestrictions = HasRestrictions(restrictionsPeriod);
|
|
if (!hasRestrictions)
|
|
{
|
|
//
|
|
balance = 0;
|
|
start = NULL;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(symbols);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XTradeSymbolInfo iInfo = symbols[i];
|
|
if (reportRestrictions)
|
|
{
|
|
//
|
|
string message = iInfo.GetRestrictionDescription(restrictionsPeriod);
|
|
alert.SendAlert(message);
|
|
}
|
|
SaveRestrictions(iInfo);
|
|
|
|
//
|
|
symbols[i].managedSLs = 0;
|
|
symbols[i].managedTPs = 0;
|
|
symbols[i].managedTrades = 0;
|
|
symbols[i].managedProfits = 0;
|
|
symbols[i].managedStart = start;
|
|
symbols[i].managedBalance = balance;
|
|
|
|
//
|
|
iInfo.Clean();
|
|
}
|
|
|
|
//
|
|
string message = prefix + "Reset Symbols Restrictions";
|
|
HandleReportProtector(message);
|
|
}
|
|
}
|
|
|
|
//
|
|
void HandleRestrictions()
|
|
{
|
|
//
|
|
bool isNewDay = timeTracker.IsNewDay();
|
|
bool isNewHour = timeTracker.IsNewHour();
|
|
bool isNewWeek = timeTracker.IsNewWeek();
|
|
bool isNewMonth = timeTracker.IsNewMonth();
|
|
|
|
//
|
|
// Reset Force Closed Flag on new Day Starts ...
|
|
if (isNewDay)
|
|
{
|
|
isForceCloseAtTime = false;
|
|
}
|
|
|
|
//
|
|
bool canResetRestrictions =
|
|
(
|
|
//
|
|
// Hourly ...
|
|
(isNewHour &&
|
|
restrictionsPeriod == XTRADE_RESTRICATION_HOURLY)
|
|
//
|
|
||
|
|
//
|
|
// Daily ...
|
|
(isNewDay &&
|
|
restrictionsPeriod == XTRADE_RESTRICATION_DAILY)
|
|
//
|
|
||
|
|
//
|
|
// Weekly ...
|
|
(isNewWeek &&
|
|
restrictionsPeriod == XTRADE_RESTRICATION_WEEKLY)
|
|
//
|
|
||
|
|
//
|
|
// Monthly ...
|
|
(isNewMonth &&
|
|
restrictionsPeriod == XTRADE_RESTRICATION_MONTHLY)
|
|
//
|
|
)
|
|
//
|
|
;
|
|
if (canResetRestrictions)
|
|
{
|
|
ResetSymbolsRestrictions();
|
|
}
|
|
}
|
|
|
|
//
|
|
// Symbol Session ...
|
|
|
|
//
|
|
bool FindSymbolSession(
|
|
int &index,
|
|
string symbol //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
index = -1;
|
|
|
|
//
|
|
result = IsValid(symbol) &&
|
|
HasChild(sessions);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(sessions);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
result = sessions[i].symbol == symbol;
|
|
if (result)
|
|
{
|
|
//
|
|
index = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
result = IsValidIndex(index);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
void AddTradingSession(
|
|
XTradeSession &session,
|
|
XSymbolSessions &symbolSession //
|
|
)
|
|
{
|
|
//
|
|
bool has = session.IsValid() &&
|
|
symbolSession.IsValid();
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Session Exists in Symbol Session ...
|
|
bool isExists = false;
|
|
if (HasChild(symbolSession.sessions))
|
|
{
|
|
//
|
|
int count = ArraySize(symbolSession.sessions);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
isExists = symbolSession.sessions[i].end == session.end &&
|
|
symbolSession.sessions[i].start == session.start;
|
|
if (isExists)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
if (!isExists)
|
|
{
|
|
//
|
|
AddRef(
|
|
session,
|
|
symbolSession.sessions //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
CArrayObj mObjects;
|
|
|
|
//
|
|
bool allowTrade;
|
|
bool allowLongs;
|
|
bool allowShorts;
|
|
|
|
//
|
|
string forceCloseAt;
|
|
bool isForceCloseAtTime;
|
|
|
|
//
|
|
int maxAllowedTPs;
|
|
int maxAllowedSLs;
|
|
int maxAllowedTrades;
|
|
ENUM_XTRADE_RESTRICTION_PERIOD restrictionsPeriod;
|
|
|
|
//
|
|
bool mSaveWins;
|
|
bool mSaveLosts;
|
|
bool mSaveTrades;
|
|
bool mSaveSignals;
|
|
bool mSaveRestrictions;
|
|
|
|
//
|
|
bool reportTrades;
|
|
bool reportSignals;
|
|
bool reportProtector;
|
|
bool reportRestrictions;
|
|
bool reportAfterTradesBalance;
|
|
|
|
//
|
|
XTradeInfo trades[];
|
|
XTradeSymbolInfo symbols[];
|
|
XSymbolSessions sessions[];
|
|
}; |