From d1e8daa131df320bc659fd6f3eac67f2c124c02f Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Mon, 22 Jul 2024 02:04:16 +0330 Subject: [PATCH] start implementing XTrade Handler Class ... --- Classes/x-saherelm.xstrategy.class.mq5 | 102 +++++-- Classes/x-saherelm.xtrade.class.mq5 | 311 ++++++++++++++++++++ Experts/x-test-strategies.ea.mq5 | 94 +++++- Libraries/x-saherelm.xtrade.lib.mq5 | 358 +++++++++++++++++++++++ Strategies/x-saherelm.xtest.strategy.mq5 | 26 +- 5 files changed, 853 insertions(+), 38 deletions(-) diff --git a/Classes/x-saherelm.xstrategy.class.mq5 b/Classes/x-saherelm.xstrategy.class.mq5 index e08d6c58..db5ce14e 100644 --- a/Classes/x-saherelm.xstrategy.class.mq5 +++ b/Classes/x-saherelm.xstrategy.class.mq5 @@ -46,18 +46,19 @@ public: // // Constructors ... void XSCBaseStrategy( - string _symbol, // Trading Symbol - ENUM_TIMEFRAMES _period, // Trading TimeFrame - double _volume, // Voluem - double _r2r, // Risk/Reward Ratio - int _slippage, // Trader Slippage - long _magicNumber, // Trader Magic Number - bool _useTPSLAsPoint = false, // Converts TP and SL by Provided Points - bool _forceMaxTPSLAsPoint = false, // Force Max TP and SL by Provided Points - double _tpPoint = 0, // TP As Point - double _slPoint = 0, // SL As Point - bool _ignoreTP = false, // TP set to 0 - bool _ignoreSL = false, // SL set to 0 + string _symbol, // Trading Symbol + ENUM_TIMEFRAMES _period, // Trading TimeFrame + double _volume, // Voluem + double _r2r, // Risk/Reward Ratio + int _slippage, // Trader Slippage + long _magicNumber, // Trader Magic Number + bool _ignoreSignalExecution = false, // Ignore Signal Execution + bool _useTPSLAsPoint = false, // Converts TP and SL by Provided Points + bool _forceMaxTPSLAsPoint = false, // Force Max TP and SL by Provided Points + double _tpPoint = 0, // TP As Point + double _slPoint = 0, // SL As Point + bool _ignoreTP = false, // TP set to 0 + bool _ignoreSL = false, // SL set to 0 bool _allowLong = true, bool _allowShort = true, int _maxAllowedLongs = 0, @@ -82,6 +83,7 @@ public: mMaxAllowedLongs = _maxAllowedLongs; mMaxAllowedShorts = _maxAllowedShorts; mForceMaxTPSLAsPoint = _forceMaxTPSLAsPoint; + mIgnoreSignalExecution = _ignoreSignalExecution; // mForceDisabled = false; @@ -371,6 +373,29 @@ public: mIgnoreSL = value; } + // + bool IgnoreSignalExecution() + { + return mIgnoreSignalExecution; + } + + // + void IgnoreSignalExecution(bool value) + { + mIgnoreSignalExecution = value; + } + + // + // On Signal Handlers ... + void AddSignalEventHandler(TOnSignal handler) + { + // + Add( + handler, + mOnSignalEventHandlers // + ); + } + // // Virtual Methods ... @@ -600,6 +625,18 @@ public: signal.sl = 0; } + // + // Notify Signal Event Handler(s) ... + NotifyOnSignalEventHandlers(signal); + + // + // Check Signal Execution Enabled or not ... + result = !mIgnoreSignalExecution; + if (!result) + { + return result; + } + // ENUM_X_SIGNAL_EXECUTION_RESULT state; result = trader.ExecuteSignal( @@ -652,12 +689,13 @@ protected: int mMaxAllowedShorts; // Max Allowed Shorts // - bool mUseTPSLAsPoint; // Converts TP and SL by Provided Points - bool mForceMaxTPSLAsPoint; // Force Max TP and SL by Provided Points - double mTPPoint; // TP As Point - double mSLPoint; // SL As Point - bool mIgnoreTP; // TP set to 0 - bool mIgnoreSL; // SL set to 0 + bool mUseTPSLAsPoint; // Converts TP and SL by Provided Points + bool mForceMaxTPSLAsPoint; // Force Max TP and SL by Provided Points + double mTPPoint; // TP As Point + double mSLPoint; // SL As Point + bool mIgnoreTP; // TP set to 0 + bool mIgnoreSL; // SL set to 0 + bool mIgnoreSignalExecution; // Ignore Signal Execution // string mSymbol; // Trading Symbol ... @@ -687,11 +725,39 @@ protected: barTracker.Clean(); } + // + // Notified Signal Event Handler(s) ... + void NotifyOnSignalEventHandlers(XSignal &signal) + { + // + if (!signal.IsValid()) + { + return; + } + + // + int count = ArraySize(mOnSignalEventHandlers); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + mOnSignalEventHandlers[i](signal); + } + } + // // Private ... private: // // Props ... + + // + // On Signal Recieved Event Handler(s) ... + TOnSignal mOnSignalEventHandlers[]; }; // \ No newline at end of file diff --git a/Classes/x-saherelm.xtrade.class.mq5 b/Classes/x-saherelm.xtrade.class.mq5 index 902631a8..9f7e1d7e 100644 --- a/Classes/x-saherelm.xtrade.class.mq5 +++ b/Classes/x-saherelm.xtrade.class.mq5 @@ -22,6 +22,7 @@ // // Imports ... #include "../Classes/x-saherelm.base.class.mq5" +#include "../Classes/x-saherelm.xalert.class.mq5" #include "../Libraries/x-saherelm.xtrade.lib.mq5" #include "../Classes/x-saherelm.xaccount.class.mq5" @@ -3772,4 +3773,314 @@ private: } }; +// +// Trade Handler Class ... +class XSCXTradeHandler : public XSCBaseAlert +{ + // + // Public ... +public: + // + + // + // Constructor(s) ... + void XSCXTradeHandler(XSCTrade *trader) + { + // + mTrader = trader; + } + + // + // Deconstructor ... + void ~XSCXTradeHandler() + { + // + delete mTrader; + } + + // + // First Step of Trade Handling ... + // Since Must Call when a Signal Executed ... + void AddExecutedSignal( + XSignal &signal, // Executed Signal + double commission = 0 // Commission + ) + { + // + // Check Signal Valid ... + if (!signal.IsValid()) + { + return; + } + + // + XTradeData item; + bool isFilled = item.Fill(signal); + if (!isFilled) + { + return; + } + + // + item.commission = commission; + + // + Add(item); + } + + // + // this Method call's by a Timer, + // or in OnTick for Update Positions, + // Data ... + void UpdateData() + { + // + XPosition positions[]; + int count = mTrader.GetPositions(positions); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + XPosition iPosition = positions[i]; + + // + int idx = -1; + bool isExists = HasItem( + iPosition.ticket, + idx // + ); + if (!isExists) + { + // + // Add New Data ... + + // + // Find Executed Signal Deal History ... + XDeal deals[]; + double commission = 0; + int dealsCount = mTrader.GetDeals( + deals, + iPosition.symbol, + iPosition.provider, + iPosition.period, + NULL, + DEAL_ENTRY_IN // + ); + if (IsValidSize(dealsCount)) + { + // + for (int i = 0; i < dealsCount; i++) + { + // + XDeal iDeal = deals[i]; + if (iDeal.positionId == iPosition.ticket) + { + // + commission = iDeal.commission; + break; + } + } + } + + // + XTradeData iData; + iData.Fill( + iPosition // + ); + + // + iData.ticket = iPosition.ticket; + + // + Add(iData); + } + else + { + // + // Update Exists ... + mData[idx].Update(iPosition); + } + } + } + + // + // Protected ... +protected: + // + XSCTrade *mTrader; // Instance of Trader Class + + // + XTradeData mData[]; // Hold Trade Data + + // + // Private ... +private: + // + // Props ... + + // + // Count Data ... + int Count() + { + return ArraySize(mData); + } + + // + // Add Item ... + bool Add(XTradeData &item) + { + // + bool result = false; + + // + result = item.IsValid(); + if (!result) + { + return result; + } + + // + int index = -1; + if (item.ticket > 0) + { + // + result = !HasItem( + item.ticket, + index // + ); + } + else + { + // + result = !HasItem( + item.symbol, + item.provider, + item.period, + item.type, + index // + ); + } + if (!result || IsValidIndex(index)) + { + // + result = false; + return result; + } + + // + AddRef( + item, + mData // + ); + + // + return result; + } + + // + // Find Item Index ... + bool HasItem( + ulong ticket, + int &index // + ) + { + // + bool result = false; + + // + index = -1; + + // + int count = Count(); + result = IsValidSize(count); + if (!result) + { + return result; + } + + // + for (int i = 0; i < count; i++) + { + // + result = mData[i].IsOwn(ticket); + if (result) + { + // + index = i; + break; + } + } + + // + return result; + } + + // + // Find Item Index ... + bool HasItem( + string symbol, + string provider, + ENUM_TIMEFRAMES period, + ENUM_X_POSITION_TYPES type, + int &index // + ) + { + // + bool result = false; + + // + index = -1; + + // + // Validate ... + result = + // + IsValid(symbol) && + IsValid(period) && + IsValid(provider) && + type != X_POSITION_TYPE_ALL && + type != X_POSITION_TYPE_NONE + // + ; + if (!result) + { + return result; + } + + // + int count = Count(); + result = IsValidSize(count); + if (!result) + { + return result; + } + + // + for (int i = 0; i < count; i++) + { + // + result = mData[i].IsOwn( + symbol, + provider, + period, + type // + ); + if (result) + { + // + index = i; + break; + } + } + + // + return result; + } +}; + // \ No newline at end of file diff --git a/Experts/x-test-strategies.ea.mq5 b/Experts/x-test-strategies.ea.mq5 index 0a585b00..e526dc45 100644 --- a/Experts/x-test-strategies.ea.mq5 +++ b/Experts/x-test-strategies.ea.mq5 @@ -67,6 +67,14 @@ XSCXCCHelper *mCCHelper; // Registred Strategies ... XSCBaseStrategy *strategies[]; +// +// Trader Instance Class ... +XSCTrade *eaTrader; + +// +// Trade Handler Instance Class ... +XSCXTradeHandler *eaTradeHandler; + // // Event Handlers ... @@ -90,7 +98,7 @@ int OnInit() drawPrefix = ShortName; // - // EventSetTimer(1); + EventSetTimer(5); // EventSetMillisecondTimer(100); // @@ -120,7 +128,11 @@ void OnDeinit(const int reason) delete mCCHelper; // - // EventKillTimer(); + delete eaTrader; + delete eaTradeHandler; + + // + EventKillTimer(); // Clean(strategies); @@ -130,19 +142,14 @@ void OnDeinit(const int reason) // On Tick Handler ... void OnTick() { - // HandleStrategiesOnTick(); - - // - HandleTradeState(); } // // On Timer ... void OnTimer() { - // - // HandleStrategiesOnTick(); + HandleTradeState(); } // @@ -199,6 +206,14 @@ bool InitialEA() return result; } + // + // Create Trader Instance ... + eaTrader = new XSCTrade( + eaSlippage, + eaMagicNumber // + ); + eaTradeHandler = new XSCXTradeHandler(eaTrader); + // XSymbolParser symbolParser; @@ -241,6 +256,7 @@ bool InitialEA() eaR2R, // R2R ... eaSlippage, magicNumber, + true, // Ignore Signal Execution true, // Use TP SL Point false, // Use Max 50, // eaTPPoint, // TP @@ -254,6 +270,7 @@ bool InitialEA() ); eurusdTestStrategy.SetAlertLogAlerts(true); eurusdTestStrategy.SetAlertEnableAlerts(true); + eurusdTestStrategy.AddSignalEventHandler(OnSignalRecieved); RegisterStrategy(eurusdTestStrategy); } } @@ -307,6 +324,67 @@ void HandleTradeState() { // // Implement All Trade States here ... + eaTradeHandler.UpdateData(); +} + +// +// All Signals from Strategies Comming Here for Execution or +// Analyzing ... +void OnSignalRecieved(XSignal &signal) +{ + // + ENUM_X_SIGNAL_EXECUTION_RESULT state; + bool isExecuted = eaTrader.ExecuteSignal( + signal, + state // + ); + + // + if (isExecuted) + { + // + // Find Executed Signal Deal History ... + XDeal deals[]; + double commission = 0; + int dealsCount = eaTrader.GetDeals( + deals, + signal.symbol, + signal.provider, + signal.period, + NULL, + DEAL_ENTRY_IN // + ); + if (IsValidSize(dealsCount)) + { + // + for (int i = 0; i < dealsCount; i++) + { + // + XDeal iDeal = deals[i]; + if (iDeal.positionId == signal.positionId) + { + // + commission = iDeal.commission; + break; + } + } + } + + // + // Add Signal to Trade Handler ... + eaTradeHandler.AddExecutedSignal( + signal, + commission // + ); + + // + string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") + + " Signal Provided by: " + signal.provider + + " on: " + signal.symbol + + " in: " + ToString(signal.period) + + " Executed Successfully ..."; + Print(msg); + } } // diff --git a/Libraries/x-saherelm.xtrade.lib.mq5 b/Libraries/x-saherelm.xtrade.lib.mq5 index 6d83a0bf..4c09d2e2 100644 --- a/Libraries/x-saherelm.xtrade.lib.mq5 +++ b/Libraries/x-saherelm.xtrade.lib.mq5 @@ -3379,9 +3379,367 @@ struct XSymbolPositions // }; +// +// an Structure for Holding Positions Data ... +struct XTradeData +{ + // + datetime time; // Issue Time (Open Position) + string symbol; // Trading Symbol + double entry; // Entry Price + double volume; // Volume + string provider; // Signaller + ENUM_TIMEFRAMES period; // Trading Timeframe + ENUM_X_POSITION_TYPES type; // Position Type + + // + ulong ticket; // Position Ticket + double profit; // Profit on Close + double commission; // Commission + double swap; // Swap + string message; // Close Reason + datetime endTime; // End Time + + // + // Constructor ... + void XTradeInfo() + { + Clean(); + } + + // + // Filling Trade Handler ... + bool Fill(XSignal &_signal) + { + // + bool result = false; + + // + Clean(); + + // + // Check Signal Validation ... + result = _signal.IsValid(); + if (!result) + { + return result; + } + + // + time = _signal.time; + symbol = _signal.symbol; + entry = _signal.entry; + volume = _signal.volume; + provider = _signal.provider; + period = _signal.period; + type = ToPositionType(_signal.type); + + // + if (_signal.IsExecuted()) + { + ticket = _signal.positionId; + } + + // + return result; + } + + // + // Filling Trade Handler ... + bool Fill(XPosition &_position) + { + // + bool result = false; + + // + Clean(); + + // + // Check Signal Validation ... + result = _position.IsValid(); + if (!result) + { + return result; + } + + // + time = _position.openAt; + symbol = _position.symbol; + entry = _position.entry; + volume = _position.volume; + provider = _position.provider; + period = _position.period; + type = ToPositionType(_position.type); + + // + ticket = _position.ticket; + + // + return result; + } + + // + // Update Data ... + bool Update(XPosition &_position) { + // + bool result = false; + + // + result = _position.IsValid(); + if (!result) { + return result; + } + + // + profit = _position.profit; + swap = _position.swap; + + // + return result; + } + + // + // Tools ... + + // + // Cleanup ... + void Clean() + { + // + time = NULL; + type = NULL; + symbol = NULL; + period = NULL; + endTime = NULL; + message = NULL; + provider = NULL; + + // + swap = 0; + entry = 0; + ticket = 0; + profit = 0; + volume = 0; + commission = 0; + } + + // + // Validation ... + bool IsValid() + { + // + bool result = false; + + // + result = + // + ticket > 0 && + IsValid(time) && + IsValid(symbol) && + IsValid(period) + // + ; + + // + return result; + } + + // + // Retrieve Trade Data Age ... + int GetAge() + { + // + int result = -1; + + // + if (!IsValid(symbol) || + !IsValid(time) || + !IsValid(endTime) || + !IsValid(period)) + { + return result; + } + + // + int startIndex = iBarShift( + symbol, + period, + time, + false // + ); + + // + int endIndex = iBarShift( + symbol, + period, + endTime, + false // + ); + + // + result = MathAbs(startIndex - endIndex); + + // + return result; + } + + // + // Check Own of Trade Data ... + // based on Ticket ... + bool IsOwn( + ulong _ticket // + ) + { + // + bool result = false; + + // + result = _ticket == ticket; + + // + return result; + } + + // + // Check Own of Trade Data ... + // based on Symbol/Provider and Period ... + bool IsOwn( + string _symbol, + string _provider, + ENUM_TIMEFRAMES _period, + ENUM_X_POSITION_TYPES _type // + ) + { + // + bool result = false; + + // + result = + // + IsValid(_symbol) && + IsValid(_period) && + IsValid(_provider) && + type != X_POSITION_TYPE_ALL && + type != X_POSITION_TYPE_NONE && + // + type == _type && + period == _period && + symbol == _symbol && + provider == _provider + // + ; + + // + return result; + } + + // + // Data Collector ... + + // + // Get Data File Name ... + string GetFileName() + { + // + string result = NULL; + + // + if (!IsValid()) + { + return result; + } + + // + result = + // + symbol + "\\" + + ToString(type) + "\\" + + (profit >= 0 ? "Profit" : "Loss") + + ToString(ticket) + "_" + + ToString(period) + "_" + + ToFormatString(time) + // + ; + + // + return result; + } + + // + // Get Signal File Name ... + string GetSignalFileName() + { + // + string result = NULL; + + // + result = + // + symbol + "\\" + + ToString(type) + "\\" + + provider + "_" + + ToFormatString(time) + // + ; + + // + return result; + } + + // + // Convert(s) To String Representation(s) ... + string ToString( + bool onlySignals = false // + ) + { + // + string result = NULL; + + // + int age = GetAge(); + + // + result = + // + (onlySignals ? "" : ToString("Ticket", ticket)) + + ToString("Symbol", symbol) + + ToString("Period", period) + + ToString("Entry", entry) + + ToString("Provider", provider) + + ToString("Type", ToString(type)) + + ToString("Time", time) + + (onlySignals + ? "" + : + // + "-------------" + "\n" + + ToString("Volume", volume) + + ToString("Profit", profit) + + ToString("Commission", commission) + + ToString("Swap", swap) + + ToString("End Time", endTime) + + ToString("Age", age) + + ToString("Message", message) + // + ) + + // + "" + // + ; + + // + return result; + } + + // +}; + // // TypeDefs ... +// +typedef void (*TOnSignal)(XSignal &signal); + // typedef void (*TOnDealsChanged)(int count); diff --git a/Strategies/x-saherelm.xtest.strategy.mq5 b/Strategies/x-saherelm.xtest.strategy.mq5 index 20421a70..00e64ccc 100644 --- a/Strategies/x-saherelm.xtest.strategy.mq5 +++ b/Strategies/x-saherelm.xtest.strategy.mq5 @@ -53,18 +53,19 @@ public: void XSCXTestStrategy( // // Base ... - string _symbol, // Trading Symbol - ENUM_TIMEFRAMES _period, // Trading TimeFrame - double _volume, // Voluem - double _r2r, // Risk/Reward Ratio - int _slippage, // Trader Slippage - long _magicNumber, // Trader Magic Number - bool _useTPSLAsPoint = false, // Converts TP and SL by Provided Points - bool _forceMaxTPSLAsPoint = false, // Force Max TP and SL by Provided Points - double _tpPoint = 0, // TP As Point - double _slPoint = 0, // SL As Point - bool _ignoreTP = false, // TP set to 0 - bool _ignoreSL = false, // SL set to 0 + string _symbol, // Trading Symbol + ENUM_TIMEFRAMES _period, // Trading TimeFrame + double _volume, // Voluem + double _r2r, // Risk/Reward Ratio + int _slippage, // Trader Slippage + long _magicNumber, // Trader Magic Number + bool _ignoreSignalExecution = false, // Ignore Signal Execution + bool _useTPSLAsPoint = false, // Converts TP and SL by Provided Points + bool _forceMaxTPSLAsPoint = false, // Force Max TP and SL by Provided Points + double _tpPoint = 0, // TP As Point + double _slPoint = 0, // SL As Point + bool _ignoreTP = false, // TP set to 0 + bool _ignoreSL = false, // SL set to 0 bool _allowLong = true, bool _allowShort = true, int _maxAllowedLongs = 0, @@ -75,6 +76,7 @@ public: _r2r, _slippage, _magicNumber, + _ignoreSignalExecution, _useTPSLAsPoint, _forceMaxTPSLAsPoint, _tpPoint,