add XTargetManager Class and use it in XBacktesterEA Class ...

also i have to implement it's usage on XCExpert Class ...
extends XTarget approaches and add support to Distances ...
This commit is contained in:
2025-10-01 17:16:13 +03:30
parent 684d3028c4
commit 2669790493
7 changed files with 1054 additions and 201 deletions
@@ -23,6 +23,7 @@
#include "../Classes/x-saherelm.base.class.mq5"
#include "../Classes/x-saherelm.x-alert.class.mq5"
#include "../Classes/x-saherelm.x-chart.helper.class.mq5"
#include "../Classes/x-saherelm.x-target.manager.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Classes/x-saherelm.x-volume.class.mq5"
@@ -80,8 +81,11 @@ class XCBackTesterEA : public XCBase
double volume; // Default volume Per Trade ...
//
XCAlert *eaAlert; // EA Scope Alert Handler ...
XCTrade *eaTrader; // EA Scope Trade Handler ...
XCAlert *eaAlert; // EA Scope Alert Handler ...
XCTrade *eaTrader; // EA Scope Trade Handler ...
XCTargetManager *eaTargetManager; // EA Scope Target Manager ...
//
XCChartHelper *eaChartHelper; // Chart Helper Class ...
//
@@ -273,6 +277,23 @@ class XCBackTesterEA : public XCBase
}
}
//
// Signal Executed Event Handlers ...
count = ArraySize(mOnSignalExecutedEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnSignalExecutedEventHandler(mOnSignalExecutedEventHandlers[i]);
}
}
//
// Init Target Managers ...
eaTargetManager = new XCTargetManager(eaTrader);
//
// Initial GUI ...
result = InitGUI();
@@ -294,6 +315,11 @@ class XCBackTesterEA : public XCBase
*/
void HandleOnDeInit()
{
//
eaTargetManager.Destroy();
delete eaTargetManager;
ZeroMemory(eaTargetManager);
//
delete eaTrader;
ZeroMemory(eaTrader);
@@ -326,7 +352,12 @@ class XCBackTesterEA : public XCBase
*/
void HandleOnTick()
{
//
UpdateGUI();
//
// Managing Targets ...
eaTargetManager.Manage();
}
/**
@@ -395,6 +426,9 @@ class XCBackTesterEA : public XCBase
//
// Update Position Buttons ...
UpdatePositionButtons();
//
eaTargetManager.HandleOnStopLossTriggered(deal);
}
/**
@@ -407,6 +441,9 @@ class XCBackTesterEA : public XCBase
//
// Update Position Buttons ...
UpdatePositionButtons();
//
eaTargetManager.HandleOnTakeProfitTriggered(deal);
}
/**
@@ -425,6 +462,13 @@ class XCBackTesterEA : public XCBase
//
// Update Position Buttons ...
UpdatePositionButtons();
//
eaTargetManager.HandleOnPositionForceClosed(
ticket,
position,
comment //
);
}
/**
@@ -502,6 +546,34 @@ class XCBackTesterEA : public XCBase
{
}
/**
* Handle New Signal Recieved ...
*
* @param signal: XSignal, reference to recieved Signal ...
*/
void HandleOnSignalTriggered(XSignal &signal)
{
//
ENUM_X_SIGNAL_EXECUTION_RESULT result;
bool isExcuted = eaTrader.ExecuteSignal(
signal,
result //
);
if (isExcuted)
{
}
}
/**
* Handle Signal Executed Event ...
*
* @param signal: XSignal, reference to Executed Signal ...
*/
void HandleOnSignalExecuted(XSignal &signal)
{
eaTargetManager.AddExecutedSignal(signal);
}
//
// Props and Setter / Getter (s) ...
@@ -652,6 +724,23 @@ class XCBackTesterEA : public XCBase
);
}
//
// Add New Signal Executed Event Handler ...
void AddOnSignalExecutedEventHandler(TOnSignal handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnSignalExecutedEventHandlers //
);
}
//
// Protected ...
protected:
@@ -667,6 +756,7 @@ class XCBackTesterEA : public XCBase
TOnStopLoss mStopLossEventHandlers[];
TOnTakeProfit mTakeProfitEventHandlers[];
TOnForceClose mOnForceCloseEventHandlers[];
TOnSignal mOnSignalExecutedEventHandlers[];
TOnDealsChanged mDealsChangedEventHandlers[];
TOnOrdersChanged mOrdersChangedEventHandlers[];
TOnPartialClose mOnPartialCloseEventHandlers[];
@@ -0,0 +1,620 @@
///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// ----------------------------------------------
// Name: XCTargetManager
// Description: provides all Positions Target 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.base.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
//
// Implementation ...
class XCTargetManager : public XCBase
{
//
// Public ...
public:
//
XCTrade *trader;
XSignal signals[];
//
// Constructor ...
XCTargetManager(
XCTrade *_trader //
) : XCBase()
{
//
trader = _trader;
XClean(signals);
}
//
// Deconstructor ...
~XCTargetManager()
{
Destroy();
}
//
void Destroy()
{
//
XClean(signals);
}
//
// Actions ...
/**
* Manage Positions ...
*
* @param barIndex: int, Bar Index ...
*/
void Manage(int barIndex = 0)
{
//
// Normalize ...
barIndex = NormalizeInt(barIndex, 0);
//
// Extract In Profit Positions ...
XPosition positions[];
int count = GetInProfitPositions(positions);
bool has = IsValidIndex(count);
if (!has)
{
return;
}
//
XSignal iSignal;
XTarget iTarget;
int signalIDX = -1;
int targetIDX = -1;
XPosition iPosition;
//
// Loop through Positions ...
for (int i = 0; i < count; i++)
{
//
// Select Positions ...
iPosition = positions[i];
has = FindSignal(
signalIDX,
iPosition.ticket //
);
if (!has)
{
continue;
}
iSignal = signals[signalIDX];
//
// Selecting Target ...
targetIDX = iSignal.appliedTargetIDX;
if (iSignal.isTargetApplied)
{
targetIDX++;
}
//
// Validate Targets Size ...
has = HasChild(iSignal.targets) &&
targetIDX < ArraySize(iSignal.targets);
if (has)
{
//
// Select Target ...
iTarget = iSignal.targets[targetIDX];
//
// Check Can Apply Target and Apply it ...
has = HandleApplyTarget(iTarget, iPosition);
if (has)
{
//
// Update Target IDX ...
iSignal.appliedTargetIDX++;
}
}
//
// Update Signal ...
signals[signalIDX] = iSignal;
//
// Cleanup ...
iSignal.Clean();
iTarget.Clean();
iPosition.Clean();
}
//
// Cleanup Resources ...
iSignal.Clean();
iTarget.Clean();
iPosition.Clean();
}
/**
* Extract in Profit Positions ...
*
* @param positions: XPosition, reference collection to holds result ...
*
* @return ( int )
*/
int GetInProfitPositions(
XPosition &positions[] //
)
{
//
int result = 0;
//
// Prepare ...
XClean(positions);
//
// Validate ...
if (trader == NULL)
{
return result;
}
//
result = trader.GetInProfitPositions(
positions,
NULL, // All Symbols ...
NULL, // All Providers ...
NULL, // All Periods ...
NULL, // All Position Types ...
X_POSITION_SELECT_ALL, // Select Method ...
true, // Filter by Magic ...
true // Force Clean ...
);
if (IsValidSize(result))
{
//
XPosition iPos;
XPosition tmp[];
Copy(
positions,
tmp //
);
XClean(positions);
while (HasChild(tmp))
{
//
iPos = tmp[0];
ArrayRemove(
tmp,
0,
1 //
);
//
int idx = -1;
bool has = FindSignal(
idx,
iPos.ticket //
);
if (has)
{
//
AddRef(
iPos,
positions //
);
}
}
//
XClean(tmp);
iPos.Clean();
//
result = ArraySize(positions);
}
//
return result;
}
/**
* Add Executed Signal ...
*
* @param signal: XSignal, reference to Executed Signal ...
*/
void AddExecutedSignal(XSignal &signal)
{
//
// Validate ...
if (!signal.IsValid() || !signal.IsExecuted())
{
return;
}
//
AddRef(
signal,
signals //
);
}
//
// Trade Event Handlers ...
/**
* Handle Stop Loss Event Triggered ...
*
* @param deal: XDeal instance refrence, provides Triggered Deal info ...
*/
void HandleOnStopLossTriggered(const XDeal &deal)
{
RemoveSignal(deal.positionId);
}
/**
* Handle Take Profit Event Triggered ...
*
* @param deal: XDeal instance refrence, provides Triggered Deal info ...
*/
void HandleOnTakeProfitTriggered(const XDeal &deal)
{
RemoveSignal(deal.positionId);
}
/**
* Handle Position Force Closed Event ...
*
* @param ticket: ulong, triggered Positions ticket ...
* @param position: XPosition instance refrence, Triggered Position ...
* @param comment: string, Comment ...
*/
void HandleOnPositionForceClosed(
const ulong ticket,
const XPosition &position,
const string comment //
)
{
RemoveSignal(ticket);
}
//
// Event Handlers Registration ...
//
void AddOnStopLossEventHandler(TOnStopLoss handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mStopLossEventHandlers
//
);
}
//
void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mTakeProfitEventHandlers
//
);
}
//
void AddOnForceClosePositionEventHandler(TOnForceClose handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnForceCloseEventHandlers
//
);
}
//
// Protected ...
protected:
//
/**
* Find Specified Signal ...
*
* @param index: int, reference to holds detected index ...
* @param signal: XSignal, reference to Specified Signal ...
*
* @return ( bool )
*/
bool FindSignal(
int &index,
XSignal &signal //
)
{
//
bool result = false;
//
// Prepare ...
index = -1;
//
// Validate ...
result = signal.IsValid() &&
HasChild(signals);
if (!result)
{
return result;
}
//
// Looping Through Signals ...
int count = ArraySize(signals);
for (int i = 0; i < count; i++)
{
//
result = signals[i]
.IsSameAs(signal);
if (result)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
return result;
}
/**
* Find Specified Signal ...
*
* @param index: int, reference to holds detected index ...
* @param ticket: ulong, Specified Signal Position ID ...
*
* @return ( bool )
*/
bool FindSignal(
int &index,
ulong ticket //
)
{
//
bool result = false;
//
// Prepare ...
index = -1;
//
// Validate ...
result = NotEmptyZero(ticket) &&
HasChild(signals);
if (!result)
{
return result;
}
//
// Looping Through Signals ...
int count = ArraySize(signals);
for (int i = 0; i < count; i++)
{
//
result = signals[i].IsValid() &&
signals[i].IsExecuted() &&
signals[i].positionId == ticket;
if (result)
{
//
index = i;
break;
}
}
//
result = IsValidIndex(index);
//
return result;
}
/**
* Remove Specified Signal ...
*
* @param ticket: ulong, Specified Ticket ...
*/
void RemoveSignal(ulong ticket)
{
//
bool has = NotEmptyZero(ticket) &&
HasChild(signals);
if (!has)
{
return;
}
//
int idx = -1;
has = FindSignal(
idx,
ticket //
);
if (!has)
{
return;
}
//
ArrayRemove(
signals,
idx,
1 //
);
}
/**
* Check Can Apply Specified Target on Specified Position or not ...
*
* @param target: XTarget, reference to Specified Target for Apply ...
* @param position: XPosition, reference to Specified Position ...
*
* @return ( bool )
*/
bool CanApplyTarget(
XTarget &target,
XPosition &position //
)
{
//
bool result = false;
//
// Validate ...
result =
target.IsValid() &&
position.IsValid();
if (!result)
{
return result;
}
//
// Check Price Reached Targets Value and Distance ...
double targetDistance = (target.actingDistance > 0 ? target.actingDistance : 0) * position.GetPointsValue();
result =
IsLong(position.type)
? position.price >= target.target + targetDistance
: position.price <= target.target - targetDistance;
//
return result;
}
/**
* Apply Specified Target on Specified Position ...
*
* @param target: XTarget, reference to Specified Target for Apply ...
* @param position: XPosition, reference to Specified Position ...
*
* @return ( bool )
*/
bool HandleApplyTarget(
XTarget &target,
XPosition &position //
)
{
//
bool result = false;
//
// Validate ...
result =
target.IsValid() &&
position.IsValid() &&
CanApplyTarget(
target,
position //
);
if (!result)
{
return result;
}
//
// Apply Target ...
string comment = NULL;
//
// Do Risk Free if Provided ...
if (target.doRF)
{
//
comment = "Taregt Manager do RF ...";
result = trader.Modify(
position.ticket,
target.target, // SL ...
position.tp, // TP ...
comment //
);
}
//
// Do Partial Close if Provided ...
if (result && target.volumeMultiplier > 0)
{
//
double volume = NormalizeVolume((position.volume * target.volumeMultiplier), position.symbol);
if (volume != position.volume)
{
//
comment = "Target Manager do Partial Close ...";
result = trader.ClosePartial(
position.ticket,
volume,
comment //
);
}
}
//
return result;
}
//
// Private ...
private:
//
//
// Required Event Handlers ...
TOnStopLoss mStopLossEventHandlers[];
TOnTakeProfit mTakeProfitEventHandlers[];
TOnForceClose mOnForceCloseEventHandlers[];
};
//
+50 -3
View File
@@ -814,6 +814,23 @@ class XCTrade : public XCBase
);
}
//
// Add New Signal Executed Event Handler ...
void AddOnSignalExecutedEventHandler(TOnSignal handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnSignalExecutedEventHandlers //
);
}
//
// Functions ...
@@ -1355,11 +1372,10 @@ class XCTrade : public XCBase
mSignal.positionId = ticket;
//
// Handle Store XPositionInfo ...
// Notify Signal Execution ...
if (mSignal.mode == X_ORDER_MODE_MARKET)
{
//
// TODO: Implement this ...
NotifySignalExecuted(mSignal);
}
}
@@ -3568,6 +3584,33 @@ class XCTrade : public XCBase
}
}
//
void NotifySignalExecuted(XSignal &signal)
{
//
bool has = signal.IsValid() &&
signal.IsExecuted();
if (!has)
{
return;
}
//
int count = ArraySize(mOnSignalExecutedEventHandlers);
if (count <= 0)
{
return;
}
//
for (int i = 0; i < count; i++)
{
//
TOnSignal iHandler = mOnSignalExecutedEventHandlers[i];
iHandler(signal);
}
}
//
// Tools ...
@@ -3728,6 +3771,10 @@ class XCTrade : public XCBase
// Trade State Changed Event Handlers ...
TOnTradeStateChanged mTradeStateChangedEventHandlers[];
//
// Signal Executed Event Handlers ...
TOnSignal mOnSignalExecutedEventHandlers[];
//
TOnModify mOnModifyEventHandlers[];
TOnForceClose mOnForceCloseEventHandlers[];