diff --git a/Classes/x-saherelm.x-chart.helper.class.mq5 b/Classes/x-saherelm.x-chart.helper.class.mq5 new file mode 100644 index 00000000..9a647cf3 --- /dev/null +++ b/Classes/x-saherelm.x-chart.helper.class.mq5 @@ -0,0 +1,338 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Class Library +// -------------------------------------- +// Name: XCChartHelper +// Description: Chart Helper Tools ... +// +// 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" + +// +// Implementation ... +class XCChartHelper : public XCBase +{ + // + // Public ... + public: + // + + // + // Constructor ... + XCChartHelper(ulong _chartId = 0) + { + // + // Normalize Chart ID ... + mChartID = NormalizeChartId(_chartId); + } + + // + // Desconstructor ... + ~XCChartHelper() + { + } + + // + // Actioins ... + + /** + * Redraw Specified Chart ... + */ + void Redraw() + { + ChartRedraw(mChartID); + } + + /** + * Chart Navigation ... + * + * @param position: ENUM_CHART_POSITION + * @param shift: int + * + * @return ( bool ) + */ + bool Navigate( + const ENUM_CHART_POSITION position, + const int shift // + ) + { + return (ChartNavigate(mChartID, position, shift)); + } + + /** + * Apply a Template to Chart ... + * + * @param filename: string file path ... + * + * @return ( bool ) + */ + bool ApplyTemplate(const string filename) const + { + return (ChartApplyTemplate(mChartID, filename)); + } + + /** + * Take an Screenshot of Chart ... + * + * @param filename: string file path ... + * @param width: int ... + * @param height: int ... + * @param align_mode: ENUM_ALIGN_MODE ... + * + * @return ( bool ) + */ + bool ScreenShot( + const string filename, + const int width, + const int height, + const ENUM_ALIGN_MODE align_mode // + ) const + { + return (ChartScreenShot(mChartID, filename, width, height, align_mode)); + } + + /** + * Writing parameters of chart to file ... + **/ + bool Save(const int file_handle) + { + // + string work_str; + int work_int; + //--- check + if (file_handle == INVALID_HANDLE || mChartID == -1) + return (false); + //--- write start marker - 0xFFFFFFFFFFFFFFFF + if (FileWriteLong(file_handle, -1) != sizeof(long)) + return (false); + //--- write chart type + if (FileWriteInteger(file_handle, Type(), INT_VALUE) != INT_VALUE) + return (false); + //--- write chart symbol + work_str = Symbol(); + work_int = StringLen(work_str); + if (FileWriteInteger(file_handle, work_int, INT_VALUE) != INT_VALUE) + return (false); + if (work_int != 0) + if (FileWriteString(file_handle, work_str, work_int) != work_int) + return (false); + //--- write period of chart + if (FileWriteInteger(file_handle, Period(), INT_VALUE) != sizeof(int)) + return (false); + //--- write value of the "Mode" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_MODE), INT_VALUE) != sizeof(int)) + return (false); + //--- write value of the "Foreground" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_FOREGROUND), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "Shift" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHIFT), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShiftSize" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHIFT), INT_VALUE) != sizeof(int)) + return (false); + //--- write value of the "AutoScroll" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_AUTOSCROLL), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "Scale" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALE), INT_VALUE) != sizeof(int)) + return (false); + //--- write value of the "ScaleFix" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALEFIX), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ScaleFix_11" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALEFIX_11), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "FixedMax" property + if (FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_FIXED_MAX)) != sizeof(double)) + return (false); + //--- write value of the "FixedMin" property + if (FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_FIXED_MIN)) != sizeof(double)) + return (false); + //--- write the "ScalePPB" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SCALE_PT_PER_BAR), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "PointsPerBar" property + if (FileWriteDouble(file_handle, ChartGetDouble(mChartID, CHART_POINTS_PER_BAR)) != sizeof(double)) + return (false); + //--- write value of the "ShowOHLC" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_OHLC), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowLineBid" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_BID_LINE), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowLineAsk" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_ASK_LINE), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowLastLine" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_LAST_LINE), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowPeriodSep" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_PERIOD_SEP), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowGrid" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_GRID), CHAR_VALUE) != sizeof(char)) + return (false); + //--- write value of the "ShowVolumes" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_VOLUMES), INT_VALUE) != sizeof(int)) + return (false); + //--- write value of the "ShowObjectDescr" property + if (FileWriteInteger(file_handle, (int)ChartGetInteger(mChartID, CHART_SHOW_OBJECT_DESCR), CHAR_VALUE) != sizeof(char)) + return (false); + //--- successful + return (true); + } + + // + // Properties ... + + /** + * Get Chart ID ... + * + * @return ( ulong ) + */ + ulong ChartId() + { + return mChartID; + } + + /** + * Get Object Type ... + * + * @return ( int ) + **/ + int Type() const + { + return (0x1111); + } + + /** + * Get Chart Mode ... + * + * @return ( ENUM_CHART_MODE ) + **/ + ENUM_CHART_MODE Mode() + { + return ((ENUM_CHART_MODE)ChartGetInteger(mChartID, CHART_MODE)); + } + + /** + * Get Chart Symbol ... + * + * @return ( string ) + */ + string Symbol() + { + return (ChartSymbol(mChartID)); + } + + /** + * Get Chart Period ... + * + * @return ( ENUM_TIMEFRAMES ) + */ + ENUM_TIMEFRAMES Period() + { + return (ChartPeriod(mChartID)); + } + + /** + * Count Chart Visible Bars ... + * + * @return ( int ) + */ + int VisibleBars() + { + return ((int)ChartGetInteger(mChartID, CHART_WIDTH_IN_BARS)); + } + + /** + * Get Chart First Visible Bars Index ... + * + * @return ( int ) + */ + int FirstVisibleBar() + { + return ((int)ChartGetInteger(mChartID, CHART_FIRST_VISIBLE_BAR)); + } + + /** + * Get Chart Last Visible Bars Index ... + * + * @return ( int ) + */ + int LastVisibleBar() + { + // + int first = FirstVisibleBar(); + int count = VisibleBars(); + int result = first + count; + return result; + } + + /** + * Get Chart's Specified SubWindows Minimum Price ... + * + * @param subWindow: int + * @return ( double ) + */ + double PriceMin(int subWindow = 0) + { + // + // Normalize Subwindow ... + if (subWindow <= 0) + { + subWindow = 0; + } + + // + return (ChartGetDouble(mChartID, CHART_PRICE_MIN, subWindow)); + } + + /** + * Get Chart's Specified SubWindows Maximum Price ... + * + * @param subWindow: int + * @return ( double ) + */ + double PriceMax(int subWindow = 0) + { + // + // Normalize Subwindow ... + if (subWindow <= 0) + { + subWindow = 0; + } + + // + return (ChartGetDouble(mChartID, CHART_PRICE_MAX, subWindow)); + } + + // + // Protected ... + protected: + // + // Private ... + private: + // + // Props ... + + // + ulong mChartID; // Chart ID ... +}; + +// \ No newline at end of file diff --git a/Documents/BKP/tmp.backtester.codes.mq5 b/Documents/BKP/tmp.backtester.codes.mq5 new file mode 100644 index 00000000..5a330f4b --- /dev/null +++ b/Documents/BKP/tmp.backtester.codes.mq5 @@ -0,0 +1,646 @@ + +void UpdateMainPanel() +{ + // + // Validate ... + if (mChartHelper == NULL || + mainPanel == NULL) + { + return; + } + + // + bool has = false; + + // + // Update Main Panel Position ... + XObjPosition mainPanelPosition; + CalculateMainPanelPosition(mainPanelPosition); + if (mainPanelPosition.IsValidForRectangle()) + { + // + has = mainPanel.ShiftPoint(1, mainPanelPosition.fromTime, mainPanelPosition.fromPrice); + if (has) + { + has = mainPanel.ShiftPoint(2, mainPanelPosition.toTime, mainPanelPosition.toPrice); + } + } +} + +/** + * Calculate Main Window Position ... + * + * @param position: XObjPosition + */ +void CalculateMainPanelPosition(XObjPosition &position) +{ + // + position.Clean(); + + // + // Reading Chart Helper Requirements ... + + // + string mChartSymbol = mChartHelper.Symbol(); + double mPoint = GetPoints(mChartSymbol); + + // + ENUM_TIMEFRAMES mChartPeriod = mChartHelper.Period(); + int mChartPeriodSeconds = PeriodSeconds(mChartPeriod); + + // + double mChartMinPrice = mChartHelper.PriceMin(); + double mChartMaxPrice = mChartHelper.PriceMax(); + int mChartVisibleBarsCount = mChartHelper.VisibleBars(); + int mChartFirstBarIndex = mChartHelper.FirstVisibleBar(); + + // + datetime mChartFirstBarTime = GetBarTime( + mChartSymbol, + mChartPeriod, + mChartFirstBarIndex // + ); + + // + position.fromTime = mChartFirstBarTime + (mChartPeriodSeconds); + position.fromPrice = mChartMaxPrice - (10 * mPoint); + + // + position.toTime = position.fromTime + (40 * mChartPeriodSeconds); + position.toPrice = position.fromPrice - (20 * mPoint); + + // + Print(" "); + + // + Print("From Time: ", position.fromTime); + Print("To Time: ", position.toTime); + + // + Print(" "); + + // + Print("From Price: ", position.fromPrice); + Print("To Price: ", position.toPrice); +} + +/////////////////////////////////////////////////////// +// +// SaherElm IT Center XTest MQL5 Expert Advisor +// ------------------------------------------------- +// Name: XBackTester +// Description: a Backtest Tools Strategy Tester +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +//////////////////////////////////////////////////////x +// +// Global Properties ... +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://saherelm.ir" +#property version "1.00" +#property description "SaherElm XBackTester" +#property strict + +// +#define ShortName "XBackTester" + +// +// Imports ... +#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" + +// +// Import Chart Objects ... +#include +#include +#include +#include +#include +#include +#include + +// +// Definitions ... +struct XObjPosition +{ + // + // Props ... + datetime fromTime; + double fromPrice; + datetime toTime; + double toPrice; + + // + // Constructor ... + XObjPosition() + { + Clean(); + } + + // + // Tools ... + + // + // Cleanup ... + void Clean() + { + // + toPrice = 0; + fromPrice = 0; + + // + toTime = NULL; + fromTime = NULL; + } + + // + // Validate XWindowPosition for Rectangle ... + bool IsValidForRectangle() + { + // + bool result = + // + toPrice > 0 && + fromPrice > 0 && + IsSpecifiedValid(toTime) && + IsSpecifiedValid(fromTime) && + fromTime < toTime + // + ; + + // + return result; + } +}; + +// +// Inputs ... + +// +// Common ... +input group "Common"; +input long eaMagicNumber = 78692110; // Magic Number +input int eaSlippage = 10; // Slippgae +input string eaLogSuffix = ""; // Log Suffix + +// +// Alert ... +// Here we Provider All Requirements for +// Configuring Alert Provider ... +input group "Alert"; +input bool eAEnableAlerts = true; // Enable Alerts +input bool eALogAlerts = true; // Log Alerts +input bool eAMailAlerts = false; // Mail Alerts +input bool eAPushAlerts = false; // Push Alerts +input bool eATerminalAlerts = false; // Terminal Alerts +// +// Define Local Variables ... + +// +string eaLogTag; // EA Log Prefix ... +XCAlert *eaAlert; // Alert Provider for Expert Adviser ... +XCTrade *eaTrader; // Trader of Expert Adviser ... + +// +// Chart Objects and Properties ... +XCChartHelper *mChartHelper; + +// +// Chart Object Class ... + +// +string objSuffix; + +// +// Main Panel ... +string mainPanelName; +CChartObjectRectLabel *mainPanel; // Main Panel ... +string buyButtonName; +CChartObjectButton *buyButton; // Buy Button ... +string sellButtonName; +CChartObjectButton *sellButton; // Sell Button ... + +// +// Event Handlers ... + +// +// Initialization ... +int OnInit() +{ + // + // Validate Inputs and Required Parameters + // for EA to Works fine ... + if (!ValidateInputs()) + { + return INIT_PARAMETERS_INCORRECT; + } + + // + // Initialize Timer ... + if (!InitTimer()) + { + return INIT_FAILED; + } + + // + // Initialize all Requirements ... + if (!InitEA()) + { + return INIT_FAILED; + } + + // + // Initialize all GUI Objects and Classes ... + if (!InitGUI()) + { + return INIT_FAILED; + } + + // + // Alert Initialization ... + string msg = "Initialized Successfully ..."; + eaAlert.SendAlert(msg); + + // + // Init Succeed ... + return INIT_SUCCEEDED; +} + +// +// DeInitialization ... +void OnDeinit(const int reason) +{ + // + // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function + // REASON_REMOVE 1 Program removed from a chart + // REASON_RECOMPILE 2 Program recompiled + // REASON_CHARTCHANGE 3 A symbol or a chart period is changed + // REASON_CHARTCLOSE 4 Chart closed + // REASON_PARAMETERS 5 Inputs changed by a user + // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings + // REASON_TEMPLATE 7 Another chart template applied + // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value + // REASON_CLOSE 9 Terminal closed + + // + // Destroy Timer ... + DestroyTimer(); + + // + // Destroy all GUI Requirements ... + DestroyGUI(); + + // + // Alert De Initialization Succeeded ... + string msg = "De Initialized Successfully ..."; + eaAlert.SendAlert(msg); + + // + // Destroy all EA Requirements ... + DestroyEA(); +} + +// +// On Tick Handler ... +void OnTick() +{ + // + UpdateGUI(); +} + +// +// Handle On Trade Event ... +void OnTrade() +{ + eaTrader.HandleOnTrade(); +} + +// +// On Timer ... +void OnTimer() +{ + // + // TODO: Implement Time Based Functionalities ... +} + +// +// Chart Event Handler ... +void OnChartEvent( + const int id, + const long &lparam, + const double &dparam, + const string &sparam // +) +{ + // + // Apply Chart Event on Main Window ... + if (id == CHARTEVENT_OBJECT_CLICK) + { + // + if (mainPanel != NULL && sparam == mainPanelName) + { + // + Print("Main Panel Clicked ..."); + } + } +} + +// +// Custom Functions ... + +/** + * Validate all Inputs of Expert Adviser ... + * + * @return ( bool ) + */ +bool ValidateInputs() +{ + // + bool result = false; + + // + string errMessage = ""; + + // + bool isCommonValid = ( + // + eaSlippage > 0 && + eaMagicNumber > 0 + // + ); + if (!isCommonValid) + { + // + errMessage += "common configurations error;" + "\n"; + } + + // + result = + // + // Common ... + isCommonValid && + // + // Otere ... + true + // + ; + + // + if (!result) + { + // + // Since here Logger not Initiallized, we Use Raw Print Command ... + Print(ShortName + " Errors: \n", errMessage); + } + + // + return result; +} + +/** + * Initialize Timer ... + * + * @return ( bool ) + */ +bool InitTimer() +{ + // + bool result = false; + + // + // TODO: Initialize Timer if necessary ... + // result = EventSetTimer(1); + // result = EventSetMillisecondTimer(100); + result = true; + + // + return result; +} + +/** + * Destroy Timer ... + */ +void DestroyTimer() +{ + // + // TODO: Use This ... + // EventKillTimer(); +} + +/** + * Initialize all Requirements for Working Fine ... + * + * @return ( bool ) + */ +bool InitEA() +{ + // + bool result = false; + + // + // Locally Apply Changes on Prefix ... + eaLogTag = ShortName + ">" + eaLogSuffix; + + // + // Create Instance of Alert Class and Configure it ... + eaAlert = new XCAlert(); + eaAlert.SetPrefix(eaLogTag); + eaAlert.SetLogAlerts(eALogAlerts); + eaAlert.SetMailAlerts(eAMailAlerts); + eaAlert.SetPushAlerts(eAPushAlerts); + eaAlert.SetEnableAlerts(eAEnableAlerts); + eaAlert.SetTerminalAlerts(eATerminalAlerts); + + // + // Create Trader Instance and Configure it ... + eaTrader = new XCTrade( + eaSlippage, + eaMagicNumber // + ); + + // + // TODO: Fix This ... + result = true; + + // + return result; +} + +/** + * Destroy all Initialized EA Requirements ... + */ +void DestroyEA() +{ + // + delete eaAlert; +} + +// +// GUI Events ... + +/** + * Initialize all GUI Requirements for EA ... + * + * @return ( bool ) + */ +bool InitGUI() +{ + // + bool result = false; + + // + int subWindow = 0; + ulong chartId = ChartID(); + objSuffix = ShortName + "_"; + + // + // Initial Chart Class Instance ... + mChartHelper = new XCChartHelper(chartId); + + // + // Create Main Panel ... + result = CreateMainPanel(); + + // + + // + return result; +} + +/** + * Update GUI based on Each Ticks ... + */ +void UpdateGUI() +{ + // + // UpdateMainPanel(); +} + +/** + * Destroy all Initialized GUi Requirements ... + */ +void DestroyGUI() +{ + // + delete mChartHelper; + ZeroMemory(mChartHelper); + + // + delete mainPanel; + ZeroMemory(mainPanel); +} + +// +// Custom Helper Functions ... + +bool CreateMainPanel() +{ + // + bool result = false; + + // + if (mChartHelper == NULL) + { + return result; + } + + // + int subWindow = 0; + + // + mainPanelName = objSuffix + "Main_Panel"; + mainPanel = new CChartObjectRectLabel(); + result = mainPanel.Create( + mChartHelper.ChartId(), + mainPanelName, + subWindow, + 10, + 25, + 400, + 200 // + ); + if (!result) + { + return result; + } + + // + // Styling Main Panel ... + mainPanel.BackColor(clrDarkGray); + mainPanel.Tooltip(mainPanelName); + + // + // Create Buttons ... + buyButtonName = objSuffix + "BTN_BUY"; + buyButton = new CChartObjectButton(); + result = buyButton.Create( + mChartHelper.ChartId(), + buyButtonName, + subWindow, + 10, + 25, + 200, + 100 // + ); + if (!result) + { + return result; + } + + // + // Apply Style of Buy Button ... + + if (result) + { + } + + // + return result; +} + +////////////////////////////////////////////////// + +// +// class XCEdit : public CEdit { +// // +// public: +// // +// string mValue; + +// // +// bool OnSetText() { +// // +// Print("Object Edit End ..."); +// return true; +// } +// }; + +// CEdit edtPositionId; + +// // +// // EDIT Position ID ... +// int edtPositionIdStartX = btnSellStartX; +// int edtPositionIdSizeX = edtPositionIdStartX + 100; +// int edtPositionIdStartY = btnSellSizeY + 5; +// int edtPositionIdSizeY = edtPositionIdStartY + 50; +// string edtPositionIdName = objSuffix + "EDT_POS_ID"; +// result = edtPositionId.Create( +// chartId, +// edtPositionIdName, +// subWindow, +// edtPositionIdStartX, +// edtPositionIdStartY, +// edtPositionIdSizeX, +// edtPositionIdSizeY // +// ); +// if (result) +// { +// // +// // Apply Edit Position ID Style ... +// edtPositionId.Text(ToXString(selectedPositionID)); +// edtPositionId.TextAlign(ALIGN_LEFT); +// } diff --git a/Experts/x-saherelm.backtester.mq5 b/Experts/x-saherelm.backtester.mq5 index 103149e2..df6f8bd0 100644 --- a/Experts/x-saherelm.backtester.mq5 +++ b/Experts/x-saherelm.backtester.mq5 @@ -24,9 +24,26 @@ // // Imports ... #include "../Classes/x-saherelm.x-alert.class.mq5" -#include "../Classes/x-saherelm.x-rm.panel.class.mq5" +#include "../Classes/x-saherelm.x-chart.helper.class.mq5" #include "../Classes/x-saherelm.x-trade.class.mq5" +// +// Import Chart Objects ... +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// +// Definitions ... + // // Inputs ... @@ -54,7 +71,32 @@ input bool eATerminalAlerts = false; // Terminal Alerts string eaLogTag; // EA Log Prefix ... XCAlert *eaAlert; // Alert Provider for Expert Adviser ... XCTrade *eaTrader; // Trader of Expert Adviser ... -XCRMPanel eaMainWindow; // Main Window ... +XCChartHelper *mChartHelper; // Chart Helper Class ... + +// +// Object Props ... + +// +string objSuffix; + +// +CButton btnBuy; +CButton btnSell; +CButton btnClose; +CButton btnCloseAll; +CButton btnSelectedPosInfo; + +// +CEdit lblPositionId; +int lblPositionIdSizeX = 0; +int lblPositionIdSizeY = 0; +int lblPositionIdStartX = 0; +int lblPositionIdStartY = 0; +ulong selectedPositionID = 0; + +// +// Position Arrays ... +CArrayObj posArray; // // Event Handlers ... @@ -148,6 +190,7 @@ void OnTick() // Handle On Trade Event ... void OnTrade() { + // eaTrader.HandleOnTrade(); } @@ -168,9 +211,6 @@ void OnChartEvent( const string &sparam // ) { - // - // Apply Chart Event on Main Window ... - eaMainWindow.ChartEvent(id, lparam, dparam, sparam); } // @@ -285,7 +325,19 @@ bool InitEA() eaSlippage, eaMagicNumber // ); - + + // + // Attach Event Handlers ... + eaTrader.AddOnDealsChangedEventHandler(OnDealsChanged); + eaTrader.AddOnStopLossEventHandler(OnStopLossTriggered); + eaTrader.AddOnOrdersChangedEventHandler(OnOrdersChanged); + eaTrader.AddOnTakeProfitEventHandler(OnTakeProfitTriggered); + eaTrader.AddOnModifyPositionEventHandler(OnPositionModified); + eaTrader.AddOnPositionsChangedEventHandler(OnPositionsChanged); + eaTrader.AddOnTradeStateChangedEventHandler(OnTradeStateChanged); + eaTrader.AddOnForceClosePositionEventHandler(OnPositionForceClosed); + eaTrader.AddOnPartialClosePositionEventHandler(OnPositionPartialClosed); + // // TODO: Fix This ... result = true; @@ -317,24 +369,155 @@ bool InitGUI() bool result = false; // - // Initialize Application Main Window ... - result = eaMainWindow.Create( - ChartID(), - ShortName, // Dialog Name ... - 0, // SubWindow ... - 10, // X1 ... - 35, // Y1 ... - 300, // X2 ... - 200 // Y2 ... - ); + int subWindow = 0; + ulong chartId = ChartID(); + objSuffix = ShortName + "_"; // - // Try To Run Main Window ... + // Initial Chart Class Instance ... + mChartHelper = new XCChartHelper(chartId); + + // + int startX = 5; + int startY = 25; + + // + // Buy ... + int btnBuyStartX = startX; + int btnBuySizeX = startX + 100; + int btnBuyStartY = startY; + int btnBuySizeY = startY + 50; + string btnBuyName = objSuffix + "BTN_BUY"; + result = btnBuy.Create( + chartId, + btnBuyName, + subWindow, + btnBuyStartX, + btnBuyStartY, + btnBuySizeX, + btnBuySizeY // + ); if (result) { - eaMainWindow.Run(); + // + // Apply Buy Button Style ... + + // + btnBuy.Text("Buy"); + btnBuy.Color(clrYellow); + btnBuy.ColorBackground(clrGreen); } + // + // Sell ... + int btnSellStartX = btnBuyStartX + btnBuySizeX; + int btnSellSizeX = btnSellStartX + 100; + int btnSellStartY = btnBuyStartY; + int btnSellSizeY = btnBuySizeY; + string btnSellName = objSuffix + "BTN_SELL"; + result = btnSell.Create( + chartId, + btnSellName, + subWindow, + btnSellStartX, + btnSellStartY, + btnSellSizeX, + btnSellSizeY // + ); + if (result) + { + // + // Apply Sell Button Style ... + + // + btnSell.Text("Sell"); + btnSell.Color(clrYellow); + btnSell.ColorBackground(clrDarkRed); + } + + // + // LABEL Position ID ... + lblPositionIdStartX = btnBuyStartX; + lblPositionIdSizeX = lblPositionIdStartX + 100; + lblPositionIdStartY = btnSellSizeY + 5; + lblPositionIdSizeY = lblPositionIdStartY + 50; + string lblPositionIdName = objSuffix + "LBL_POS_ID"; + result = lblPositionId.Create( + chartId, + lblPositionIdName, + subWindow, + lblPositionIdStartX, + lblPositionIdStartY, + lblPositionIdSizeX, + lblPositionIdSizeY // + ); + if (result) + { + // + // Apply Label Position ID Style ... + lblPositionId.ReadOnly(true); + lblPositionId.Text("Pos ID: "); + lblPositionId.Color(clrYellow); + lblPositionId.ColorBackground(clrDarkGray); + } + + // + // Close ... + int btnCloseStartX = startX; + int btnCloseSizeX = btnCloseStartX + 100; + int btnCloseStartY = lblPositionIdSizeY + 5; + int btnCloseSizeY = lblPositionIdSizeY + 50; + string btnCloseName = objSuffix + "BTN_CLOSE"; + result = btnClose.Create( + chartId, + btnCloseName, + subWindow, + btnCloseStartX, + btnCloseStartY, + btnCloseSizeX, + btnCloseSizeY // + ); + if (result) + { + // + // Apply Close Button Style ... + + // + btnClose.Text("Close"); + btnClose.Color(clrYellow); + btnClose.ColorBackground(clrDarkOrange); + } + + // + // Close All ... + int btnCloseAllStartX = btnCloseSizeX + 5; + int btnCloseAllSizeX = btnCloseAllStartX + 100; + int btnCloseAllStartY = lblPositionIdSizeY + 5; + int btnCloseAllSizeY = lblPositionIdSizeY + 50; + string btnCloseAllName = objSuffix + "BTN_CLOSE_ALL"; + result = btnCloseAll.Create( + chartId, + btnCloseAllName, + subWindow, + btnCloseAllStartX, + btnCloseAllStartY, + btnCloseAllSizeX, + btnCloseAllSizeY // + ); + if (result) + { + // + // Apply Close Button Style ... + + // + btnCloseAll.Text("Close All"); + btnCloseAll.Color(clrYellow); + btnCloseAll.ColorBackground(clrDarkRed); + } + + // + UpdateGUIState(); + // return result; } @@ -345,7 +528,94 @@ bool InitGUI() void UpdateGUI() { // - eaMainWindow.UpdateState(); + 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 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(); + Print("Changed Selected Pos ID: ", selectedPositionID); + } + } + + // + iBtn.Pressed(false); + } + + // + ZeroMemory(iBtn); + } + } } /** @@ -354,5 +624,448 @@ void UpdateGUI() void DestroyGUI() { // - eaMainWindow.Destroy(); + delete mChartHelper; + ZeroMemory(mChartHelper); + + // } + +// +// Trade Event Handlers ... + +/** + * Handle StopLoss Triggered Positions ... + * + * @param deal: XDeal instance ... + */ +void OnStopLossTriggered(const XDeal &deal) +{ +} + +/** + * Handle Take Profit Triggered Positions ... + * + * @param deal: XDeal instance ... + */ +void OnTakeProfitTriggered(const XDeal &deal) +{ +} + +/** + * Handle Force Close a Position ... + * + * @param ticket: ULONG ... + * @param position: XPosition instance ... + * @param comment: String ... + */ +void OnPositionForceClosed( + const ulong ticket, + const XPosition &position, + const string comment // +) +{ +} + +/** + * Handle Deals Changes ... + * + * @param count: Integer, Number of Changes ... + */ +void OnDealsChanged(int count) +{ +} + +/** + * Handle Orders Changes ... + * + * @param count: Integer, Number of Changes ... + */ +void OnOrdersChanged(int count) +{ +} + +/** + * Handle Positions Changes ... + * + * @param count: Integer, Number of Changes ... + */ +void OnPositionsChanged(int count) +{ + // + // Update Position Buttons ... + UpdatePositionButtons(); +} + +/** + * Handle Position Modified ... + * + * @param ticket: ulong, Position Ticket ... + * @param profit: double, Position Profit ... + * @param comment: string, Comment ... + */ +void OnPositionModified( + const ulong ticket, + const double profit, + const string comment // +) +{ +} + +/** + * Handle Position Partial Closed ... + * + * @param ticket: ulong, Position Ticket ... + * @param profit: double, Position Profit ... + * @param comment: string, Comment ... + */ +void OnPositionPartialClosed( + const ulong ticket, + const double profit, + const string comment // +) +{ +} + +/** + * Handle Trade State Changed ... + * + * @param state: XOnTradeHandlerState instance ... + */ +void OnTradeStateChanged(const XOnTradeHandlerState &state) +{ +} + +// +// GUI Event Handlers and Helpers ... + +// +void HandleBuyTrade() +{ + HandleTrade(X_DIRECTION_BULLISH); +} + +// +void HandleSellTrade() +{ + HandleTrade(X_DIRECTION_BEARISH); +} + +// +void HandleTrade(ENUM_X_DIRECTION dir) +{ + // + if (!HasDirection(dir)) + { + return; + } + + // + string symbol = mChartHelper.Symbol(); + ENUM_TIMEFRAMES period = mChartHelper.Period(); + + // + bool isBullish = IsSpecifiedBullish(dir); + + // + double r2r = 1.5; + double volume = 0.01; + double riskInPoints = 100; + 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) + { + // + // Force Close All ... + Print("Close All Positions ..."); + } + else + { + // + // Close Specified Position ... + Print("Close Position: ", positionID); + } +} + +// +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; + } + + // + SpecifiedClean(nameParts); + + // + return result; +} + +// +void UpdatePositionButtons() +{ + // + // Retrieve Positions ... + XPosition positions[]; + int count = eaTrader.GetPositions( + positions // + ); + bool has = IsValidSize(count); + if (!has) + { + // + posArray.Clear(); + selectedPositionID = 0; + return; + } + + // + posArray.Clear(); + + // + // Selected Position ID ... + if (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 = mChartHelper.ChartId(); + + // + int btnWidth = 25; + + // + int lastX = lblPositionIdSizeX + 5; + int lastSizeX = lastX + btnWidth; + + // + int lastY = lblPositionIdStartY; + int lastSizeY = lblPositionIdSizeY; + + // + 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(); + } + + // + SpecifiedClean(positions); + UpdateGUIState(); +} + +// +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 State of BtnClose and BtnClose All ... + if (selectedPositionID == 0) + { + // + btnClose.Disable(); + btnClose.ColorBackground(clrDarkGray); + } + else + { + // + btnClose.Enable(); + btnClose.ColorBackground(clrDarkOrange); + } +} + +// \ No newline at end of file diff --git a/Libraries/x-saherelm.common.extensions.lib.mq5 b/Libraries/x-saherelm.common.extensions.lib.mq5 index 0adecfe7..5ffe946e 100644 --- a/Libraries/x-saherelm.common.extensions.lib.mq5 +++ b/Libraries/x-saherelm.common.extensions.lib.mq5 @@ -570,6 +570,25 @@ double NormalizePrice( return result; } +/** + * Normalize Chart Id ... + * + * @param _chartId: ulong + * + * @return ( ulong ) + */ +ulong NormalizeChartId(ulong _chartId = 0) +{ + // + if (_chartId <= 0) + { + _chartId = ChartID(); + } + + // + return _chartId; +} + // // END Normalizations ... // diff --git a/Libraries/x-saherelm.x-trade.lib.mq5 b/Libraries/x-saherelm.x-trade.lib.mq5 index a90a11aa..7ac2a1c2 100644 --- a/Libraries/x-saherelm.x-trade.lib.mq5 +++ b/Libraries/x-saherelm.x-trade.lib.mq5 @@ -4248,7 +4248,7 @@ int GetYoungest( // int iAge = GetAge(iItem); - if (iAge > 0 && + if (iAge >= 0 && (tmpAge == 0 || tmpAge > iAge)) { // @@ -4258,7 +4258,7 @@ int GetYoungest( } // - if (tmpAge > 0) + if (tmpAge >= 0) { // result = tmpAge;