Files
xMQL5/BKPS/14040218 XCAEA/XCAEA/Classes/xcaea.x-trade.manager.class.mq5
T
2025-05-08 14:17:59 +03:30

1420 lines
32 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCXCAEATradeManager
// Description: provides all Requirements for Managing Trades ...
//
//
// 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-poi.drawer.class.mq5"
#include "../../Classes/x-saherelm.x-trade.class.mq5"
#include "../Classes/xcaea.x-data.collector.class.mq5"
#include "../Libraries/xcaea.signaller.lib.mq5"
//
// Definitions ...
//
enum ENUM_XCAEA_TRADE_FINALIZATION
{
XCAEA_TRADE_FINAL_NONE, // None
XCAEA_TRADE_FINAL_TP, // TP
XCAEA_TRADE_FINAL_SL, // SL
XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT, // Profit
XCAEA_TRADE_FINAL_CLOSE_IN_LOSE, // Lost
};
string ToString(ENUM_XCAEA_TRADE_FINALIZATION value)
{
//
string result = NULL;
//
result = EnumToString(value);
//
return result;
}
//
struct XCAEATrade
{
//
// Props ...
double swap;
double profit;
XSignal signal;
ulong positionID;
double commission;
XCAEAStrategyConditions conditions;
ENUM_XCAEA_TRADE_FINALIZATION finalize;
//
datetime signaledAt;
datetime executedAt;
datetime finalizedAt;
//
// Constructor ...
XCAEATrade()
{
Clean();
}
//
// Tools ...
/**
* Cleanup Model ...
*/
void Clean()
{
//
swap = 0;
profit = 0;
signal.Clean();
commission = 0;
positionID = 0;
conditions.Clean();
finalize = XCAEA_TRADE_FINAL_NONE;
//
signaledAt = NULL;
executedAt = NULL;
finalizedAt = NULL;
//
ZeroMemory(this);
}
/**
* Check Has Signal ...
*
* @return ( bool )
*/
bool HasSignal()
{
//
bool result = false;
//
result = signal.IsValid();
//
return result;
}
/**
* Check Signal Executed or Not ...
*
* @return ( bool )
*/
bool IsExecuted()
{
//
bool result = false;
//
result = positionID > 0;
//
return result;
}
/**
* Check a Trade is Finalized or not ...
*
* @return ( bool )
*/
bool IsFinalized()
{
//
bool result = false;
//
result = HasSignal() &&
IsExecuted() &&
finalize != XCAEA_TRADE_FINAL_NONE;
//
return result;
}
/**
* Generate Summary Info ...
*
* @return ( string )
*/
string Summary()
{
//
string result = NULL;
//
result =
//
ToString("Swap: ", swap) +
ToString("Profit: ", profit) +
ToString("Commission: ", commission) +
ToString("Finalize: ", ToString(finalize)) +
ToString("PositionID: ", positionID) +
ToString("----------------") + "\n" +
ToString("Signaled At: ", ToFormatString(signaledAt)) +
ToString("Executed At: ", ToFormatString(executedAt)) +
ToString("Finalized At: ", ToFormatString(finalizedAt))
//
;
//
return result;
}
/**
* Get Data Collection File Name ...
*
* @return ( string )
*/
string GetFileName()
{
//
string result = NULL;
//
bool hasSignal = HasSignal();
bool isExecuted = IsExecuted();
bool isFinalized = IsFinalized();
if (!hasSignal && !isExecuted && !isFinalized)
{
return result;
}
//
result =
//
signal.symbol + "\\" +
ToString(signal.type) + "\\" +
(profit >= 0 ? "Profit" : "Loss") + "\\" +
ToString(positionID) + "_" +
ToString(signal.period) + "_" +
ToFormatString(signaledAt)
//
;
//
return result;
}
/**
* Converts Model to String Representation ...
*
* @param onlySignals: Boolean, Specified Represent Only Signal or not ...
*
* @return ( string )
*/
string ToString(
bool includeSummary = true,
bool includeSignal = true,
bool includeConditions = true //
)
{
//
string result = NULL;
//
// Generating Model Summary ...
string summary = Summary();
//
// Generating Signal Summary ...
string signalSummary = signal.ToString();
//
// Generating Conditions Summary ...
string conditionsSummary = conditions
.conditions
.GenerateSummary(
true,
true,
true,
false // Ignore False Conditions ...
);
//
if (includeSummary)
{
//
result +=
ToString("----------------") + "\n" +
ToString("Summary: ") + "\n" +
ToString("----------------") + "\n" +
summary + "\n";
}
//
if (includeSignal)
{
//
result +=
ToString("----------------") + "\n" +
ToString("Signal: ") + "\n" +
ToString("----------------") + "\n" +
signalSummary + "\n";
}
//
if (includeConditions)
{
//
result +=
ToString("----------------") + "\n" +
ToString("Conditions: ") + "\n" +
ToString("----------------") + "\n" +
conditionsSummary + "\n";
}
//
return result;
}
};
//
// Implementations ...
class XCXCAEATradeManager : public XCBaseAlert
{
//
// Public ...
public:
//
// Props ...
XCTrade *trader;
bool drawSignal;
XCPOIDrawer *drawer;
XCXCAEADataCollector *collector;
//
// Constructors ...
XCXCAEATradeManager(
XCTrade *&_trader,
string _path = NULL // Base Path
)
{
//
trader = _trader;
drawSignal = true;
drawer = new XCPOIDrawer();
//
allowTrade = true;
allowLongs = true;
allowShorts = true;
//
reportTrades = true;
reportSignals = true;
reportProtector = true;
reportAfterTradesBalance = true;
//
SaveTrades(false);
//
// Normalizing Collector Path ...
if (!IsValid(_path))
{
_path = "X121XCAEATradeManager" + "\\" + trader.mAccount.GetCompany();
}
//
collector = new XCXCAEADataCollector();
collector.Path(_path);
}
//
// De Constructors ...
~XCXCAEATradeManager()
{
//
Clean(trades);
//
ZeroMemory(trader);
ZeroMemory(drawer);
ZeroMemory(collector);
}
//
// Properties ...
//
bool SaveTrades()
{
return mSaveTrades;
}
//
void SaveTrades(bool value)
{
mSaveTrades = value;
}
//
// TODO: Implement this ...
void SetTradeSession(
string start,
string end //
)
{
//
bool has = IsValid(start) ||
IsValid(end);
if (!has)
{
return;
}
//
}
//
void SetTradeReports(
bool _reportTrades,
bool _reportSignals,
bool _reportProtector,
bool _reportAfterTradesBalance //
)
{
//
reportTrades = _reportTrades;
reportSignals = _reportSignals;
reportProtector = _reportProtector;
reportAfterTradesBalance = _reportAfterTradesBalance;
}
//
void SetTradePermissions(
bool _allowTrade,
bool _allowLongs,
bool _allowShorts //
)
{
//
allowTrade = _allowTrade;
allowLongs = _allowLongs;
allowShorts = _allowShorts;
}
//
// Tools ...
/**
* Do All Position Managing Senaros ...
*/
void Manage()
{
//
bool has = HasChild(trades);
if (!has)
{
return;
}
//
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;
}
//
// After Update All Trades we can Do 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 ...
// TODO: Implement this ...
//
// 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 ...
double targets[];
Copy(
trades[idx].signal.targets,
targets //
);
int targetsCount = ArraySize(targets);
has = IsValidSize(targetsCount);
if (has)
{
//
for (int j = 0; j < targetsCount; j++)
{
//
double 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
: iPosition.sl > jTarget;
if (!has)
{
continue;
}
//
double targetDelta = isLong
? jTarget + (targetDistance * points)
: jTarget - (targetDistance * points);
//
// Can Risk Free ...
bool canRF = jTarget > 0 &&
(isLong
? jTarget > iPosition.entry
: jTarget < iPosition.entry) &&
(isLong
? iPosition.price > targetDelta
: iPosition.price < targetDelta);
if (canRF)
{
//
double sl = jTarget;
double tp = iPosition.tp;
string comment = "RF On Target ...";
bool isModified = trader.Modify(
iPosition.ticket,
sl,
tp,
comment //
);
if (isModified)
{
//
trades[idx].signal.appliedTargetIDX = j;
trades[idx].signal.isTargetApplied = true;
//
string message = prefix +
ToString(iPosition.type) +
" Position: " +
ToString(iPosition.ticket) +
" RF On Traget: " + ToString(jTarget) +
" Successfully ...";
HandleReportProtector(message);
}
}
}
}
//
// Handle Partial Close on Specified Targets ...
has = !trades[idx].signal.isPartiallyClosed &&
trades[idx].signal.partialCloseOnTPLevel > 0 &&
trades[idx].signal.partialCloseMultiplier > 0;
if (has)
{
//
// Check Reward Touched ...
double iCurrReward = iPosition.CalculateTouchedReward();
has = iCurrReward >= trades[idx].signal.partialCloseOnTPLevel;
if (has)
{
//
// Check Volume Multiplier ...
double volumeMultiplier = trades[idx].signal.partialCloseMultiplier;
has = volumeMultiplier > 0 &&
volumeMultiplier <= 1;
if (has)
{
//
// Do Partial Closing ...
double volume = iPosition.volume * volumeMultiplier;
//
// Normalizing Volume ...
volume = NormalizeVolume(
volume,
iPosition.symbol //
);
//
string comment = "PC On Target ...";
has = trader.ClosePartial(
iPosition.ticket,
volume,
comment //
);
if (has)
{
//
// Update Model ...
trades[idx].signal.isPartiallyClosed = true;
//
string message = prefix +
ToString(iPosition.type) +
" Position: " +
ToString(iPosition.ticket) +
" PC (" + ToString(volume) + ") On Traget: " + ToString(iCurrReward) +
" Successfully ...";
HandleReportProtector(message);
}
}
}
}
}
}
}
/**
* 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(message);
}
//
// 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;
}
//
// TODO: Complete this ...
// Implementing Trade Session Validating ...
//
// TODO: Complete this ...
// Implementing Trade Permissions Based on SL(s) Count ...
//
return result;
}
/**
* Execute Specified Signal ...
*
* @param signal: XSignal instance reference ...
* @param conditions: XCAEAStrategyConditions instance reference ...
*
* @return ( bool )
*/
bool Execute(
XSignal &signal,
XCAEAStrategyConditions &conditions //
)
{
//
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(message);
}
//
if (result)
{
//
if (drawSignal)
{
//
XCSignalObject *iObj;
isExists = drawer.DrawSignal(
signal,
iObj //
);
//
if (isExists)
{
//
mObjects.Add(iObj);
ZeroMemory(iObj);
}
}
//
XCAEATrade iTrade;
//
ulong positionID = trader.GetLastOpenPositionTicket();
double commission = trader.GetPositionCommission(positionID);
//
iTrade.signal = signal;
iTrade.conditions = conditions;
iTrade.positionID = positionID;
iTrade.commission = commission;
iTrade.signaledAt = signal.time;
iTrade.executedAt = TimeCurrent();
//
AddRef(
iTrade,
trades //
);
}
//
return result;
}
/**
* 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 = XCAEA_TRADE_FINAL_TP;
//
tpCount++;
managedTPCount++;
//
// Save Trade ...
Save(trades[idx]);
//
// Handle Balance Reporting ...
string prefix = "TP: " + ToString(trades[idx].profit);
HandleReportBalance(prefix);
//
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 = XCAEA_TRADE_FINAL_SL;
//
slCount++;
managedSLCount++;
//
// Save Trade ...
Save(trades[idx]);
//
// Handle Balance Reporting ...
string prefix = "SL: " + ToString(trades[idx].profit);
HandleReportBalance(prefix);
//
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
? XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT
: XCAEA_TRADE_FINAL_CLOSE_IN_LOSE;
//
if (isInProfit)
{
//
tpCount++;
managedTPCount++;
}
else
{
//
slCount++;
managedSLCount++;
}
//
// Save Trade ...
Save(trades[idx]);
//
// Handle Balance Reporting ...
string prefix = "Force Close " +
(isInProfit
? "Profit"
: "Loss") +
": " +
ToString(trades[idx].profit);
HandleReportBalance(prefix);
//
RemoveTrade(idx);
}
//
// TODO: Complete this ...
void HandleGuard()
{
}
/**
* Handle New Day ...
*/
void HandleNewDay()
{
//
// Clean Managed SL(s) and TP(s) and Positions ...
}
/**
* 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++)
{
//
XCAEATrade 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++)
{
//
XCAEATrade 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 GetFilePath(XCAEATrade &trade)
{
//
string fileName = trade.GetFileName();
//
return GetFilePath(fileName);
}
//
string GetFilePath(string fileName)
{
//
string result = NULL;
//
string mPath = collector.Path();
//
result =
//
mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log"
//
;
//
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 + "\n"
: "") +
"Account Balance: " + ToString(balance) + currency;
Alert(message);
}
//
void HandleReportProtector(string message = NULL)
{
//
if (!reportProtector)
{
return;
}
//
Alert(message);
}
//
bool Save(
XCAEATrade &trade,
bool includeSummary = true,
bool includeSignal = true,
bool includeConditions = true //
)
{
//
bool result = false;
//
if (!mSaveTrades)
{
return result;
}
//
string filePath = GetFilePath(trade);
result = IsValid(filePath);
if (!result)
{
return result;
}
//
string content = trade.ToString(
includeSummary,
includeSignal,
includeConditions //
);
//
result = collector.Save(
filePath,
content //
);
//
return result;
}
//
// Private ...
private:
//
// Props ...
//
bool allowTrade;
bool allowLongs;
bool allowShorts;
//
bool mSaveTrades;
//
int slCount;
int tpCount;
//
int managedSLCount;
int managedTPCount;
//
bool reportTrades;
bool reportSignals;
bool reportProtector;
bool reportAfterTradesBalance;
//
CArrayObj mObjects;
//
XCAEATrade trades[];
};
//