From 88ea5ec9283c6dec22ca29a3c450214bb5fdeae7 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sun, 15 Sep 2024 22:40:31 +0330 Subject: [PATCH] Extract XTrade Handler and Refactor it as X121TradeHandler for Implementing Position Protector ... --- .../x-saherelm.x121.xtrade.handler.class.mq5 | 1736 +++++++++++++++++ Experts/x-saherelm.x121.ea.mq5 | 7 +- MQL5.code-workspace | 3 +- 3 files changed, 1742 insertions(+), 4 deletions(-) create mode 100644 Classes/x-saherelm.x121.xtrade.handler.class.mq5 diff --git a/Classes/x-saherelm.x121.xtrade.handler.class.mq5 b/Classes/x-saherelm.x121.xtrade.handler.class.mq5 new file mode 100644 index 00000000..54045533 --- /dev/null +++ b/Classes/x-saherelm.x121.xtrade.handler.class.mq5 @@ -0,0 +1,1736 @@ +/////////////////////////////////////////////////////// +// +// SaherElm IT Center MQL5 Class Library +// ---------------------------------------------- +// Name: X121SCTradeHandler +// Description: provides all Trade Handling requirements ... +// +// +// Maintainer: +// ------------ +// Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) +// +////////////////////////////////////////////////////// +// +// Global Properties ... +#property library +#property copyright "Copyright 2023, SaherElm IT Center" +#property link "https://www.saherelm.ir" +#property version "1.00" +#property strict + +// +// Imports ... +#include "../Classes/x-saherelm.xtrade.class.mq5" + +// +// Definitions ... + +// +// an Structure for Holding Positions Data ... +struct X121TradeData +{ + // + 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 swap; // Swap + double profit; // Profit on Close + double commission; // Commission + double maxDrawdown; // Max Position Drawdown + string message; // Close Reason + datetime endTime; // End Time + + // + XSignal signal; // Signal Object + int pushers; // Signal Pushers + string conditions; // Signal Conditions + + // + // 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; + } + + // + signal = _signal; + time = _signal.time; + entry = _signal.entry; + symbol = _signal.symbol; + volume = _signal.volume; + period = _signal.period; + pushers = _signal.pushers; + provider = _signal.provider; + conditions = _signal.conditions; + 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; + + // + if (profit < 0 && + (maxDrawdown == 0 || + MathAbs(profit) > MathAbs(maxDrawdown))) + { + maxDrawdown = profit; + } + + // + 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; + maxDrawdown = 0; + + // + signal.Clean(); + } + + // + // 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) + + // + // Attach Trade Info ... + (onlySignals ? "" : + // + "-------------" + "\n" + + ToString("Volume", volume) + + ToString("Profit", profit) + + ToString("Commission", commission) + + ToString("Swap", swap) + + ToString("Max Drawdown", maxDrawdown) + + ToString("End Time", endTime) + + ToString("Age", age) + + ToString("Message", message) + + "" + // + ) + + // + // Attach Conditions to Signals ... + (!onlySignals ? "" : + // + "-------------" + "\n" + + ToString("Pushers", pushers) + + "Conditions:" + "\n" + + "-------------" + "\n" + + conditions + + "" + // + ) + + // + "" + // + ; + + // + return result; + } + + // +}; + +// +// Implementation ... + +// +// a Class For Read and Write Trade Info Data in Files ... +class X121TradeCollector +{ + // + // Public ... + public: + // + // Props ... + + // + // Constructor(s) ... + void X121TradeCollector( + string _path = NULL // Base Path + ) + { + // + mAccount = new XSCAccount(); + + // + if (IsValid(_path)) + { + mPath = _path; + } + else + { + mPath = "X121TradeData" + "\\" + mAccount.GetCompany(); + } + } + + // + // Deconstructor ... + void ~X121TradeCollector() + { + } + + // + bool IsExists(X121TradeData &item) + { + // + bool result = false; + + // + int mHandler = GetFileHandlerForRead(item); + result = mHandler != INVALID_HANDLE; + FileClose(mHandler); + + // + return result; + } + + // + bool Save(X121TradeData &item) + { + // + bool result = false; + + // + // Check info is Valid ... + result = item.IsValid(); + if (!result) + { + return result; + } + + // + string content = item.ToString(); + content += "\n" + "-----------" + "\n" + "Signal: " + "\n" + "-----------" + "\n" + item.ToString(true); + + // + result = IsValid(content); + if (!result) + { + return result; + } + + // + int mHandler = GetFileHandlerForWrite(item); + result = mHandler != INVALID_HANDLE; + if (!result) + { + return result; + } + + // + FileWrite(mHandler, content); + FileFlush(mHandler); + FileClose(mHandler); + + // + return result; + } + + // + bool SaveSignal(X121TradeData &item) + { + // + bool result = false; + + // + string content = item.ToString(true); + result = IsValid(content); + if (!result) + { + return result; + } + + // + int mHandler = GetSignalFileHandlerForWrite(item); + result = mHandler != INVALID_HANDLE; + if (!result) + { + return result; + } + + // + FileWrite(mHandler, content); + FileFlush(mHandler); + FileClose(mHandler); + + // + return result; + } + + // + // Conditions only save for Loss Signals ... + // this means the profit must be Lower than Zero ... + // ans also message Contains SL ... + bool SaveConditions(X121TradeData &item) + { + // + bool result = false; + + // + // Validate Item ... + result = + // + item.profit < 0 && + Contains("SL", item.message) + // + ; + if (!result) + { + return result; + } + + // + string content = item.signal.conditions; + result = IsValid(content); + if (!result) + { + return result; + } + + // + int mHandler = GetConditionsFileHandlerForWrite(item); + result = mHandler != INVALID_HANDLE; + if (!result) + { + return result; + } + + // + FileSeek(mHandler, 0, SEEK_END); + FileWrite(mHandler, content); + FileFlush(mHandler); + FileClose(mHandler); + + // + return result; + } + + // + // Protected ... + protected: + // + // Private ... + private: + // + // Props ... + + // + string mPath; // Base Path ... + + // + XSCAccount *mAccount; + + // + string GetFilePath(X121TradeData &item) + { + // + string fileName = item.GetFileName(); + + // + return GetFilePath(fileName); + } + string GetFilePath(string fileName) + { + // + string result = ""; + + // + result = + // + mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + string GetSignalFilePath(X121TradeData &item) + { + // + string fileName = item.GetSignalFileName(); + + // + return GetSignalFilePath(fileName); + } + string GetSignalFilePath(string fileName) + { + // + string result = ""; + + // + result = + // + mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + string GetConditionsFilePath(X121TradeData &item) + { + // + bool isLong = IsLong(item.type); + + // + string fileName = + item.symbol + "\\" + + (isLong ? "Longs" : "Shorts"); + + // + return GetConditionsFilePath(fileName); + } + string GetConditionsFilePath(string fileName) + { + // + string result = ""; + + // + result = + // + mPath + "\\" + "Conditions" + "\\" + fileName + ".x121.log" + // + ; + + // + return result; + } + + // + int GetFileHandlerForRead(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_TXT // + ); + + // + return result; + } + int GetFileHandlerForWrite(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_WRITE | FILE_TXT // + ); + + // + return result; + } + + // + int GetSignalFileHandlerForRead(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetSignalFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_TXT // + ); + + // + return result; + } + int GetSignalFileHandlerForWrite(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetSignalFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_WRITE | FILE_TXT // + ); + + // + return result; + } + + // + int GetConditionsFileHandlerForRead(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetConditionsFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_TXT // + ); + + // + return result; + } + int GetConditionsFileHandlerForWrite(X121TradeData &item) + { + // + int result = INVALID_HANDLE; + + // + string filePath = GetConditionsFilePath(item); + if (!IsValid(filePath)) + { + return result; + } + + // + result = FileOpen( + filePath, + FILE_READ | FILE_WRITE | FILE_TXT // + ); + + // + return result; + } +}; + +// +// Trade Handler Class ... +class X121SCTradeHandler : public XSCBaseAlert +{ + // + // Public ... + public: + // + + // + // Constructor(s) ... + void X121SCTradeHandler(XSCTrade *trader) + { + // + mTrader = trader; + mCollector = new X121TradeCollector(); + } + + // + // Deconstructor ... + void ~X121SCTradeHandler() + { + // + delete mTrader; + delete mCollector; + } + + // + // Getter(s) / Setter(s) ... + + // + bool SaveSignals() + { + return mSaveSignals; + } + + // + void SaveSignals(bool value) + { + mSaveSignals = value; + } + + // + bool SaveTrades() + { + return mSaveTrades; + } + + // + void SaveTrades(bool value) + { + mSaveTrades = value; + } + + // + bool SaveConditions() + { + return mSaveConditions; + } + + // + void SaveConditions(bool value) + { + mSaveConditions = value; + } + + // + bool AllowLong() + { + return mAllowLong; + } + + // + void AllowLong(bool value) + { + mAllowLong = value; + } + + // + bool AllowShort() + { + return mAllowShort; + } + + // + void AllowShort(bool value) + { + mAllowShort = value; + } + + // + int MaxAllowedLongs() + { + return mMaxAllowedLongs; + } + + // + void MaxAllowedLongs(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mMaxAllowedLongs = value; + } + + // + int MaxAllowedShorts() + { + return mMaxAllowedShorts; + } + + // + void MaxAllowedShorts(int value) + { + // + if (value < 0) + { + value = 0; + } + + // + mMaxAllowedShorts = value; + } + + // + bool UseMaxAllowedTradesPerSymbol() + { + return mUseMaxAllowedTradesPerSymbol; + } + + // + void UseMaxAllowedTradesPerSymbol(bool value) + { + mUseMaxAllowedTradesPerSymbol = value; + } + + // + double MaxDrawdownPercentForOpenTrades() + { + return mMaxDrawdownPercentForOpenTrades; + } + + // + void MaxDrawdownPercentForOpenTrades(double value) + { + // + if (value < 0) + { + value = 0; + } + + // + mMaxDrawdownPercentForOpenTrades = value; + } + + // + // Read Only Props ... + + // + int MaxSameTimeTrades() + { + return maxSameTimeTrades; + } + + // + // Calculate Max Drawdown and it's Percent ... + double GetMaxDrawdown() + { + // + XPosition positions[]; + int positionsCount = mTrader.GetPositions(positions); + if (IsValidSize(positionsCount)) + { + double mEquity = mTrader.mAccount.GetEquity(); + if (mEquity > maxDrawdown) + { + maxDrawdown = mEquity; + } + + // + drawdownPercent = (maxDrawdown - mEquity) / (staticBalanceForCalculateDrawdown > 0 ? staticBalanceForCalculateDrawdown : maxDrawdown) * 100; + drawdownPercent = NormalizeDouble(drawdownPercent, 3); + } + else + { + // + maxDrawdown = 0.0; + drawdownPercent = 0.0; + } + + // + return drawdownPercent; + } + + // + // Trade Handling Functions ... + + // + // First Step of Trade Handling ... + // Since Must Call when a Signal Executed ... + void AddData( + XSignal &signal, // Executed Signal + double commission = 0 // Commission + ) + { + // + // Check Signal Valid ... + if (!signal.IsValid()) + { + return; + } + + // + // Check Signal Support or Recovery ... + bool isSupport = IsSupport(signal.comment); + bool isRecovery = ExtractRecoveredTicket(signal.comment) > 0; + + // + X121TradeData item; + bool isFilled = item.Fill(signal); + if (!isFilled) + { + return; + } + + // + item.commission = commission; + + // + Add(item); + + // + SaveSignal(item); + } + + // + void Finish(const XDeal &deal) + { + // + int idx = -1; + bool hasItem = HasItem( + deal.positionId, + idx // + ); + if (!hasItem) + { + return; + } + + // + // Update Item Info ... + mData[idx].swap = deal.swap; + mData[idx].endTime = deal.time; + mData[idx].profit = deal.profit; + + // + mData[idx].message = + deal.reason == DEAL_REASON_TP ? "TP" : "SL"; + + // + Save(idx); + } + + // + void Finish( + const ulong ticket, + const XPosition &position, + const string comment // + ) + { + // + int idx = -1; + bool hasItem = HasItem( + ticket, + idx // + ); + if (!hasItem) + { + return; + } + + // + // Update Item Info ... + mData[idx].swap = position.swap; + mData[idx].endTime = TimeCurrent(); + mData[idx].profit = position.profit; + + // + mData[idx].message = comment; + + // + Save(idx); + } + + // + // 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; + } + } + } + + // + X121TradeData iData; + iData.Fill( + iPosition // + ); + + // + iData.ticket = iPosition.ticket; + + // + Add(iData); + } + else + { + // + // Update Exists ... + mData[idx].Update(iPosition); + } + } + + // + count = Count(); + if (count > maxSameTimeTrades) + { + maxSameTimeTrades = count; + } + + // + GetMaxDrawdown(); + } + + // + void UpdateSignal(XSignal &signal) + { + // + if (!signal.IsValid()) + { + return; + } + + // + int count = Count(); + if (!IsValidSize(count)) + { + return; + } + + // + for (int i = 0; i < count; i++) + { + // + bool isOwn = mData[i].IsOwn( + signal.symbol, + signal.provider, + signal.period, + ToPositionType(signal.type) // + ); + if (isOwn) + { + mData[i].Fill(signal); + } + } + } + + // + void RemoveSignal(XSignal &signal) + { + // + if (!signal.IsValid()) + { + return; + } + + // + int signalIDX = -1; + ENUM_X_POSITION_TYPES xType = ToPositionType(signal.type); + bool hasSignal = HasItem( + signal.symbol, + signal.provider, + signal.period, + xType, + signalIDX // + ); + if (!hasSignal || !IsValidIndex(signalIDX)) + { + return; + } + + // + // Remove Item From List ... + ArrayRemove( + mData, + signalIDX, + 1 // + ); + } + + /** + * this Method Synchronize + * all Exists Positions and parse them + * and add them into mData Collection if + * their not Exists ... + */ + void Sync() + { + // + // TODO: Implement here ... + } + + /** + * Validate Signal For Execution ... + */ + bool CanExecute(XSignal &signal) + { + // + bool result = false; + + // + // Validate Signal ... + result = signal.IsValid(); + if (!result) + { + return result; + } + + // + // TODO: + // Here we can apply Same type or Opposit Type + // Signals behaviour ... + // also check For Market Open ... + + // + // Check Market ... + // TODO: Fix this ... + // result = mSymbolSession.CanTrade(signal.symbol); + // if (!result) { + // return result; + // } + + // + return result; + } + + /** + * Execute Specific Signal using Trade Handler ... + */ + bool ExecuteSignal( + XSignal &signal, // Signal for Execution + ENUM_X_SIGNAL_EXECUTION_RESULT &state, // Execution State + bool ignorePolicies = false // Ignore Execution Policies + ) + { + // + bool result = false; + + // + // Check Signal Validation ... + result = signal.IsValid(); + if (!result) + { + return result; + } + + // + // Filter Signals if Necessary ... + result = CanExecute(signal); + if (!result) + { + // + state = X_SIGNAL_EXECUTION_FAILED_INVALID_PARAMS; + return result; + } + + // + // Check Policies ... + if (!ignorePolicies) + { + // + bool isLong = IsLong(signal.type); + + // + // Check Allow Trade Type ... + result = + isLong + ? mAllowLong + : mAllowShort; + if (!result) + { + // + state = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED; + return result; + } + + // + // Check Trades Count ... + if (mMaxAllowedLongs > 0 || mMaxAllowedShorts > 0) + { + // + XPosition longs[]; + XPosition shorts[]; + + // + // Counting Positions ... + if (mUseMaxAllowedTradesPerSymbol) + { + // + mTrader.GetPositions( + longs, + shorts, + signal.symbol // + ); + } + else + { + // + mTrader.GetPositions( + longs, + shorts, + NULL // All Symbols ... + ); + } + + // + int longsCount = ArraySize(longs); + int shortsCount = ArraySize(shorts); + + // + result = + // + // Long ... + isLong + ? mMaxAllowedLongs <= 0 + ? true + : longsCount < mMaxAllowedLongs + // + // Short ... + : mMaxAllowedShorts <= 0 + ? true + : shortsCount < mMaxAllowedShorts + // + ; + if (!result) + { + // + state = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED; + return result; + } + } + + // + // Check Drawdown ... + if (mMaxDrawdownPercentForOpenTrades > 0) + { + // + drawdownPercent = GetMaxDrawdown(); + + // + result = + drawdownPercent <= 0 + ? true + : drawdownPercent < mMaxDrawdownPercentForOpenTrades; + if (!result) + { + // + state = X_SIGNAL_EXECUTION_FAILED_NO_EQUITY; + return result; + } + } + } + + // + // Execute Signal ... + result = mTrader.ExecuteSignal( + signal, + state // + ); + if (result) + { + // + double commission = mTrader.GetPositionCommission(signal.positionId); + + // + // Add Signal to Trade Handler ... + AddData( + signal, + commission // + ); + + // + string msg = (signal.type == POSITION_TYPE_BUY ? "Long" : "Short") + + " Signal Provided by: " + signal.provider + + (signal.pushers > 1 ? " Pushers: " + ToString(signal.pushers) : "") + + " on: " + signal.symbol + + " in: " + ToString(signal.period) + + " Executed Successfully ..."; + Alert(msg); + } + + // + return result; + } + + /** + * Handle Positiona Protections + */ + void HandleProtection() + { + // + // TODO: Implement this ... + } + + // + // Protected ... + protected: + // + XSCTrade *mTrader; // Instance of Trader Class + X121TradeCollector *mCollector; // Instance of Trade Collector Class + + // + X121TradeData mData[]; // Hold Trade Data + + // + // Private ... + private: + // + // Props ... + + // + // Read Only ... + int maxSameTimeTrades; // Max Same Time Trades + double maxDrawdown; // Max Drawdown + double drawdownPercent; // Drawdown Percent + double staticBalanceForCalculateDrawdown; // Static Balance for Max Drawdown Calculation + + // + bool mSaveSignals; // Save Signals + bool mSaveTrades; // Save Trades + bool mSaveConditions; // Save SL Conditions + + // + bool mAllowLong; // Allow Long/Buy Trade Type + bool mAllowShort; // Allow Short/Sell Trade Type + int mMaxAllowedLongs; // Max Allowe Long/Buy Trades + int mMaxAllowedShorts; // Max Allowe Short/Sell Trades + bool mUseMaxAllowedTradesPerSymbol; // Use Max Allowed Trades Per Symbol + double mMaxDrawdownPercentForOpenTrades; // Max Drawdown Percent for Open Trades + + // + // Count Data ... + int Count() + { + return ArraySize(mData); + } + + // + // Add Item ... + bool Add(X121TradeData &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; + } + + // + void Save(int index) + { + // + int count = Count(); + if (!IsValidIndex(index) || index > count - 1) + { + return; + } + + // + // Save Trade ... + if (mSaveTrades) + { + mCollector.Save(mData[index]); + } + + // + if (mSaveConditions) + { + mCollector.SaveConditions(mData[index]); + } + + // + // Remove Item From List ... + ArrayRemove( + mData, + index, + 1 // + ); + } + + // + void SaveSignal(X121TradeData &item) + { + // + if (!mSaveSignals) + { + return; + } + + // + // Save Signal ... + mCollector.SaveSignal(item); + } + + // +}; + +// \ No newline at end of file diff --git a/Experts/x-saherelm.x121.ea.mq5 b/Experts/x-saherelm.x121.ea.mq5 index c85571e9..95b17160 100644 --- a/Experts/x-saherelm.x121.ea.mq5 +++ b/Experts/x-saherelm.x121.ea.mq5 @@ -24,8 +24,9 @@ // // Imports ... -#include "../Helpers/x-saherelm.xct.helper.mq5" +#include "../Classes/x-saherelm.x121.xtrade.handler.class.mq5" #include "../Helpers/x-saherelm.xcc.helper.mq5" +#include "../Helpers/x-saherelm.xct.helper.mq5" // #include "../Parsers/x-saherelm.xch.parser.mq5" @@ -126,7 +127,7 @@ XTimeTracker eaTimeTracker; // // Trade Handler Instance Class ... -XSCXTradeHandler *eaTradeHandler; +X121SCTradeHandler *eaTradeHandler; // string mTag = ""; @@ -414,7 +415,7 @@ bool InitialEA() // // Initialize Trade Handler ... - eaTradeHandler = new XSCXTradeHandler(eaTrader); + eaTradeHandler = new X121SCTradeHandler(eaTrader); eaTradeHandler.SaveTrades(true); eaTradeHandler.SaveSignals(true); eaTradeHandler.SaveConditions(true); diff --git a/MQL5.code-workspace b/MQL5.code-workspace index 8a663c03..3282973b 100644 --- a/MQL5.code-workspace +++ b/MQL5.code-workspace @@ -8,6 +8,7 @@ "C_Cpp.default.includePath": [ "c:\\Users\\SaherElm\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\MQL5\\Include\\Include", "c:\\Users\\SaherElm\\AppData\\Roaming\\MetaQuotes\\Terminal\\D0E8209F77C8CF37AD8BF550E51FF075\\MQL5\\Include" - ] + ], + "C_Cpp.clang_format_style": "{ BasedOnStyle: Microsoft, UseTab: Never, IndentWidth: 4, TabWidth: 4, ColumnLimit: 0}" } }