/////////////////////////////////////////////////////// // // 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 #include #include #include #include #include #include #include #include #include #include // // 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 = eaLogSuffix + "_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; } }; //