2429 lines
54 KiB
Plaintext
2429 lines
54 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-target.class.mq5"
|
|
#include "../Classes/x-saherelm.x-poi.drawer.class.mq5"
|
|
#include "../Classes/x-saherelm.x-trade.class.mq5"
|
|
#include "../Classes/x-saherelm.x-volume.class.mq5"
|
|
|
|
//
|
|
// Implementation ...
|
|
class XCTradeManager : public XCBase
|
|
{
|
|
//
|
|
// Public ...
|
|
public:
|
|
//
|
|
// Props ...
|
|
XCAlert *alert;
|
|
XCGuard *guard;
|
|
XCTrade *trader;
|
|
XCTarget *target;
|
|
XCVolume *volume;
|
|
XTimeTracker timeTracker;
|
|
XCDataCollector *collector;
|
|
|
|
//
|
|
XCPOIDrawer *drawer;
|
|
|
|
//
|
|
TCheckForGuard checkForGuardEventListener;
|
|
|
|
//
|
|
// Constructor ...
|
|
XCTradeManager(
|
|
XCAlert *_alert,
|
|
XCTrade *_trader,
|
|
XCVolume *_volume,
|
|
XCGuard *_guard,
|
|
XCTarget *_target //
|
|
)
|
|
{
|
|
//
|
|
alert = _alert;
|
|
guard = _guard;
|
|
volume = _volume;
|
|
trader = _trader;
|
|
target = _target;
|
|
|
|
//
|
|
drawer = new XCPOIDrawer();
|
|
|
|
//
|
|
allowTrade = true;
|
|
allowLongs = true;
|
|
allowShorts = true;
|
|
|
|
//
|
|
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();
|
|
|
|
//
|
|
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 SetSymbolConfigurations(string symbolConfiguration)
|
|
{
|
|
//
|
|
int count = 0;
|
|
bool has = false;
|
|
|
|
//
|
|
// If Not Provided ...
|
|
has = IsSpecifiedValid(symbolConfiguration);
|
|
if (!has)
|
|
{
|
|
//
|
|
AddDefaultSymbolConfiguration();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Try to Parse Data ...
|
|
string symbolData[];
|
|
count = SplitContent(
|
|
symbolData,
|
|
symbolConfiguration,
|
|
"]" //
|
|
);
|
|
has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
//
|
|
AddDefaultSymbolConfiguration();
|
|
|
|
//
|
|
SpecifiedClean(symbolData);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Loop through Exists Symbol Data's ...
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
string iData = symbolData[i];
|
|
|
|
//
|
|
// Extract Data Parts ...
|
|
string iParts[];
|
|
int iPartsCount = SplitContent(
|
|
iParts,
|
|
iData,
|
|
"|" //
|
|
);
|
|
has = IsValidSize(iPartsCount);
|
|
if (has)
|
|
{
|
|
//
|
|
// Determines Required Parts ...
|
|
bool hasName = iPartsCount >= 1;
|
|
bool hasSymbol = iPartsCount >= 2;
|
|
bool hasPeriod = iPartsCount >= 3;
|
|
bool hasSessionsDescriptor = iPartsCount >= 4;
|
|
bool hasMaxAllowedSpred = iPartsCount >= 5;
|
|
bool hasMaxAllowedPositios = iPartsCount >= 6;
|
|
bool hasRestrictionDescriptor = iPartsCount >= 7;
|
|
|
|
//
|
|
// Parts IDX ...
|
|
int partsIDX = 0;
|
|
|
|
//
|
|
// Extract Name ...
|
|
string iName =
|
|
hasName
|
|
? iParts[partsIDX]
|
|
: "";
|
|
iName = EscapeString(iName);
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Symbol ...
|
|
string iSymbol =
|
|
hasSymbol
|
|
? iParts[partsIDX]
|
|
: _Symbol;
|
|
iSymbol = EscapeString(iSymbol);
|
|
iSymbol = NormalizeSymbol(iSymbol);
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Period ...
|
|
ENUM_TIMEFRAMES iPeriod =
|
|
hasPeriod
|
|
? ToPeriod(EscapeString(iParts[partsIDX]))
|
|
: _Period;
|
|
iPeriod = NormalizePeriod(iPeriod);
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Sessions Descriptor ...
|
|
string iSessionsDescriptor =
|
|
hasSessionsDescriptor
|
|
? iParts[partsIDX]
|
|
: "";
|
|
iSessionsDescriptor = EscapeString(iSessionsDescriptor);
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Max Allowed Sspread ...
|
|
double iMaxAllowedSpread =
|
|
hasMaxAllowedSpred
|
|
? (double)(EscapeString(iParts[partsIDX]))
|
|
: 25;
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Max Allowedd Positions ...
|
|
int iMaxAllowedPositions =
|
|
hasMaxAllowedPositios
|
|
? (int)(EscapeString(iParts[partsIDX]))
|
|
: 5;
|
|
partsIDX++;
|
|
|
|
//
|
|
// Extract Restrictions Descriptor ...
|
|
string iRestrictionsDescriptor =
|
|
hasRestrictionDescriptor
|
|
? iParts[partsIDX]
|
|
: "";
|
|
iRestrictionsDescriptor = EscapeString(iRestrictionsDescriptor);
|
|
partsIDX++;
|
|
|
|
//
|
|
XSymbolTradeConfig iConfig;
|
|
|
|
//
|
|
// Initialize Config ...
|
|
has = iConfig.Init(
|
|
iName,
|
|
iSymbol,
|
|
iPeriod,
|
|
iSessionsDescriptor,
|
|
iMaxAllowedSpread,
|
|
iMaxAllowedPositions,
|
|
iRestrictionsDescriptor //
|
|
);
|
|
if (has)
|
|
{
|
|
AddOrUpdateSymbolConfiguration(iConfig);
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
iConfig.Clean();
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
SpecifiedClean(iParts);
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
SpecifiedClean(symbolData);
|
|
}
|
|
|
|
//
|
|
int FillSymbolConfigurations(XSymbolTradeConfig &dest[])
|
|
{
|
|
//
|
|
SpecifiedClean(dest);
|
|
|
|
//
|
|
Copy(
|
|
symbolTradeConfigs,
|
|
dest //
|
|
);
|
|
|
|
//
|
|
int result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
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;
|
|
}
|
|
|
|
//
|
|
// Tools and Helpers ...
|
|
|
|
/**
|
|
* Do All Position Managing Senaros ...
|
|
*/
|
|
void Manage()
|
|
{
|
|
//
|
|
// Handle Restrictions ...
|
|
HandleRestrictions();
|
|
|
|
//
|
|
// Managing Targets ...
|
|
target.Manage();
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
XGuard guards[];
|
|
int guardsCount = CheckForGuard(guards);
|
|
bool hasGuard = IsValidSize(guardsCount);
|
|
if (hasGuard)
|
|
{
|
|
//
|
|
guard.DoGuards(guards);
|
|
}
|
|
|
|
//
|
|
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 ...
|
|
|
|
//
|
|
// Handle Force Close At Time ...
|
|
has = HandleForceCloseAtTime();
|
|
if (has)
|
|
{
|
|
//
|
|
Clean(ownPosition);
|
|
Clean(ownLongPosition);
|
|
Clean(ownShortPosition);
|
|
|
|
//
|
|
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 Can Analyse Market based on Configurations ...
|
|
*
|
|
* @param symbol: Symbol
|
|
* @param period: Period
|
|
* @param time: Analysing Time
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool CanAnalyse(
|
|
string symbol,
|
|
ENUM_TIMEFRAMES period,
|
|
datetime time //
|
|
)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Finding Symbol Configuration Index ...
|
|
int idx = FindSymbolConfigurationIndex(
|
|
symbol,
|
|
period //
|
|
);
|
|
|
|
//
|
|
// If Not Exists ...
|
|
if (!IsValidIndex(idx))
|
|
{
|
|
//
|
|
result = true;
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// If Exists ...
|
|
|
|
//
|
|
// Check Session is Active ...
|
|
result = symbolTradeConfigs[idx].CanTrade(time);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Symbol Restrictions ...
|
|
result = symbolTradeConfigs[idx].IsRestricted(time);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
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 ...
|
|
result = AllowTrade(signal.type);
|
|
if (!result)
|
|
{
|
|
//
|
|
result = false;
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Sessions and Restrictions ...
|
|
result = CanAnalyse(
|
|
signal.symbol,
|
|
signal.period,
|
|
signal.time //
|
|
);
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Check Signal Spread ...
|
|
int idx = FindSymbolConfigurationIndex(
|
|
signal.symbol,
|
|
signal.period //
|
|
);
|
|
if (IsValidIndex(idx))
|
|
{
|
|
//
|
|
double spread = GetSpread(signal.symbol);
|
|
result =
|
|
symbolTradeConfigs[idx].maxAllowedSpread > 0
|
|
? spread <= symbolTradeConfigs[idx].maxAllowedSpread
|
|
: true;
|
|
if (!result)
|
|
{
|
|
//
|
|
executionResult = X_SIGNAL_EXECUTION_FAILED_SPREAD;
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Checking Max Allowed Positions ...
|
|
if (symbolTradeConfigs[idx].maxAllowedPositions > 0)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
ENUM_X_POSITION_TYPES mType = IsLong(signal.type)
|
|
? X_POSITION_TYPE_LONG
|
|
: X_POSITION_TYPE_SHORT;
|
|
int positionsCount = trader.GetPositions(
|
|
positions,
|
|
symbolTradeConfigs[idx].symbol,
|
|
NULL, // All Providers ...
|
|
symbolTradeConfigs[idx].period,
|
|
mType,
|
|
true, // Filter By Magic ...
|
|
true // Force Clean ...
|
|
);
|
|
result = !IsValidSize(positionsCount) ||
|
|
(IsValidSize(positionsCount) &&
|
|
positionsCount < symbolTradeConfigs[idx].maxAllowedPositions);
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
SpecifiedClean(positions);
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check Allow Trade or not ..
|
|
*
|
|
* @param type: ENUM_POSITIO_TYPE
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool AllowTrade(ENUM_POSITION_TYPE type)
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
result = allowTrade;
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result =
|
|
(IsLong(type) && allowLongs) ||
|
|
(IsShort(type) && allowShorts);
|
|
|
|
//
|
|
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 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 ...
|
|
bool isExecuted = false;
|
|
ENUM_X_SIGNAL_EXECUTION_RESULT signalExecutionResult;
|
|
bool canExecute = CheckSignal(
|
|
signal,
|
|
signalExecutionResult //
|
|
);
|
|
if (canExecute)
|
|
{
|
|
//
|
|
HandleSignalVolume(signal);
|
|
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 Config ...
|
|
|
|
//
|
|
int configIdx = FindSymbolConfigurationIndex(
|
|
trades[idx].signal.symbol,
|
|
trades[idx].signal.period //
|
|
);
|
|
isExists = IsValidIndex(configIdx);
|
|
if (isExists)
|
|
{
|
|
//
|
|
symbolTradeConfigs[configIdx].profits += 1;
|
|
symbolTradeConfigs[configIdx].managedTPs += 1;
|
|
symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
|
|
}
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
|
|
//
|
|
target.HandleOnTakeProfitTriggered(deal);
|
|
}
|
|
|
|
/**
|
|
* 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]);
|
|
}
|
|
|
|
//
|
|
// Handle Symbol Config ...
|
|
|
|
//
|
|
int configIdx = FindSymbolConfigurationIndex(
|
|
trades[idx].signal.symbol,
|
|
trades[idx].signal.period //
|
|
);
|
|
isExists = IsValidIndex(configIdx);
|
|
if (isExists)
|
|
{
|
|
//
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
symbolTradeConfigs[configIdx].profits += 1;
|
|
symbolTradeConfigs[configIdx].managedTPs += 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
symbolTradeConfigs[configIdx].losses += 1;
|
|
symbolTradeConfigs[configIdx].managedSLs += 1;
|
|
}
|
|
|
|
//
|
|
symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
|
|
}
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
|
|
//
|
|
target.HandleOnStopLossTriggered(deal);
|
|
}
|
|
|
|
/**
|
|
* 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 Config ...
|
|
|
|
//
|
|
int configIdx = FindSymbolConfigurationIndex(
|
|
trades[idx].signal.symbol,
|
|
trades[idx].signal.period //
|
|
);
|
|
isExists = IsValidIndex(configIdx);
|
|
if (isExists)
|
|
{
|
|
//
|
|
if (isInProfit)
|
|
{
|
|
//
|
|
symbolTradeConfigs[configIdx].profits += 1;
|
|
symbolTradeConfigs[configIdx].managedTPs += 1;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
symbolTradeConfigs[configIdx].losses += 1;
|
|
symbolTradeConfigs[configIdx].managedSLs += 1;
|
|
}
|
|
|
|
//
|
|
symbolTradeConfigs[configIdx].managedProfits += trades[idx].profit;
|
|
}
|
|
|
|
//
|
|
RemoveTrade(idx);
|
|
|
|
//
|
|
target.HandleOnPositionForceClosed(
|
|
position.ticket,
|
|
position,
|
|
position.comment //
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Handle All Symbols Config Force Close At Time ...
|
|
*
|
|
* @return ( bool )
|
|
*/
|
|
bool HandleForceCloseAtTime()
|
|
{
|
|
//
|
|
bool result = false;
|
|
|
|
//
|
|
// Check Symbol Config Exists ...
|
|
result = HasChild(symbolTradeConfigs);
|
|
if (!result)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
result = false;
|
|
int count = ArraySize(symbolTradeConfigs);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Check Can Force Closed At ...
|
|
bool has = IsSpecifiedValid(symbolTradeConfigs[i].closeAllOpenTradesAt);
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Parse Date ...
|
|
datetime forceCloseTime = ParseDateTimeFromTimeString(symbolTradeConfigs[i].closeAllOpenTradesAt);
|
|
has = IsTimeInRange(
|
|
TimeCurrent(),
|
|
forceCloseTime //
|
|
) &&
|
|
!symbolTradeConfigs[i].isForceCloseAtTime;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Selecting Positions ...
|
|
XTradeInfo iTrades[];
|
|
int iTradesCount = FillSymbolConfigurationTrades(
|
|
symbolTradeConfigs[i],
|
|
iTrades //
|
|
);
|
|
has = IsValidSize(iTradesCount);
|
|
if (!has)
|
|
{
|
|
//
|
|
SpecifiedClean(iTrades);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Converts Trades to Positions ...
|
|
XPosition iPositions[];
|
|
int iPositionsCount = ToXPositions(
|
|
iTrades,
|
|
iPositions //
|
|
);
|
|
has = IsValidSize(iPositionsCount);
|
|
if (!has)
|
|
{
|
|
//
|
|
SpecifiedClean(iTrades);
|
|
SpecifiedClean(iPositions);
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Do Force Closing ...
|
|
has = HandleForceCloseTrades(iPositions);
|
|
if (has && !result)
|
|
{
|
|
result = true;
|
|
}
|
|
|
|
//
|
|
// Cleanup Resources ...
|
|
SpecifiedClean(iTrades);
|
|
SpecifiedClean(iPositions);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* 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 Guard Events ...
|
|
*
|
|
* @param action: ENUM_X_GUARD_ACTIONS
|
|
* @param positions: XPosition[]
|
|
*/
|
|
void HandleGuardEvent(
|
|
ENUM_X_GUARD_ACTIONS action,
|
|
XPosition &positions[] //
|
|
)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Handle Signal Executed Event ...
|
|
*
|
|
* @param signal: XSignal, reference to Executed Signal ...
|
|
*/
|
|
void HandleSignalExecuted(XSignal &signal)
|
|
{
|
|
target.AddExecutedSignal(signal);
|
|
}
|
|
|
|
//
|
|
// 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);
|
|
}
|
|
|
|
/**
|
|
* Handle All Exists Symbol Configurations Restrictions ...
|
|
*/
|
|
void HandleRestrictions()
|
|
{
|
|
//
|
|
// Count Symbol Configurations ...
|
|
int count = ArraySize(symbolTradeConfigs);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Handle Restrictions ...
|
|
datetime cTime = TimeCurrent();
|
|
bool isNewDay = timeTracker.IsNewDay();
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
// Reset Force Close At Time ...
|
|
if (isNewDay)
|
|
{
|
|
symbolTradeConfigs[i].isForceCloseAtTime = false;
|
|
}
|
|
|
|
//
|
|
// Handle Restrictions ...
|
|
symbolTradeConfigs[i].HandleRestrictions(cTime);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Apply Signal Volume ...
|
|
*
|
|
* @param signal: XSignal
|
|
*/
|
|
void HandleSignalVolume(XSignal &signal)
|
|
{
|
|
//
|
|
// Validate ...
|
|
if (!signal.IsValid())
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Handle Volume Management ...
|
|
double iVolume = volume.CalculateVolume(signal);
|
|
bool has = iVolume > X_MIN_VOLUME &&
|
|
iVolume != signal.volume;
|
|
if (has)
|
|
{
|
|
signal.volume = iVolume;
|
|
}
|
|
|
|
//
|
|
// Here we Can Implement Custom Signalling Volume Management
|
|
// based on Different Symbol Configurations ...
|
|
}
|
|
|
|
/**
|
|
* 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 count = ArraySize(positions);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
HandleForceClose(positions[i]);
|
|
}
|
|
|
|
//
|
|
result = IsValidSize(count);
|
|
if (result)
|
|
{
|
|
//
|
|
string message = prefix + "Force Close (" + ToXString(count) + ") Trades at Specified Time ...";
|
|
HandleReportProtector(message);
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Check For Guards ...
|
|
*
|
|
* @param dest: XGuard[]
|
|
*/
|
|
int CheckForGuard(XGuard &dest[])
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
// Cleanup Dest ...
|
|
SpecifiedClean(dest);
|
|
|
|
//
|
|
// Check For Guards ...
|
|
if (checkForGuardEventListener != NULL)
|
|
{
|
|
//
|
|
XPosition positions[];
|
|
trader.GetPositions(positions);
|
|
result = checkForGuardEventListener(dest, positions, 0);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
//
|
|
// Private ...
|
|
private:
|
|
//
|
|
// Props ...
|
|
|
|
//
|
|
CArrayObj mObjects;
|
|
|
|
//
|
|
bool allowTrade;
|
|
bool allowLongs;
|
|
bool allowShorts;
|
|
|
|
//
|
|
bool mSaveWins;
|
|
bool mSaveLosts;
|
|
bool mSaveTrades;
|
|
bool mSaveSignals;
|
|
bool mSaveRestrictions;
|
|
|
|
//
|
|
bool reportTrades;
|
|
bool reportSignals;
|
|
bool reportProtector;
|
|
bool reportRestrictions;
|
|
bool reportAfterTradesBalance;
|
|
|
|
//
|
|
XTradeInfo trades[];
|
|
XSymbolTradeConfig symbolTradeConfigs[];
|
|
|
|
/**
|
|
* 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 //
|
|
);
|
|
}
|
|
|
|
//
|
|
void AddOrUpdateSymbolConfiguration(XSymbolTradeConfig &config)
|
|
{
|
|
//
|
|
// Check Validation ...
|
|
bool has = config.IsValid();
|
|
if (!has)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Check Index ...
|
|
int idx = FindSymbolConfigurationIndex(config);
|
|
has = IsValidIndex(idx);
|
|
if (has)
|
|
{
|
|
//
|
|
// Update Exists ...
|
|
symbolTradeConfigs[idx] = config;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Add New ...
|
|
AddRef(
|
|
config,
|
|
symbolTradeConfigs //
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
void AddDefaultSymbolConfiguration()
|
|
{
|
|
//
|
|
// Define new Configuration ...
|
|
XSymbolTradeConfig iConfig;
|
|
|
|
//
|
|
// Check Initialization ...
|
|
bool has = iConfig.Init(
|
|
"Default",
|
|
_Symbol,
|
|
_Period //
|
|
);
|
|
|
|
//
|
|
// Add Or Update ...
|
|
if (has)
|
|
{
|
|
AddOrUpdateSymbolConfiguration(iConfig);
|
|
}
|
|
|
|
//
|
|
// Clean Resources ...
|
|
iConfig.Clean();
|
|
}
|
|
|
|
//
|
|
int FindSymbolConfigurationIndex(XSymbolTradeConfig &config)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
if (!config.IsValid() || !SpecifiedHasChild(symbolTradeConfigs))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(symbolTradeConfigs);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
if (symbolTradeConfigs[i].IsSame(config))
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int FillSymbolConfigurationTrades(
|
|
XSymbolTradeConfig &config,
|
|
XTradeInfo &dest[] //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
SpecifiedClean(dest);
|
|
|
|
//
|
|
// Check Trades ...
|
|
int count = ArraySize(trades);
|
|
if (!IsValidSize(count))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Validate Config ...
|
|
if (!config.IsValid())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Find Config ...
|
|
int idx = FindSymbolConfigurationIndex(config);
|
|
if (!IsValidIndex(idx))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
bool has = false;
|
|
string symbol = symbolTradeConfigs[idx].symbol;
|
|
ENUM_TIMEFRAMES period = symbolTradeConfigs[idx].period;
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
has =
|
|
trades[i].signal.symbol == symbol &&
|
|
trades[i].signal.period == period &&
|
|
trades[i].positionID > 0;
|
|
if (!has)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
AddRef(
|
|
trades[i],
|
|
dest //
|
|
);
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int FindSymbolConfigurationIndex(
|
|
string symbol,
|
|
ENUM_TIMEFRAMES period //
|
|
)
|
|
{
|
|
//
|
|
int result = -1;
|
|
|
|
//
|
|
// Validate Args ...
|
|
if (
|
|
!IsSpecifiedValid(symbol) ||
|
|
!IsSpecifiedValid(period) ||
|
|
!HasChild(symbolTradeConfigs))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
int count = ArraySize(symbolTradeConfigs);
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
if (symbolTradeConfigs[i].symbol == symbol &&
|
|
symbolTradeConfigs[i].period == period)
|
|
{
|
|
//
|
|
result = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
// Tools ...
|
|
|
|
//
|
|
int ToXPositions(
|
|
XTradeInfo &source[],
|
|
XPosition &dest[] //
|
|
)
|
|
{
|
|
//
|
|
int result = 0;
|
|
|
|
//
|
|
SpecifiedClean(dest);
|
|
|
|
//
|
|
int count = ArraySize(source);
|
|
bool has = IsValidSize(count);
|
|
if (!has)
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
for (int i = 0; i < count; i++)
|
|
{
|
|
//
|
|
XPosition iPosition;
|
|
has = trader.GetPosition(
|
|
source[i].positionID,
|
|
iPosition);
|
|
if (has)
|
|
{
|
|
//
|
|
AddRef(
|
|
iPosition,
|
|
dest //
|
|
);
|
|
}
|
|
|
|
//
|
|
iPosition.Clean();
|
|
}
|
|
|
|
//
|
|
result = ArraySize(dest);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
//
|
|
string EscapeString(string value)
|
|
{
|
|
//
|
|
string result = value;
|
|
|
|
//
|
|
if (!IsSpecifiedValid(value))
|
|
{
|
|
return result;
|
|
}
|
|
|
|
//
|
|
StringReplace(
|
|
result,
|
|
"[",
|
|
"" //
|
|
);
|
|
|
|
//
|
|
StringReplace(
|
|
result,
|
|
"]",
|
|
"" //
|
|
);
|
|
|
|
//
|
|
StringReplace(
|
|
result,
|
|
"|",
|
|
"" //
|
|
);
|
|
|
|
//
|
|
result = Trim(result);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
}; |