Files
MQL5Data/Classes/x-saherelm.x-trade-manager.class.mq5
T
2025-10-07 18:09:38 +03:30

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;
}
};