/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // -------------------------------------- // Name: XCXCAEATradeManager // Description: provides all Requirements for Managing Trades ... // // // 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.x-alert.class.mq5" #include "../../Classes/x-saherelm.x-poi.drawer.class.mq5" #include "../../Classes/x-saherelm.x-trade.class.mq5" #include "../Classes/xcaea.x-data.collector.class.mq5" #include "../Libraries/xcaea.signaller.lib.mq5" // // Definitions ... // enum ENUM_XCAEA_TRADE_FINALIZATION { XCAEA_TRADE_FINAL_NONE, // None XCAEA_TRADE_FINAL_TP, // TP XCAEA_TRADE_FINAL_SL, // SL XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT, // Profit XCAEA_TRADE_FINAL_CLOSE_IN_LOSE, // Lost }; string ToString(ENUM_XCAEA_TRADE_FINALIZATION value) { // string result = NULL; // result = EnumToString(value); // return result; } // struct XCAEATrade { // // Props ... double swap; double profit; XSignal signal; ulong positionID; double commission; XCAEAStrategyConditions conditions; ENUM_XCAEA_TRADE_FINALIZATION finalize; // datetime signaledAt; datetime executedAt; datetime finalizedAt; // // Constructor ... XCAEATrade() { Clean(); } // // Tools ... /** * Cleanup Model ... */ void Clean() { // swap = 0; profit = 0; signal.Clean(); commission = 0; positionID = 0; conditions.Clean(); finalize = XCAEA_TRADE_FINAL_NONE; // signaledAt = NULL; executedAt = NULL; finalizedAt = NULL; // ZeroMemory(this); } /** * Check Has Signal ... * * @return ( bool ) */ bool HasSignal() { // bool result = false; // result = signal.IsValid(); // return result; } /** * Check Signal Executed or Not ... * * @return ( bool ) */ bool IsExecuted() { // bool result = false; // result = positionID > 0; // return result; } /** * Check a Trade is Finalized or not ... * * @return ( bool ) */ bool IsFinalized() { // bool result = false; // result = HasSignal() && IsExecuted() && finalize != XCAEA_TRADE_FINAL_NONE; // return result; } /** * Generate Summary Info ... * * @return ( string ) */ string Summary() { // string result = NULL; // result = // ToString("Swap: ", swap) + ToString("Profit: ", profit) + ToString("Commission: ", commission) + ToString("Finalize: ", ToString(finalize)) + ToString("PositionID: ", positionID) + ToString("----------------") + "\n" + ToString("Signaled At: ", ToFormatString(signaledAt)) + ToString("Executed At: ", ToFormatString(executedAt)) + ToString("Finalized At: ", ToFormatString(finalizedAt)) // ; // return result; } /** * Get Data Collection File Name ... * * @return ( string ) */ string GetFileName() { // string result = NULL; // bool hasSignal = HasSignal(); bool isExecuted = IsExecuted(); bool isFinalized = IsFinalized(); if (!hasSignal && !isExecuted && !isFinalized) { return result; } // result = // signal.symbol + "\\" + ToString(signal.type) + "\\" + (profit >= 0 ? "Profit" : "Loss") + "\\" + ToString(positionID) + "_" + ToString(signal.period) + "_" + ToFormatString(signaledAt) // ; // return result; } /** * Converts Model to String Representation ... * * @param onlySignals: Boolean, Specified Represent Only Signal or not ... * * @return ( string ) */ string ToString( bool includeSummary = true, bool includeSignal = true, bool includeConditions = true // ) { // string result = NULL; // // Generating Model Summary ... string summary = Summary(); // // Generating Signal Summary ... string signalSummary = signal.ToString(); // // Generating Conditions Summary ... string conditionsSummary = conditions .conditions .GenerateSummary( true, true, true, false // Ignore False Conditions ... ); // if (includeSummary) { // result += ToString("----------------") + "\n" + ToString("Summary: ") + "\n" + ToString("----------------") + "\n" + summary + "\n"; } // if (includeSignal) { // result += ToString("----------------") + "\n" + ToString("Signal: ") + "\n" + ToString("----------------") + "\n" + signalSummary + "\n"; } // if (includeConditions) { // result += ToString("----------------") + "\n" + ToString("Conditions: ") + "\n" + ToString("----------------") + "\n" + conditionsSummary + "\n"; } // return result; } }; // // Implementations ... class XCXCAEATradeManager : public XCBaseAlert { // // Public ... public: // // Props ... XCTrade *trader; bool drawSignal; XCPOIDrawer *drawer; XCXCAEADataCollector *collector; // // Constructors ... XCXCAEATradeManager( XCTrade *&_trader, string _path = NULL // Base Path ) { // trader = _trader; drawSignal = true; drawer = new XCPOIDrawer(); // allowTrade = true; allowLongs = true; allowShorts = true; // reportTrades = true; reportSignals = true; reportProtector = true; reportAfterTradesBalance = true; // SaveTrades(false); // // Normalizing Collector Path ... if (!IsValid(_path)) { _path = "X121XCAEATradeManager" + "\\" + trader.mAccount.GetCompany(); } // collector = new XCXCAEADataCollector(); collector.Path(_path); } // // De Constructors ... ~XCXCAEATradeManager() { // Clean(trades); // ZeroMemory(trader); ZeroMemory(drawer); ZeroMemory(collector); } // // Properties ... // bool SaveTrades() { return mSaveTrades; } // void SaveTrades(bool value) { mSaveTrades = value; } // // TODO: Implement this ... void SetTradeSession( string start, string end // ) { // bool has = IsValid(start) || IsValid(end); if (!has) { return; } // } // void SetTradeReports( bool _reportTrades, bool _reportSignals, bool _reportProtector, bool _reportAfterTradesBalance // ) { // reportTrades = _reportTrades; reportSignals = _reportSignals; reportProtector = _reportProtector; reportAfterTradesBalance = _reportAfterTradesBalance; } // void SetTradePermissions( bool _allowTrade, bool _allowLongs, bool _allowShorts // ) { // allowTrade = _allowTrade; allowLongs = _allowLongs; allowShorts = _allowShorts; } // // Tools ... /** * Do All Position Managing Senaros ... */ void Manage() { // bool has = HasChild(trades); if (!has) { return; } // int count = ArraySize(trades); for (int i = 0; i < count; i++) { // has = trades[i].IsExecuted() && !trades[i].IsFinalized(); if (!has) { continue; } // XPosition iPosition; has = trader.GetPosition( trades[i].positionID, iPosition // ); if (!has) { continue; } // // Update Trade Data ... trades[i].swap = iPosition.swap; trades[i].profit = iPosition.profit; } // // After Update All Trades we can Do Protection ... Protect(); } /** * Do All Position Protection Here ... */ void Protect() { // string prefix = "Protector: "; int targetDistance = 5; // bool has = HasChild(trades); if (!has) { return; } // XPosition ownPosition[]; XPosition ownLongPosition[]; XPosition ownShortPosition[]; // int count = ArraySize(trades); for (int i = 0; i < count; i++) { // has = trades[i].IsExecuted() && !trades[i].IsFinalized(); if (!has) { continue; } // XPosition iPosition; has = trader.GetPosition( trades[i].positionID, iPosition // ); if (!has) { continue; } // AddRef( iPosition, ownPosition // ); // if (IsLong(iPosition.type)) { // AddRef( iPosition, ownLongPosition // ); } else { // AddRef( iPosition, ownShortPosition // ); } } // has = HasChild(ownPosition); if (!has) { // Clean(ownPosition); Clean(ownLongPosition); Clean(ownShortPosition); // return; } // double swapSum = 0; double profitSum = 0; double commissionSum = 0; // // Calculate Required Data On Collection ... count = ArraySize(ownPosition); for (int i = 0; i < count; i++) { // XPosition iPosition = ownPosition[i]; // swapSum += iPosition.swap; profitSum += iPosition.profit; commissionSum += iPosition.commission; } // // Do Collection Protectiong ... // TODO: Implement this ... // // Do Signle Protections ... for (int i = 0; i < count; i++) { // // Select Position ... XPosition iPosition = ownPosition[i]; int idx = -1; has = HasTrade( idx, iPosition.ticket // ); if (!has) { continue; } // bool isLong = IsLong(iPosition.type); double points = GetPoints(iPosition.symbol); // // Handle RF On Targets ... if (iPosition.profit > 0) { // // Handle Risk Free On Targets ... double targets[]; Copy( trades[idx].signal.targets, targets // ); int targetsCount = ArraySize(targets); has = IsValidSize(targetsCount); if (has) { // for (int j = 0; j < targetsCount; j++) { // double jTarget = targets[j]; // int appliedTargetIDX = -1; // // Check Target is Applied Before or Not ... has = !trades[idx].signal.isTargetApplied ? true : trades[idx].signal.appliedTargetIDX < j; if (!has) { continue; } // // Check Traget Validation ... has = iPosition.sl == 0 ? true : isLong ? iPosition.sl < jTarget : iPosition.sl > jTarget; if (!has) { continue; } // double targetDelta = isLong ? jTarget + (targetDistance * points) : jTarget - (targetDistance * points); // // Can Risk Free ... bool canRF = jTarget > 0 && (isLong ? jTarget > iPosition.entry : jTarget < iPosition.entry) && (isLong ? iPosition.price > targetDelta : iPosition.price < targetDelta); if (canRF) { // double sl = jTarget; double tp = iPosition.tp; string comment = "RF On Target ..."; bool isModified = trader.Modify( iPosition.ticket, sl, tp, comment // ); if (isModified) { // trades[idx].signal.appliedTargetIDX = j; trades[idx].signal.isTargetApplied = true; // string message = prefix + ToString(iPosition.type) + " Position: " + ToString(iPosition.ticket) + " RF On Traget: " + ToString(jTarget) + " Successfully ..."; HandleReportProtector(message); } } } } // // Handle Partial Close on Specified Targets ... has = !trades[idx].signal.isPartiallyClosed && trades[idx].signal.partialCloseOnTPLevel > 0 && trades[idx].signal.partialCloseMultiplier > 0; if (has) { // // Check Reward Touched ... double iCurrReward = iPosition.CalculateTouchedReward(); has = iCurrReward >= trades[idx].signal.partialCloseOnTPLevel; if (has) { // // Check Volume Multiplier ... double volumeMultiplier = trades[idx].signal.partialCloseMultiplier; has = volumeMultiplier > 0 && volumeMultiplier <= 1; if (has) { // // Do Partial Closing ... double volume = iPosition.volume * volumeMultiplier; // // Normalizing Volume ... volume = NormalizeVolume( volume, iPosition.symbol // ); // string comment = "PC On Target ..."; has = trader.ClosePartial( iPosition.ticket, volume, comment // ); if (has) { // // Update Model ... trades[idx].signal.isPartiallyClosed = true; // string message = prefix + ToString(iPosition.type) + " Position: " + ToString(iPosition.ticket) + " PC (" + ToString(volume) + ") On Traget: " + ToString(iCurrReward) + " Successfully ..."; HandleReportProtector(message); } } } } } } } /** * Check Signal Execution Conditions ... * * @param signal: XSignal instance reference ... * @param executionResult: ENUM_X_SIGNAL_EXECUTION_RESULT member reference ... * * @return ( bool ) */ bool CheckSignal( XSignal &signal, ENUM_X_SIGNAL_EXECUTION_RESULT &executionResult // ) { // bool result = false; // // Cleanup ... executionResult = X_SIGNAL_EXECUTION_UNKNOWN; // // Validate Signal ... result = signal.IsValid(); if (!result) { return result; } // // Reporting Signal ... if (reportSignals) { // string message = signal.GetMessage("Provided"); Alert(message); } // // Check Trade Permission ... if (!allowTrade) { // result = false; executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED; // return result; } // bool isLong = IsLong(signal.type); bool isShort = IsShort(signal.type); // bool isLongAndAllowed = isLong && allowLongs; // bool isShortAndAllowed = isShort && allowShorts; // result = isLongAndAllowed || isShortAndAllowed; if (!result) { // executionResult = X_SIGNAL_EXECUTION_FAILED_NOT_ALLOWED; // return result; } // // TODO: Complete this ... // Implementing Trade Session Validating ... // // TODO: Complete this ... // Implementing Trade Permissions Based on SL(s) Count ... // return result; } /** * Execute Specified Signal ... * * @param signal: XSignal instance reference ... * @param conditions: XCAEAStrategyConditions instance reference ... * * @return ( bool ) */ bool Execute( XSignal &signal, XCAEAStrategyConditions &conditions // ) { // bool result = false; // result = signal.IsValid(); if (!result) { return result; } // int idx = -1; bool isExists = HasTrade( idx, signal // ); result = !isExists; if (!result) { return result; } // ENUM_X_SIGNAL_EXECUTION_RESULT executionResult; // // Checking Signal For Execution ... result = CheckSignal( signal, executionResult // ); if (result) { // result = trader.ExecuteSignal( signal, executionResult // ); } // // Alert Signal Execution ... if (reportTrades) { // string executionMessage = ToString(executionResult); // string message = NULL; if (result) { message = signal.GetMessage("Executed"); } else { message = signal.GetMessage("Failed due (" + executionMessage + ")"); } // Alert(message); } // if (result) { // if (drawSignal) { // XCSignalObject *iObj; isExists = drawer.DrawSignal( signal, iObj // ); // if (isExists) { // mObjects.Add(iObj); ZeroMemory(iObj); } } // XCAEATrade iTrade; // ulong positionID = trader.GetLastOpenPositionTicket(); double commission = trader.GetPositionCommission(positionID); // iTrade.signal = signal; iTrade.conditions = conditions; iTrade.positionID = positionID; iTrade.commission = commission; iTrade.signaledAt = signal.time; iTrade.executedAt = TimeCurrent(); // AddRef( iTrade, trades // ); } // return result; } /** * Handle Position Take Profit ... * * @param deal: XDeal instance reference ... */ void HandleTP(const XDeal &deal) { // // Check Ticket Exists or not ... int idx = -1; bool isExists = HasTrade( idx, deal.positionId // ); if (!isExists) { return; } // // Apply Required Data ... trades[idx].swap = deal.swap; trades[idx].profit = deal.profit; trades[idx].finalizedAt = TimeCurrent(); trades[idx].finalize = XCAEA_TRADE_FINAL_TP; // tpCount++; managedTPCount++; // // Save Trade ... Save(trades[idx]); // // Handle Balance Reporting ... string prefix = "TP: " + ToString(trades[idx].profit); HandleReportBalance(prefix); // RemoveTrade(idx); } /** * Handle Position Stop Loss ... * * @param deal: XDeal instance reference ... */ void HandleSL(const XDeal &deal) { // // Check Ticket Exists or not ... int idx = -1; bool isExists = HasTrade( idx, deal.positionId // ); if (!isExists) { return; } // // Apply Required Data ... trades[idx].swap = deal.swap; trades[idx].profit = deal.profit; trades[idx].finalizedAt = TimeCurrent(); trades[idx].finalize = XCAEA_TRADE_FINAL_SL; // slCount++; managedSLCount++; // // Save Trade ... Save(trades[idx]); // // Handle Balance Reporting ... string prefix = "SL: " + ToString(trades[idx].profit); HandleReportBalance(prefix); // RemoveTrade(idx); } /** * Handle Position Force Close ... * * @param deal: XDeal instance reference ... */ void HandleForceClose(const XPosition &position) { // // Check Ticket Exists or not ... int idx = -1; bool isExists = HasTrade( idx, position.ticket // ); if (!isExists) { return; } // bool isInProfit = position.profit > 0; // // Apply Required Data ... trades[idx].swap = position.swap; trades[idx].profit = position.profit; trades[idx].finalizedAt = TimeCurrent(); trades[idx].commission = position.commission; trades[idx].finalize = isInProfit ? XCAEA_TRADE_FINAL_CLOSE_IN_PROFIT : XCAEA_TRADE_FINAL_CLOSE_IN_LOSE; // if (isInProfit) { // tpCount++; managedTPCount++; } else { // slCount++; managedSLCount++; } // // Save Trade ... Save(trades[idx]); // // Handle Balance Reporting ... string prefix = "Force Close " + (isInProfit ? "Profit" : "Loss") + ": " + ToString(trades[idx].profit); HandleReportBalance(prefix); // RemoveTrade(idx); } // // TODO: Complete this ... void HandleGuard() { } /** * Handle New Day ... */ void HandleNewDay() { // // Clean Managed SL(s) and TP(s) and Positions ... } /** * Find Executed Trade by Providing Position Ticket ... * * @param index: int reference, holding founded item index ... * @param ticket: ulong, Specified Position Ticket ... * * @return ( bool ) */ bool HasTrade( int &index, ulong ticket // ) { // bool result = false; // index = -1; // result = ticket > 0 && HasChild(trades); if (!result) { return result; } // int count = ArraySize(trades); for (int i = 0; i < count; i++) { // XCAEATrade iTrade = trades[i]; // result = iTrade.HasSignal() && iTrade.IsExecuted() && iTrade.positionID == ticket; if (result) { // index = i; // iTrade.Clean(); break; } // iTrade.Clean(); } // result = IsValidIndex(index); // return result; } /** * Find Trade Item By Signal ... * * @param index: int reference, holding founded item index ... * * @return ( bool ) */ bool HasTrade( int &index, XSignal &signal // ) { // bool result = false; // index = -1; // result = signal.IsValid() && HasChild(trades); if (!result) { return result; } // int count = ArraySize(trades); for (int i = 0; i < count; i++) { // XCAEATrade iTrade = trades[i]; // result = iTrade.HasSignal() && iTrade.signal.IsSameAs(signal); if (result) { // index = i; // iTrade.Clean(); break; } // iTrade.Clean(); } // result = IsValidIndex(index); // return result; } /** * Remove a Trade From List ... * * @param index: int ... */ void RemoveTrade(int index) { // bool has = HasChild(trades) && IsValidIndex(index) && index < ArraySize(trades); if (!has) { return; } // ArrayRemove( trades, index, 1 // ); } // // Protected ... protected: // // Tools ... // string GetFilePath(XCAEATrade &trade) { // string fileName = trade.GetFileName(); // return GetFilePath(fileName); } // string GetFilePath(string fileName) { // string result = NULL; // string mPath = collector.Path(); // result = // mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log" // ; // return result; } // void HandleReportBalance(string prefix = NULL) { // if (!reportAfterTradesBalance) { return; } // double balance = trader.mAccount.GetBalance(); string currency = trader.mAccount.GetCurrency(); // string message = (IsValid(prefix) ? prefix + "\n" : "") + "Account Balance: " + ToString(balance) + currency; Alert(message); } // void HandleReportProtector(string message = NULL) { // if (!reportProtector) { return; } // Alert(message); } // bool Save( XCAEATrade &trade, bool includeSummary = true, bool includeSignal = true, bool includeConditions = true // ) { // bool result = false; // if (!mSaveTrades) { return result; } // string filePath = GetFilePath(trade); result = IsValid(filePath); if (!result) { return result; } // string content = trade.ToString( includeSummary, includeSignal, includeConditions // ); // result = collector.Save( filePath, content // ); // return result; } // // Private ... private: // // Props ... // bool allowTrade; bool allowLongs; bool allowShorts; // bool mSaveTrades; // int slCount; int tpCount; // int managedSLCount; int managedTPCount; // bool reportTrades; bool reportSignals; bool reportProtector; bool reportAfterTradesBalance; // CArrayObj mObjects; // XCAEATrade trades[]; }; //