Files
MQL5Data/Classes/x-saherelm.x-backtester.expert.class.mq5
T
2025-09-16 23:48:52 +03:30

2373 lines
58 KiB
Plaintext

///////////////////////////////////////////////////////
//
// SaherElm IT Center MQL5 Class Library
// --------------------------------------
// Name: XCBackTesterEA
// Description: Back Tester Expert Class ...
//
// 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-alert.class.mq5"
#include "../Classes/x-saherelm.x-chart.helper.class.mq5"
#include "../Classes/x-saherelm.x-trade.class.mq5"
#include "../Classes/x-saherelm.x-volume.class.mq5"
//
// Import Chart Objects ...
#include <Arrays/ArrayObj.mqh>
#include <ChartObjects/ChartObject.mqh>
#include <ChartObjects/ChartObjectsArrows.mqh>
#include <ChartObjects/ChartObjectsFibo.mqh>
#include <ChartObjects/ChartObjectsLines.mqh>
#include <ChartObjects/ChartObjectsShapes.mqh>
#include <ChartObjects/ChartObjectsTxtControls.mqh>
#include <Charts/Chart.mqh>
#include <Controls/Button.mqh>
#include <Controls/Edit.mqh>
#include <Controls/Label.mqh>
//
// Definitions ...
//
// Implementation ...
class XCBackTesterEA : public XCBase
{
//
// Public ...
public:
//
//
// Common ...
long eaMagicNumber; // Magic Number
int eaSlippage; // Slippgae
string eaLogSuffix; // Log Suffix
//
// Symbol / Period ...
string symbol; // Symbol
ENUM_TIMEFRAMES period; // Period
//
// Alert ...
bool eaEnableAlerts; // Enable Alerts
bool eaLogAlerts; // Log Alerts
bool eaMailAlerts; // Mail Alerts
bool eaPushAlerts; // Push Alerts
bool eaTerminalAlerts; // Terminal Alerts
//
// SL/TP Manipulation Props ...
double allowedTPSLStep; // Value for Change TP/SL in Each Step ...
double minAllowedRiskInPoint; // Min Allowed Risk in Point ...
double riskInPoint; // Risk in Point per Trades ...
double riskToRewardRatio; // Risk To Reward per Trades ...
double volume; // Default volume Per Trade ...
//
XCAlert *eaAlert; // EA Scope Alert Handler ...
XCTrade *eaTrader; // EA Scope Trade Handler ...
XCChartHelper *eaChartHelper; // Chart Helper Class ...
//
// Constructor ...
XCBackTesterEA()
{
//
selectedPositionID = 0;
//
volume = 0.01;
riskInPoint = 50;
allowedTPSLStep = 5;
riskToRewardRatio = 2;
minAllowedRiskInPoint = 10;
}
//
// Deconstructor ...
~XCBackTesterEA()
{
}
//
// Actions / Handlers ...
//
// Expert Advisor Event Handlers ...
/**
* Handle Initialization of Expert ...
*
* @return ( bool )
*/
bool HandleOnInit()
{
//
bool result = false;
//
// Validate Inputs ...
result = ValidateInputs();
if (!result)
{
return result;
}
//
int count = 0;
bool has = false;
//
// Initialize Alert Handler ...
eaAlert = new XCAlert();
eaAlert.SetPrefix(eaLogSuffix);
eaAlert.SetLogAlerts(eaLogAlerts);
eaAlert.SetMailAlerts(eaMailAlerts);
eaAlert.SetPushAlerts(eaPushAlerts);
eaAlert.SetEnableAlerts(eaEnableAlerts);
eaAlert.SetTerminalAlerts(eaTerminalAlerts);
//
// Initialize Trader ...
eaTrader = new XCTrade(
eaSlippage,
eaMagicNumber,
0,
0,
0 //
);
//
// Trade Handler Attachments ...
//
// Position Modifiy Event Handlers ...
count = ArraySize(mOnModifyEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnModifyPositionEventHandler(mOnModifyEventHandlers[i]);
}
}
//
// Position Stop Loss Event Handlers ...
count = ArraySize(mStopLossEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnStopLossEventHandler(mStopLossEventHandlers[i]);
}
}
//
// Position Take Profit Event Handlers ...
count = ArraySize(mTakeProfitEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnTakeProfitEventHandler(mTakeProfitEventHandlers[i]);
}
}
//
// Position Force Close Event Handlers ...
count = ArraySize(mOnForceCloseEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnForceClosePositionEventHandler(mOnForceCloseEventHandlers[i]);
}
}
//
// Deals Changed Event Handlers ...
count = ArraySize(mDealsChangedEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnDealsChangedEventHandler(mDealsChangedEventHandlers[i]);
}
}
//
// Orders Changed Event Handlers ...
count = ArraySize(mOrdersChangedEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnOrdersChangedEventHandler(mOrdersChangedEventHandlers[i]);
}
}
//
// Positions Changed Event Handlers ...
count = ArraySize(mPositionsChangedEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnPositionsChangedEventHandler(mPositionsChangedEventHandlers[i]);
}
}
//
// Trade Changed Event Handlers ...
count = ArraySize(mTradeStateChangedEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnTradeStateChangedEventHandler(mTradeStateChangedEventHandlers[i]);
}
}
//
// Postion Partially Close Event Handlers ...
count = ArraySize(mOnPartialCloseEventHandlers);
has = IsValidSize(count);
if (has)
{
//
for (int i = 0; i < count; i++)
{
eaTrader.AddOnPartialClosePositionEventHandler(mOnPartialCloseEventHandlers[i]);
}
}
//
// Initial GUI ...
result = InitGUI();
if (!result)
{
return result;
}
//
// Normalize Requirements ...
NormalizeProps();
//
return result;
}
/**
* Handle De Initialization of Expert ...
*/
void HandleOnDeInit()
{
//
delete eaTrader;
ZeroMemory(eaTrader);
//
SpecifiedClean(mOnModifyEventHandlers);
SpecifiedClean(mStopLossEventHandlers);
SpecifiedClean(mTakeProfitEventHandlers);
SpecifiedClean(mOnForceCloseEventHandlers);
SpecifiedClean(mDealsChangedEventHandlers);
SpecifiedClean(mOrdersChangedEventHandlers);
SpecifiedClean(mOnPartialCloseEventHandlers);
SpecifiedClean(mPositionsChangedEventHandlers);
SpecifiedClean(mTradeStateChangedEventHandlers);
//
DestroyGUI();
//
string message = "DeInitialized Successfully ...";
eaAlert.SendAlert(message);
//
delete eaAlert;
ZeroMemory(eaAlert);
}
/**
* Handle Tick ...
*/
void HandleOnTick()
{
UpdateGUI();
}
/**
* Handle Trade ...
*/
void HandleOnTrade()
{
eaTrader.HandleOnTrade();
}
/**
* Handle Timer ...
*/
void HandleOnTimer()
{
}
/**
* Handle Chart Event ...
*
* @param id: int, Event id ...
* @param lparam: long, Event Long Parameter ...
* @param dparam: double, Event Double Parameter ...
* @param sparam: string, Event String Parameter ...
*/
void HandleOnChartEvent(
const int id,
const long &lparam,
const double &dparam,
const string &sparam //
)
{
}
//
// Validators ...
/**
* Validate Inputs ...
*
* @return ( bool )
*/
bool ValidateInputs()
{
//
bool result = false;
//
// TODO: Handle this ...
result = true;
//
return result;
}
//
// Trade Event Handlers ...
/**
* Handle Stop Loss Event Triggered ...
*
* @param deal: XDeal instance refrence, provides Triggered Deal info ...
*/
void HandleOnStopLossTriggered(const XDeal &deal)
{
//
// Update Position Buttons ...
UpdatePositionButtons();
}
/**
* Handle Take Profit Event Triggered ...
*
* @param deal: XDeal instance refrence, provides Triggered Deal info ...
*/
void HandleOnTakeProfitTriggered(const XDeal &deal)
{
//
// Update Position Buttons ...
UpdatePositionButtons();
}
/**
* 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 //
)
{
//
// Update Position Buttons ...
UpdatePositionButtons();
}
/**
* Handle Position Partially Closed Event ...
*
* @param ticket: ulong, triggered Positions ticket ...
* @param profit: double, Position Profit ...
* @param comment: string, Comment ...
*/
void HandleOnPositionPartialClosed(
const ulong ticket,
const double profit,
const string comment //
)
{
}
/**
* Handle Deals Changed ...
*
* @param count: int, number of changes ...
* if the count is positive, means new added ...
* if the count is negative, means removed ...
*/
void HandleOnDealsChanged(int count)
{
}
/**
* Handle Orders Changed ...
*
* @param count: int, number of changes ...
* if the count is positive, means new added ...
* if the count is negative, means removed ...
*/
void HandleOnOrdersChanged(int count)
{
}
/**
* Handle Positions Changed ...
*
* @param count: int, number of changes ...
* if the count is positive, means new added ...
* if the count is negative, means removed ...
*/
void HandleOnPositionsChanged(int count)
{
//
// Update Position Buttons ...
UpdatePositionButtons();
}
/**
* Handle Position Modified Event ...
*
* @param ticket: ulong, triggered Positions ticket ...
* @param profit: double, Position Profit ...
* @param comment: string, Comment ...
*/
void HandleOnPositionModified(
const ulong ticket,
const double profit,
const string comment //
)
{
}
/**
* Handle all Trades States Changed Event ...
*
* @param state: XOnTradeHandlerState instance refrence, Changed Trade State data ...
*/
void HandleOnTradeStateChanged(const XOnTradeHandlerState &state)
{
}
//
// Props and Setter / Getter (s) ...
//
// Trade Event Listeners ...
//
void AddOnStopLossEventHandler(TOnStopLoss handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mStopLossEventHandlers //
);
}
//
void AddOnTakeProfitEventHandler(TOnTakeProfit handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mTakeProfitEventHandlers //
);
}
//
void AddOnDealsChangedEventHandler(TOnDealsChanged handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mDealsChangedEventHandlers //
);
}
//
void AddOnOrdersChangedEventHandler(TOnOrdersChanged handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOrdersChangedEventHandlers //
);
}
//
void AddOnModifyPositionEventHandler(TOnModify handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnModifyEventHandlers //
);
}
//
void AddOnPositionsChangedEventHandler(TOnPositionsChanged handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mPositionsChangedEventHandlers //
);
}
//
void AddOnTradeStateChangedEventHandler(TOnTradeStateChanged handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mTradeStateChangedEventHandlers //
);
}
//
void AddOnForceClosePositionEventHandler(TOnForceClose handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnForceCloseEventHandlers //
);
}
//
void AddOnPartialClosePositionEventHandler(TOnPartialClose handler)
{
//
if (handler == NULL)
{
return;
}
//
Add(
handler,
mOnPartialCloseEventHandlers //
);
}
//
// Protected ...
protected:
//
// Private ...
private:
//
// Props ...
//
// Trade Event Listeners ...
TOnModify mOnModifyEventHandlers[];
TOnStopLoss mStopLossEventHandlers[];
TOnTakeProfit mTakeProfitEventHandlers[];
TOnForceClose mOnForceCloseEventHandlers[];
TOnDealsChanged mDealsChangedEventHandlers[];
TOnOrdersChanged mOrdersChangedEventHandlers[];
TOnPartialClose mOnPartialCloseEventHandlers[];
TOnPositionsChanged mPositionsChangedEventHandlers[];
TOnTradeStateChanged mTradeStateChangedEventHandlers[];
//
// Object Props ...
//
string objSuffix;
//
CButton btnBuy;
CButton btnSell;
CButton btnClose;
CButton btnCloseAll;
CEdit lblPositionId;
//
CButton lblTP;
CButton lblSL;
CButton btnSLPlus;
CButton btnTPPlus;
CButton btnSLMinus;
CButton btnTPMinus;
CButton btnSLPlusPlus;
CButton btnTPPlusPlus;
CButton btnSLMinusMinus;
CButton btnTPMinusMinus;
//
CArrayObj posArray;
CButton *btnSelectedPosInfo;
CArrayObj selectedPosInfoArr;
//
ulong selectedPositionID;
bool showSelectedPositionInfo;
//
// GUI Event(s) ...
/**
* Initialize all GUI Requirements for EA ...
*
* @return ( bool )
*/
bool InitGUI()
{
//
bool result = false;
//
int subWindow = 0;
ulong chartId = ChartID();
objSuffix = eaLogSuffix + "_";
//
// Initial Chart Class Instance ...
eaChartHelper = new XCChartHelper(chartId);
//
int startX = 5;
int startY = 25;
//
int defaultGap = 5;
int defaultWidth = 100;
int defaultHeight = 30;
//
// Buy ...
int btnBuyX1 = startX;
int btnBuyX2 = startX + defaultWidth;
int btnBuyY1 = startY;
int btnBuyY2 = startY + defaultHeight;
string btnBuyName = objSuffix + "BTN_BUY";
result = btnBuy.Create(
chartId,
btnBuyName,
subWindow,
btnBuyX1,
btnBuyY1,
btnBuyX2,
btnBuyY2 //
);
if (result)
{
//
// Apply Buy Button Style ...
//
btnBuy.Text("Buy");
btnBuy.Color(clrYellow);
btnBuy.ColorBackground(clrGreen);
}
//
// Sell ...
int btnSellX1 = btnBuyX1 + btnBuyX2;
int btnSellX2 = btnSellX1 + defaultWidth;
int btnSellY1 = btnBuyY1;
int btnSellY2 = btnBuyY2;
string btnSellName = objSuffix + "BTN_SELL";
result = btnSell.Create(
chartId,
btnSellName,
subWindow,
btnSellX1,
btnSellY1,
btnSellX2,
btnSellY2 //
);
if (result)
{
//
// Apply Sell Button Style ...
//
btnSell.Text("Sell");
btnSell.Color(clrYellow);
btnSell.ColorBackground(clrDarkRed);
}
//
// LABEL Position ID ...
int lblPositionIdX1 = btnBuyX1;
int lblPositionIdX2 = lblPositionIdX1 + defaultWidth;
int lblPositionIdY1 = btnSellY2 + defaultGap;
int lblPositionIdY2 = lblPositionIdY1 + defaultHeight;
string lblPositionIdName = objSuffix + "LBL_POS_ID";
result = lblPositionId.Create(
chartId,
lblPositionIdName,
subWindow,
lblPositionIdX1,
lblPositionIdY1,
lblPositionIdX2,
lblPositionIdY2 //
);
if (result)
{
//
// Apply Label Position ID Style ...
lblPositionId.ReadOnly(true);
lblPositionId.Text("Pos ID: ");
lblPositionId.Color(clrYellow);
lblPositionId.ColorBackground(clrDarkGray);
}
//
// Close ...
int btnCloseX1 = startX;
int btnCloseX2 = btnCloseX1 + defaultWidth;
int btnCloseY1 = lblPositionIdY2 + defaultGap;
int btnCloseY2 = btnCloseY1 + defaultHeight;
string btnCloseName = objSuffix + "BTN_CLOSE";
result = btnClose.Create(
chartId,
btnCloseName,
subWindow,
btnCloseX1,
btnCloseY1,
btnCloseX2,
btnCloseY2 //
);
if (result)
{
//
// Apply Close Button Style ...
//
btnClose.Text("Close");
btnClose.Color(clrYellow);
btnClose.ColorBackground(clrDarkOrange);
}
//
// Close All ...
int btnCloseAllX1 = btnCloseX2 + defaultGap;
int btnCloseAllX2 = btnCloseAllX1 + defaultWidth;
int btnCloseAllY1 = lblPositionIdY2 + defaultGap;
int btnCloseAllY2 = btnCloseAllY1 + defaultHeight;
string btnCloseAllName = objSuffix + "BTN_CLOSE_ALL";
result = btnCloseAll.Create(
chartId,
btnCloseAllName,
subWindow,
btnCloseAllX1,
btnCloseAllY1,
btnCloseAllX2,
btnCloseAllY2 //
);
if (result)
{
//
// Apply Close Button Style ...
//
btnCloseAll.Text("Close All");
btnCloseAll.Color(clrYellow);
btnCloseAll.ColorBackground(clrDarkRed);
}
//
int minusPlusWidth = 20;
//
// TP Label ...
int lblTPX1 = btnCloseX1;
int lblTPX2 = lblTPX1 + defaultWidth;
int lblTPY1 = btnCloseY2 + defaultGap;
int lblTPY2 = lblTPY1 + defaultHeight;
string lblTPName = objSuffix + "LBL_TP";
result = lblTP.Create(
chartId,
lblTPName,
subWindow,
lblTPX1,
lblTPY1,
lblTPX2,
lblTPY2 //
);
if (result)
{
//
// Apply Style ...
lblTP.Disable();
lblTP.Text("TP");
lblTP.Color(clrWhite);
lblTP.ColorBackground(clrGreen);
}
//
// TP Plus ...
int btnTPPlusX1 = lblTPX1;
int btnTPPlusX2 = btnTPPlusX1 + minusPlusWidth;
int btnTPPlusY1 = lblTPY2 + defaultGap;
int btnTPPlusY2 = btnTPPlusY1 + defaultHeight;
string btnTPPlusName = objSuffix + "BTN_TP_PLUS";
result = btnTPPlus.Create(
chartId,
btnTPPlusName,
subWindow,
btnTPPlusX1,
btnTPPlusY1,
btnTPPlusX2,
btnTPPlusY2 //
);
if (result)
{
//
// Apply Style ...
btnTPPlus.Text("+");
btnTPPlus.Color(clrYellow);
btnTPPlus.ColorBackground(clrGreen);
}
//
// TP Plus Plus ...
int btnTPPlusPlusX1 = btnTPPlusX2 + defaultGap;
int btnTPPlusPlusX2 = btnTPPlusPlusX1 + minusPlusWidth;
int btnTPPlusPlusY1 = lblTPY2 + defaultGap;
int btnTPPlusPlusY2 = btnTPPlusPlusY1 + defaultHeight;
string btnTPPlusPlusName = objSuffix + "BTN_TP_PLUSPLUS";
result = btnTPPlusPlus.Create(
chartId,
btnTPPlusPlusName,
subWindow,
btnTPPlusPlusX1,
btnTPPlusPlusY1,
btnTPPlusPlusX2,
btnTPPlusPlusY2 //
);
if (result)
{
//
// Apply Style ...
btnTPPlusPlus.Text("++");
btnTPPlusPlus.Color(clrYellow);
btnTPPlusPlus.ColorBackground(clrGreen);
}
//
// TP Minus ...
int btnTPMinusX1 = lblTPX2 - minusPlusWidth;
int btnTPMinusX2 = btnTPMinusX1 + minusPlusWidth;
int btnTPMinusY1 = lblTPY2 + defaultGap;
int btnTPMinusY2 = btnTPMinusY1 + defaultHeight;
string btnTPMinusName = objSuffix + "BTN_TP_MINUS";
result = btnTPMinus.Create(
chartId,
btnTPMinusName,
subWindow,
btnTPMinusX1,
btnTPMinusY1,
btnTPMinusX2,
btnTPMinusY2 //
);
if (result)
{
//
// Apply Style ...
btnTPMinus.Text("-");
btnTPMinus.Color(clrYellow);
btnTPMinus.ColorBackground(clrDarkRed);
}
//
// TP Minus Minus ...
int btnTPMinusMinusX1 = btnTPMinusX1 - minusPlusWidth - defaultGap;
int btnTPMinusMinusX2 = btnTPMinusMinusX1 + minusPlusWidth;
int btnTPMinusMinusY1 = lblTPY2 + defaultGap;
int btnTPMinusMinusY2 = btnTPMinusY1 + defaultHeight;
string btnTPMinusMinusName = objSuffix + "BTN_TP_MINUSMINUS";
result = btnTPMinusMinus.Create(
chartId,
btnTPMinusMinusName,
subWindow,
btnTPMinusMinusX1,
btnTPMinusMinusY1,
btnTPMinusMinusX2,
btnTPMinusMinusY2 //
);
if (result)
{
//
// Apply Style ...
btnTPMinusMinus.Text("--");
btnTPMinusMinus.Color(clrYellow);
btnTPMinusMinus.ColorBackground(clrDarkRed);
}
//
// SL Label ...
int lblSLX1 = btnCloseAllX1;
int lblSLX2 = lblSLX1 + defaultWidth;
int lblSLY1 = btnCloseAllY2 + defaultGap;
int lblSLY2 = lblTPY1 + defaultHeight;
string lblSLName = objSuffix + "LBL_SL";
result = lblSL.Create(
chartId,
lblSLName,
subWindow,
lblSLX1,
lblSLY1,
lblSLX2,
lblSLY2 //
);
if (result)
{
//
// Apply Style ...
lblSL.Disable();
lblSL.Text("SL");
lblSL.Color(clrWhite);
lblSL.ColorBackground(clrDarkRed);
}
//
// SL Plus ...
int btnSLPlusX1 = lblSLX1;
int btnSLPlusX2 = btnSLPlusX1 + minusPlusWidth;
int btnSLPlusY1 = lblSLY2 + defaultGap;
int btnSLPlusY2 = btnSLPlusY1 + defaultHeight;
string btnSLPlusName = objSuffix + "BTN_SL_PLUS";
result = btnSLPlus.Create(
chartId,
btnSLPlusName,
subWindow,
btnSLPlusX1,
btnSLPlusY1,
btnSLPlusX2,
btnSLPlusY2 //
);
if (result)
{
//
// Apply Style ...
btnSLPlus.Text("+");
btnSLPlus.Color(clrYellow);
btnSLPlus.ColorBackground(clrGreen);
}
//
// SL Plus Plus ...
int btnSLPlusPlusX1 = btnSLPlusX2 + defaultGap;
int btnSLPlusPlusX2 = btnSLPlusPlusX1 + minusPlusWidth;
int btnSLPlusPlusY1 = lblSLY2 + defaultGap;
int btnSLPlusPlusY2 = btnSLPlusPlusY1 + defaultHeight;
string btnSLPlusPlusName = objSuffix + "BTN_SL_PLUSPLUS";
result = btnSLPlusPlus.Create(
chartId,
btnSLPlusPlusName,
subWindow,
btnSLPlusPlusX1,
btnSLPlusPlusY1,
btnSLPlusPlusX2,
btnSLPlusPlusY2 //
);
if (result)
{
//
// Apply Style ...
btnSLPlusPlus.Text("++");
btnSLPlusPlus.Color(clrYellow);
btnSLPlusPlus.ColorBackground(clrGreen);
}
//
// SL Minus ...
int btnSLMinusX1 = lblSLX2 - minusPlusWidth;
int btnSLMinusX2 = btnSLMinusX1 + minusPlusWidth;
int btnSLMinusY1 = lblSLY2 + defaultGap;
int btnSLMinusY2 = btnSLMinusY1 + defaultHeight;
string btnSLMinusName = objSuffix + "BTN_SL_MINUS";
result = btnSLMinus.Create(
chartId,
btnSLMinusName,
subWindow,
btnSLMinusX1,
btnSLMinusY1,
btnSLMinusX2,
btnSLMinusY2 //
);
if (result)
{
//
// Apply Style ...
btnSLMinus.Text("-");
btnSLMinus.Color(clrYellow);
btnSLMinus.ColorBackground(clrDarkRed);
}
//
// SL Minus Minus ...
int btnSLMinusMinusX1 = btnSLMinusX1 - minusPlusWidth - defaultGap;
int btnSLMinusMinusX2 = btnSLMinusMinusX1 + minusPlusWidth;
int btnSLMinusMinusY1 = lblSLY2 + defaultGap;
int btnSLMinusMinusY2 = btnSLMinusY1 + defaultHeight;
string btnSLMinusMinusName = objSuffix + "BTN_SL_MINUSMINUS";
result = btnSLMinusMinus.Create(
chartId,
btnSLMinusMinusName,
subWindow,
btnSLMinusMinusX1,
btnSLMinusMinusY1,
btnSLMinusMinusX2,
btnSLMinusMinusY2 //
);
if (result)
{
//
// Apply Style ...
btnSLMinusMinus.Text("--");
btnSLMinusMinus.Color(clrYellow);
btnSLMinusMinus.ColorBackground(clrDarkRed);
}
//
UpdateGUIState();
//
return result;
}
/**
* Update GUI based on Each Ticks ...
*/
void UpdateGUI()
{
//
UpdateGUIState();
//
// Buy Button Pressed ...
if (btnBuy.Pressed())
{
//
HandleBuyTrade();
btnBuy.Pressed(false);
}
//
// Sell Button Pressed ...
if (btnSell.Pressed())
{
//
HandleSellTrade();
btnSell.Pressed(false);
}
//
// Close Button Pressed ...
if (btnClose.Pressed())
{
//
if (btnClose.IsEnabled())
{
//
if (selectedPositionID > 0)
{
HandleCloseTrade(selectedPositionID);
}
}
//
btnClose.Pressed(false);
}
//
// Close All Button Pressed ...
if (btnCloseAll.Pressed())
{
//
if (btnCloseAll.IsEnabled())
{
HandleCloseTrade(0);
}
//
btnCloseAll.Pressed(false);
}
//
// Check Show Hide Selected Position Info ...
if (btnSelectedPosInfo != NULL)
{
//
if (btnSelectedPosInfo.Pressed())
{
//
// Check Enable ...
if (btnSelectedPosInfo.IsEnabled())
{
//
// Handle Toggle Show/Hide lblSelectedPositionInfo ...
CEdit *iLbl = NULL;
bool isVisible = showSelectedPositionInfo;
int count = selectedPosInfoArr.Total();
if (IsValidSize(count))
{
//
// Loop Through Labels ...
for (int i = 0; i < count; i++)
{
//
iLbl = (CEdit *)selectedPosInfoArr.At(i);
isVisible = iLbl.IsVisible();
if (isVisible)
{
iLbl.Hide();
}
else
{
iLbl.Show();
}
}
//
// Update Button Text ...
isVisible = showSelectedPositionInfo;
string btnText = isVisible
? "Hide Info"
: "Show Info";
btnSelectedPosInfo.Text(btnText);
showSelectedPositionInfo = !showSelectedPositionInfo;
}
//
ZeroMemory(iLbl);
}
//
btnSelectedPosInfo.Pressed(false);
}
}
//
// TP Managing ...
//
// TP Plus ...
if (btnTPPlus.Pressed())
{
//
if (btnTPPlus.IsEnabled())
{
HandleTPChange(X_DIRECTION_BULLISH);
}
//
btnTPPlus.Pressed(false);
}
//
// TP Plus Plus ...
if (btnTPPlusPlus.Pressed())
{
//
if (btnTPPlusPlus.IsEnabled())
{
HandleTPChange(X_DIRECTION_BULLISH, 2);
}
//
btnTPPlusPlus.Pressed(false);
}
//
// TP Minus ...
if (btnTPMinus.Pressed())
{
//
if (btnTPMinus.IsEnabled())
{
HandleTPChange(X_DIRECTION_BEARISH);
}
//
btnTPMinus.Pressed(false);
}
//
// TP Minus Minus ...
if (btnTPMinusMinus.Pressed())
{
//
if (btnTPMinusMinus.IsEnabled())
{
HandleTPChange(X_DIRECTION_BEARISH, 2);
}
//
btnTPMinusMinus.Pressed(false);
}
//
// SL Managing ...
//
// SL Plus ...
if (btnSLPlus.Pressed())
{
//
if (btnSLPlus.IsEnabled())
{
HandleSLChange(X_DIRECTION_BULLISH);
}
//
btnSLPlus.Pressed(false);
}
//
// SL Plus Plus ...
if (btnSLPlusPlus.Pressed())
{
//
if (btnSLPlusPlus.IsEnabled())
{
HandleSLChange(X_DIRECTION_BULLISH, 2);
}
//
btnSLPlusPlus.Pressed(false);
}
//
// SL Minus ...
if (btnSLMinus.Pressed())
{
//
if (btnSLMinus.IsEnabled())
{
HandleSLChange(X_DIRECTION_BEARISH);
}
//
btnSLMinus.Pressed(false);
}
//
// SL Minus Minus ...
if (btnSLMinusMinus.Pressed())
{
//
if (btnSLMinusMinus.IsEnabled())
{
HandleSLChange(X_DIRECTION_BEARISH, 2);
}
//
btnSLMinusMinus.Pressed(false);
}
//
// Check Positions Buttons ...
int posCount = posArray.Total();
bool has = IsValidSize(posCount);
if (has)
{
//
for (int i = 0; i < posCount; i++)
{
//
CButton *iBtn = (CButton *)posArray.At(i);
//
if (iBtn.Pressed())
{
//
if (iBtn.IsEnabled())
{
//
ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());
if (posID > 0 && selectedPositionID != posID)
{
selectedPositionID = posID;
UpdateGUIState();
}
}
//
iBtn.Pressed(false);
}
//
ZeroMemory(iBtn);
}
}
}
/**
* Destroy all Initialized GUi Requirements ...
*/
void DestroyGUI()
{
//
delete eaChartHelper;
ZeroMemory(eaChartHelper);
//
delete btnSelectedPosInfo;
ZeroMemory(btnSelectedPosInfo);
}
/**
* Update GUI Element Styles ...
*/
void UpdateGUIState()
{
//
// Update States Of Position Select Buttons ...
int count = posArray.Total();
bool has = IsValidSize(count) && selectedPositionID > 0;
if (has)
{
//
for (int i = 0; i < count; i++)
{
//
CButton *iBtn = (CButton *)posArray.At(i);
//
ulong posID = ExtractPositionIDFromButtonName(iBtn.Name());
//
// Select Position ...
XPosition iPos;
bool hasPosition = eaTrader.GetPosition(
posID,
iPos //
);
string iTooltip =
!hasPosition
? ""
: IsLong(iPos.type)
? "Long"
: "Short";
//
if (posID == selectedPositionID)
{
//
iBtn.Disable();
iBtn.ColorBackground(clrDarkGray);
}
else
{
//
iBtn.Enable();
//
color iBtnBG = IsLong(iPos.type)
? clrGreen
: clrDarkRed;
//
iBtn.ColorBackground(iBtnBG);
}
//
ZeroMemory(iBtn);
}
}
//
// Update State of Close All ...
if (has && count > 1)
{
//
btnCloseAll.Enable();
btnCloseAll.ColorBackground(clrDarkRed);
}
else
{
//
btnCloseAll.Disable();
btnCloseAll.ColorBackground(clrDarkGray);
}
//
// Update Selected Position Info Button State ...
if (has)
{
//
// Reading Global Chart Info ...
int subWindow = 0;
int chartWidth = eaChartHelper.Width();
ulong chartId = eaChartHelper.ChartId();
//
// Read Selected Position ...
XPosition selectedPosition;
bool hasPosition = eaTrader.GetPosition(
selectedPositionID,
selectedPosition //
);
//
// Read All Positions ...
double profits = 0;
XPosition positions[];
string positionsInfo = "";
int positionsCount = eaTrader.GetPositions(positions);
has = IsValidSize(positionsCount);
if (has)
{
//
for (int i = 0; i < positionsCount; i++)
{
profits += positions[i].profit;
}
//
if (positionsCount > 1)
{
positionsInfo = "Positions Count: " + ToXString(positionsCount) + ", Profits: " + ToXString(profits);
}
}
//
if (hasPosition)
{
//
// Preparing Position Info ...
string posTicket = "Ticket: " + ToXString(selectedPosition.ticket);
string posType = "Type: " + ToXString(selectedPosition.type);
string posSymbolPeriod =
"Symbol: " + selectedPosition.symbol + ", " +
"Period: " + ToXString(selectedPosition.period);
string posProfit = "Profit: " + ToXString(selectedPosition.profit);
string posRiskToRewardRatio = "RiskToReward: " + ToXString(selectedPosition.GetRiskRewardRatio());
string accountInfo =
"Balance: " + ToXString(eaTrader.mAccount.GetBalance()) + ", " +
"Equity: " + ToXString(eaTrader.mAccount.GetEquity());
//
string posInfos[];
//
// Ticket ...
Add(
posTicket,
posInfos //
);
//
// Type ...
Add(
posType,
posInfos //
);
//
// Symbol Period ...
Add(
posSymbolPeriod,
posInfos //
);
//
// Profit ...
Add(
posProfit,
posInfos //
);
//
// Risk to Reward Ratio ...
Add(
posRiskToRewardRatio,
posInfos //
);
//
// Add Positions Info if Exists ...
if (IsSpecifiedValid(positionsInfo))
{
//
Add(
positionsInfo,
posInfos //
);
//
// Clear Info Array ...
selectedPosInfoArr.Clear();
}
//
// Adding Account Info ...
Add(
accountInfo,
posInfos //
);
//
// Create Button If Not Exists ...
if (btnSelectedPosInfo == NULL)
{
//
btnSelectedPosInfo = new CButton();
//
string name = objSuffix + "BTN_TOOGLE_SPOS_INFO";
//
CRect iRect = btnTPPlus.Rect();
//
int x1 = iRect.left;
int x2 = x1 + 200 + 5;
//
int y1 = iRect.bottom + 5;
int y2 = y1 + iRect.Height();
//
bool isCreated = btnSelectedPosInfo.Create(
chartId,
name,
subWindow,
x1,
y1,
x2,
y2 //
);
if (!isCreated)
{
//
delete btnSelectedPosInfo;
ZeroMemory(btnSelectedPosInfo);
}
else
{
//
// Apply Styles ...
btnSelectedPosInfo.Color(clrYellow);
btnSelectedPosInfo.ColorBackground(clrDarkBlue);
}
}
//
// Check Btn Exists ...
has = btnSelectedPosInfo != NULL;
if (has)
{
//
int posInfoCount = ArraySize(posInfos);
int lblInfoCount = selectedPosInfoArr.Total();
has = IsValidSize(lblInfoCount) && lblInfoCount == posInfoCount;
//
// Create info Labels ...
if (!has)
{
//
selectedPosInfoArr.Clear();
has = IsValidSize(posInfoCount);
if (has)
{
//
// Creat Info Control ...
//
string name = objSuffix + "LBL_SPOS_INFO";
//
int lblHeight = 25;
int lblWidth = 450;
//
int startX = chartWidth - lblWidth - 5;
int sizeX = startX + lblWidth;
//
int startY = 5;
int sizeY = startY + lblHeight;
//
// Loop through Position Info's Array to Create Label Controls ...
for (int i = 0; i < posInfoCount; i++)
{
//
string iText = posInfos[i];
string iTextMD5 = ToMD5(iText);
//
string iName = name + "_" + iTextMD5;
//
int iX1 = startX;
int iX2 = sizeX;
//
int iY1 = startY;
int iY2 = startY + lblHeight;
//
CEdit *iLbl = new CEdit();
bool isCreated = iLbl.Create(
chartId,
iName,
subWindow,
iX1,
iY1,
iX2,
iY2 //
);
if (isCreated)
{
//
iLbl.Text(iText);
iLbl.Color(clrYellow);
iLbl.ColorBorder(clrDarkSlateGray);
iLbl.ColorBackground(clrDarkSlateGray);
//
if (showSelectedPositionInfo)
{
iLbl.Show();
}
else
{
iLbl.Hide();
}
//
selectedPosInfoArr.Add(iLbl);
//
startY = iY2;
}
}
}
}
//
// Update Info Labels ...
lblInfoCount = selectedPosInfoArr.Total();
has = IsValidSize(lblInfoCount);
if (has)
{
//
CEdit *iLbl = NULL;
//
// Loop Through Infos ...
for (int i = 0; i < lblInfoCount; i++)
{
//
iLbl = (CEdit *)selectedPosInfoArr.At(i);
//
iLbl.Text(posInfos[i]);
}
//
bool isVisible = showSelectedPositionInfo;
string btnText = isVisible
? "Hide Info"
: "Show Info";
btnSelectedPosInfo.Text(btnText);
//
ZeroMemory(iLbl);
}
}
//
ZeroMemory(posInfos);
}
//
SpecifiedClean(positions);
}
else
{
//
// Toggle Selected Position Btn ...
if (btnSelectedPosInfo != NULL)
{
//
btnSelectedPosInfo.Destroy();
//
delete btnSelectedPosInfo;
ZeroMemory(btnSelectedPosInfo);
}
//
// Selected Position Lbls ...
selectedPosInfoArr.Clear();
}
//
// Update State of BtnClose and BtnClose All ...
if (selectedPositionID == 0)
{
//
btnClose.Disable();
btnClose.ColorBackground(clrDarkGray);
//
lblTP.Disable();
lblTP.ColorBackground(clrDarkGray);
//
btnTPPlus.Disable();
btnTPPlus.ColorBackground(clrDarkGray);
//
btnTPPlusPlus.Disable();
btnTPPlusPlus.ColorBackground(clrDarkGray);
//
btnTPMinus.Disable();
btnTPMinus.ColorBackground(clrDarkGray);
//
btnTPMinusMinus.Disable();
btnTPMinusMinus.ColorBackground(clrDarkGray);
//
lblSL.Disable();
lblSL.ColorBackground(clrDarkGray);
//
btnSLPlus.Disable();
btnSLPlus.ColorBackground(clrDarkGray);
//
btnSLPlusPlus.Disable();
btnSLPlusPlus.ColorBackground(clrDarkGray);
//
btnSLMinus.Disable();
btnSLMinus.ColorBackground(clrDarkGray);
//
btnSLMinusMinus.Disable();
btnSLMinusMinus.ColorBackground(clrDarkGray);
}
else
{
//
btnClose.Enable();
btnClose.ColorBackground(clrDarkOrange);
//
lblTP.Enable();
lblTP.ColorBackground(clrGreen);
//
btnTPPlus.Enable();
btnTPPlus.ColorBackground(clrGreen);
//
btnTPPlusPlus.Enable();
btnTPPlusPlus.ColorBackground(clrGreen);
//
btnTPMinus.Enable();
btnTPMinus.ColorBackground(clrDarkRed);
//
btnTPMinusMinus.Enable();
btnTPMinusMinus.ColorBackground(clrDarkRed);
//
lblSL.Enable();
lblSL.ColorBackground(clrDarkRed);
//
btnSLPlus.Enable();
btnSLPlus.ColorBackground(clrGreen);
//
btnSLPlusPlus.Enable();
btnSLPlusPlus.ColorBackground(clrGreen);
//
btnSLMinus.Enable();
btnSLMinus.ColorBackground(clrDarkRed);
//
btnSLMinusMinus.Enable();
btnSLMinusMinus.ColorBackground(clrDarkRed);
}
}
/**
* Update Position's Selector Buttons ...
*/
void UpdatePositionButtons()
{
//
// Retrieve Positions ...
XPosition positions[];
int count = eaTrader.GetPositions(
positions //
);
bool has = IsValidSize(count);
if (!has)
{
//
posArray.Clear();
selectedPositionID = 0;
UpdateGUIState();
return;
}
//
posArray.Clear();
//
// Selected Position ID ...
bool isSelectdPositionIDExists = false;
if (has && selectedPositionID > 0)
{
//
for (int i = 0; i < count; i++)
{
//
if (!isSelectdPositionIDExists &&
positions[i].ticket == selectedPositionID)
{
//
isSelectdPositionIDExists = true;
break;
}
}
}
if (!isSelectdPositionIDExists ||
selectedPositionID == 0)
{
//
XPosition youngest;
int youngestIDX = GetXYoungest(youngest, positions);
bool hasYoungest = IsValidIndex(youngestIDX) &&
youngest.IsValid();
if (hasYoungest)
{
selectedPositionID = youngest.ticket;
}
//
youngest.Clean();
}
//
int subWindow = 0;
ulong chartId = eaChartHelper.ChartId();
//
int btnWidth = 25;
int btnHeight = 25;
//
CRect iRect = lblPositionId.Rect();
//
int lastX = iRect.left + iRect.Width() + 5;
int lastSizeX = lastX + btnWidth;
//
int lastY = iRect.top;
int lastSizeY = lastY + iRect.Height();
//
for (int i = 0; i < count; i++)
{
//
XPosition iPos = positions[i];
//
string posTypeStr = IsLong(iPos.type)
? "LONG"
: "SHORT";
//
int iPosBtnX = lastX;
int iPosBtnSizeX = iPosBtnX + btnWidth;
string iPosBtnName = objSuffix + "_" + posTypeStr + "_POS_SELECT_BTN_" + ToXString(iPos.ticket);
//
CButton *iPosBtn = new CButton();
has = iPosBtn.Create(
chartId,
iPosBtnName,
subWindow,
iPosBtnX,
lastY,
iPosBtnSizeX,
lastSizeY //
);
if (has)
{
//
// Apply Styles ...
string iPosBtnText = ToXString(iPos.ticket);
//
iPosBtn.Text(iPosBtnText);
iPosBtn.Color(clrWhite);
//
color iPosBtnBG = IsLong(iPos.type)
? clrGreen
: clrDarkRed;
iPosBtn.ColorBackground(iPosBtnBG);
//
lastX = iPosBtnSizeX + 5;
//
if (selectedPositionID == iPos.ticket)
{
iPosBtn.Disable();
}
//
posArray.Add(iPosBtn);
}
//
iPos.Clean();
}
//
ZeroMemory(positions);
UpdateGUIState();
}
//
// GUI Event Handlers ...
//
void HandleBuyTrade()
{
HandleTrade(X_DIRECTION_BULLISH);
}
//
void HandleSellTrade()
{
HandleTrade(X_DIRECTION_BEARISH);
}
//
void HandleTrade(ENUM_X_DIRECTION dir)
{
//
if (!HasDirection(dir))
{
return;
}
//
NormalizeProps();
//
bool isBullish = IsSpecifiedBullish(dir);
//
double r2r = riskToRewardRatio;
double _volume = volume;
double _riskInPoints = riskInPoint;
double point = GetPoints(symbol);
double entry = GetEntry(symbol, dir);
//
double risk = _riskInPoints * point;
double reward = risk * r2r;
//
double sl = isBullish
? entry - risk
: entry + risk;
double tp = isBullish
? entry + reward
: entry - reward;
//
string comment = ShortName + "_POS_" + ToXString(dir);
//
if (isBullish)
{
//
eaTrader.Buy(
symbol,
period,
_volume,
entry,
sl,
tp,
comment //
);
}
else
{
//
eaTrader.Sell(
symbol,
period,
_volume,
entry,
sl,
tp,
comment //
);
}
}
//
void HandleCloseTrade(ulong positionID)
{
//
if (positionID == 0)
{
//
XPosition positions[];
int count = eaTrader.GetPositions(
positions //
);
bool has = IsValidSize(count);
if (has)
{
//
int closedCount = eaTrader.Close(positions);
has = IsValidSize(closedCount);
}
}
else
{
//
XPosition position;
bool has = eaTrader.GetPosition(
positionID,
position //
);
if (has)
{
//
has = eaTrader.Close(positionID);
}
}
}
//
void HandleTPChange(
ENUM_X_DIRECTION dir,
int multiplier = 1 //
)
{
//
// Validate ...
if (selectedPositionID == 0 || !HasDirection(dir))
{
return;
}
//
NormalizeProps();
//
// Normalize Multiplier ...
multiplier = NormalizeInt(multiplier, 1, 2);
//
// Select Position ...
XPosition position;
bool has = eaTrader.GetPosition(
selectedPositionID,
position //
);
if (!has)
{
//
position.Clean();
return;
}
//
bool isLong = IsLong(position.type);
bool isBullish = IsSpecifiedBullish(dir);
//
double point = GetPoints(position.symbol);
double stepValue = multiplier * (point * allowedTPSLStep);
//
bool mustIncrease =
isBullish
? isLong
: !isLong;
//
double tp = position.tp;
tp =
mustIncrease
? tp + stepValue
: tp - stepValue;
bool isModified = eaTrader.Modify(
position.ticket,
position.sl,
tp,
objSuffix + "Change TP" //
);
if (isModified)
{
UpdateGUIState();
}
//
position.Clean();
}
//
void HandleSLChange(
ENUM_X_DIRECTION dir,
int multiplier = 1 //
)
{
//
// Validate ...
if (selectedPositionID == 0 || !HasDirection(dir))
{
return;
}
//
NormalizeProps();
//
// Normalize Multiplier ...
multiplier = NormalizeInt(multiplier, 1, 2);
//
// Select Position ...
XPosition position;
bool has = eaTrader.GetPosition(
selectedPositionID,
position //
);
if (!has)
{
//
position.Clean();
return;
}
//
bool isLong = IsLong(position.type);
bool isBullish = IsSpecifiedBullish(dir);
//
double point = GetPoints(position.symbol);
double stepValue = multiplier * (point * allowedTPSLStep);
//
bool mustIncrease =
isBullish
? !isLong
: isLong;
//
double risk = position.GetRiskInPoint();
if (risk <= minAllowedRiskInPoint)
{
//
position.Clean();
return;
}
//
double sl = position.sl;
sl =
mustIncrease
? sl + stepValue
: sl - stepValue;
bool isModified = eaTrader.Modify(
position.ticket,
sl,
position.tp,
objSuffix + "Change SL" //
);
if (isModified)
{
UpdateGUIState();
}
//
position.Clean();
}
//
// Custom Tools ...
//
void NormalizeProps()
{
//
if (volume < 0.01)
{
volume = 0.01;
}
//
if (riskInPoint < 0)
{
riskInPoint = 50;
}
//
if (allowedTPSLStep < 5)
{
allowedTPSLStep = 5;
}
//
if (riskToRewardRatio < 0.5)
{
riskToRewardRatio = 2;
}
//
if (minAllowedRiskInPoint < 10)
{
minAllowedRiskInPoint = 10;
}
}
//
ulong ExtractPositionIDFromButtonName(string name)
{
//
ulong result = 0;
//
string nameParts[];
int namePartsCount = SplitContent(nameParts, name, "_");
bool has = IsValidSize(namePartsCount);
if (has)
{
//
string lastPart = nameParts[namePartsCount - 1];
StringReplace(lastPart, "_", "");
result = (long)lastPart;
}
//
ZeroMemory(nameParts);
//
return result;
}
};
//