/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // -------------------------------------- // Name: XCXCATBEATradeManager // 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 "../../Libraries/x-saherelm.x-poi.lib.mq5" #include "../Classes/xcatbea.x-data.collector.class.mq5" #include "../Libraries/xcatbea.lib.mq5" #include "../Libraries/xcatbea.signaller.lib.mq5" // // Definitions ... // enum ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD { XCATBEA_TRADE_RESTRICATION_NONE, // None XCATBEA_TRADE_RESTRICATION_HOURLY, // Per Hour XCATBEA_TRADE_RESTRICATION_DAILY, // Per Day XCATBEA_TRADE_RESTRICATION_WEEKLY, // Per Week XCATBEA_TRADE_RESTRICATION_MONTHLY, // Per Month }; // string ToString(ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD value) { // string result = NULL; // result = EnumToString(value); // return result; } // enum ENUM_XCATBEA_TRADE_FINALIZATION { XCATBEA_TRADE_FINAL_NONE, // None XCATBEA_TRADE_FINAL_TP, // TP XCATBEA_TRADE_FINAL_SL, // SL XCATBEA_TRADE_FINAL_CLOSE_IN_PROFIT, // Profit XCATBEA_TRADE_FINAL_CLOSE_IN_LOSE, // Lost }; // string ToString(ENUM_XCATBEA_TRADE_FINALIZATION value) { // string result = NULL; // result = EnumToString(value); // return result; } // struct XCATBEASymbolInfo { // // Properties ... // string symbol; // Trading Symbol // int longs; // All Long Trades int shorts; // All Short Trades // int trades; // All Trades // int losses; // All Loosing Trades int profits; // All Winning Trades // datetime managedStart; // Managed Start Time // int managedSLs; // Managed SL(s) int managedTPs; // Managed TP(s) int managedTrades; // Managed Trades // double managedBalance; // Managed Start Balance double managedProfits; // Managed Profit(s) // // Constructor ... XCATBEASymbolInfo() { Clean(); } // // Tools ... // // Cleanup ... void Clean() { // symbol = NULL; // longs = 0; shorts = 0; // trades = 0; // losses = 0; profits = 0; // managedStart = NULL; // managedSLs = 0; managedTPs = 0; managedTrades = 0; // managedBalance = 0; managedProfits = 0; // ZeroMemory(this); } // // Validate ... bool IsValid() { // bool result = false; // result = IsValid(symbol); // return result; } // bool IsSameAs(XCATBEASymbolInfo &item) { // bool result = false; // result = IsValid() && item.IsValid() && symbol == item.symbol; // return result; } // string GetRestrictionDescription( ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD period // ) { // string result = NULL; // if (!IsValid() && period != XCATBEA_TRADE_RESTRICATION_NONE) { return result; } // result += "-------------------" + "\n" + "Restrictions: " + "\n" + "-------------------" + "\n" + "Symbol: " + ToString(symbol) + "\n" + "Period: " + ToString(period) + "\n" + "Start: " + ToFormatString(managedStart) + "\n" + "Balance: " + ToString(managedBalance) + "\n" + "-------------------" + "\n" + "Trades: " + ToString(managedTrades) + "\n" + "SL: " + ToString(managedSLs) + "\n" + "TP: " + ToString(managedTPs) + "\n" + "Profit: " + ToString(managedProfits) + "\n" + ""; // return result; } }; // struct XCATBEATradeSession { // // Properties ... string start; // Session Start Time (hh:mm) string end; // Session End Time (hh:mm) string name; // Session Name // // Constructor ... XCATBEATradeSession() { Clean(); } // // Tools ... // // Cleanup ... void Clean() { // end = NULL; name = NULL; start = NULL; // ZeroMemory(this); } // // Validate ... bool IsValid() { // bool result = false; // result = IsValid(start) && IsValid(end); if (!result) { return result; } // return result; } // // Check Session is Active or Not ... bool IsActive(datetime time = NULL) { // bool result = false; // time = NormalizeTime(time); // result = IsValid(); if (!result) { return result; } // result = IsTimeInRange( time, start, end // ); // return result; } }; struct XCATBEASymbolSession { // // Propeties ... // string symbol; // Trading Symbol XCATBEATradeSession sessions[]; // Trading Sessions // // Constructor ... XCATBEASymbolSession() { Clean(); } // // Tools ... // // Cleanup ... void Clean() { // symbol = NULL; Clean(sessions); // ZeroMemory(this); } // // Validate ... bool IsValid() { // bool result = false; // result = IsValid(symbol); // return result; } // // Check Allow Trade in Active Sessions ... bool CanTrade(datetime time = NULL) { // bool result = false; // // Normalize Args ... time = NormalizeTime(time); // // Validating ... result = IsValid(); if (!result) { return result; } // // if there is not any defined sessions for Symbol ... // it's allow Trading in all times ... result = !HasChild(sessions); if (result) { return result; } // // Detect an Active Session ... int count = ArraySize(sessions); for (int i = 0; i < count; i++) { // result = sessions[i].IsActive(time); if (result) { break; } } // return result; } // }; // struct XCATBEATrade { // // Props ... double swap; double profit; XSignal signal; ulong positionID; double commission; XCATBEAStrategyConditions conditions; ENUM_XCATBEA_TRADE_FINALIZATION finalize; // datetime signaledAt; datetime executedAt; datetime finalizedAt; // // Constructor ... XCATBEATrade() { Clean(); } // // Tools ... /** * Cleanup Model ... */ void Clean() { // swap = 0; profit = 0; signal.Clean(); commission = 0; positionID = 0; conditions.Clean(); finalize = XCATBEA_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 != XCATBEA_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( false, 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; } string GetConditionsString() { // string result = NULL; // double bullishScore = 0; double bearishScore = 0; conditions .conditions .GenerateScore( bullishScore, bearishScore // ); // string conditionsStr = conditions .conditions .GenerateSummary( false, true, // Only Conditions ... false, false // Ignore False Conditions ... ); string condParts[]; int condPartsCount = SplitContent( condParts, conditionsStr, "\n" // ); if (IsValidSize(condPartsCount)) { // conditionsStr = NULL; while (HasChild(condParts)) { // string iStr = condParts[0]; ArrayRemove( condParts, 0, 1 // ); // bool isLast = !HasChild(condParts); // bool isCond = Contains( "is", iStr, false // ); if (isCond) { // string iParts[]; int iPartsCount = SplitContent( iParts, iStr, ":" // ); if (iPartsCount == 2) { // string iVal = iParts[1]; StringTrimLeft(iVal); StringTrimRight(iVal); bool isPassed = ToBoolean(iVal); conditionsStr += (isPassed ? "" : "!") + "conditions." + iParts[0] + (!isLast ? " &&" : "") + "\n"; } } } Clean(condParts); } else { conditionsStr = NULL; } // result = "(" + "\n" + "\\" + "\\" + " " + ToString("Type", ToString(signal.type)) + "\\" + "\\" + " " + ToString("Profit", profit) + "\\" + "\\" + " " + ToString("Finalize", ToString(finalize)) + "\\" + "\\" + " " + ToString("PositionID", positionID) + "\\" + "\\" + " " + ToString("----------------") + "\n" + "\\" + "\\" + " " + ToString("Signaled At", ToFormatString(signaledAt)) + "\\" + "\\" + " " + ToString("Executed At", ToFormatString(executedAt)) + "\\" + "\\" + " " + ToString("Finalized At", ToFormatString(finalizedAt)) + "\\" + "\\" + " " + ToString("----------------") + "\n" + "\\" + "\\" + " " + ToString("Scores:") + "\n" + "\\" + "\\" + " " + ToString("Bullish", bullishScore) + "\\" + "\\" + " " + ToString("Bearish", bearishScore) + "\\" + "\\" + " " + ToString("----------------") + "\n" + conditionsStr + (IsValid(conditionsStr) ? "" : "") + ") && "; // return result; } }; // // Implementations ... class XCXCATBEATradeManager : public XCBaseAlert { // // Public ... public: // // Props ... XCTrade *trader; bool drawSignal; XCPOIDrawer *drawer; XTimeTracker timeTracker; XCXCATBEADataCollector *collector; // // Constructors ... XCXCATBEATradeManager( XCTrade *&_trader, string _path = NULL // Base Path ) { // trader = _trader; drawSignal = true; drawer = new XCPOIDrawer(); // allowTrade = true; allowLongs = true; allowShorts = true; // forceCloseAt = NULL; isForceCloseAtTime = false; // maxAllowedTPs = 0; maxAllowedSLs = 0; maxAllowedTrades = 0; restrictionsPeriod = XCATBEA_TRADE_RESTRICATION_NONE; // reportTrades = true; reportSignals = true; reportProtector = true; reportAfterTradesBalance = true; // SaveTrades(false); // // Normalizing Collector Path ... if (!IsValid(_path)) { _path = "X121XCATBEATradeManager" + "\\" + trader.mAccount.GetCompany(); } // collector = new XCXCATBEADataCollector(); collector.Path(_path); } // // De Constructors ... ~XCXCATBEATradeManager() { // Clean(trades); // timeTracker.Clean(); // ZeroMemory(trader); ZeroMemory(drawer); ZeroMemory(collector); } // // Properties ... // bool SaveTrades() { return mSaveTrades; } // void SaveTrades(bool value) { mSaveTrades = value; } // bool SaveSignals() { return mSaveSignals; } // void SaveSignals(bool value) { mSaveSignals = value; } // bool SaveWins() { return mSaveWins; } // void SaveWins(bool value) { mSaveWins = value; } // bool SaveLosts() { return mSaveLosts; } // void SaveLosts(bool value) { mSaveLosts = value; } // bool SaveRestrictions() { return mSaveRestrictions; } // void SaveRestrictions(bool value) { mSaveRestrictions = value; } // void SetForceCloseTradesAt( string _forceCloseAt // ) { forceCloseAt = _forceCloseAt; } // void SetTradeSession( string symbol, string start, string end, string name = NULL // ) { // // Validate Args ... bool has = IsValid(end) && IsValid(start) && IsValid(symbol); if (!has) { return; } // // Create Session ... // XCATBEATradeSession session; // session.end = end; session.name = name; session.start = start; // int sessionIDX = -1; has = FindSymbolSession( sessionIDX, symbol // ); // if (!has) { // // Add New Symbol Session ... // XCATBEASymbolSession iSymbolSession; iSymbolSession.symbol = symbol; // AddTradingSession( session, iSymbolSession // ); // AddRef( iSymbolSession, sessions // ); // iSymbolSession.Clean(); } else { // // Update Trading Sessions of Exists Symbol Session ... AddTradingSession( session, sessions[sessionIDX] // ); } // // Cleanup Resources ... session.Clean(); } // void SetTradeReports( bool _reportTrades, bool _reportSignals, bool _reportProtector, bool _reportRestrictions, bool _reportAfterTradesBalance // ) { // reportTrades = _reportTrades; reportSignals = _reportSignals; reportProtector = _reportProtector; reportRestrictions = _reportRestrictions; reportAfterTradesBalance = _reportAfterTradesBalance; } // void SetTradePermissions( bool _allowTrade, bool _allowLongs, bool _allowShorts // ) { // allowTrade = _allowTrade; allowLongs = _allowLongs; allowShorts = _allowShorts; } // void SetTradeRestrictions( ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD _period, int _maxAllowedTPs, int _maxAllowedSLs, int _maxAllowedTrades // ) { // // Normalizing Args ... _maxAllowedTPs = NormalizeInt(_maxAllowedTPs, 0); _maxAllowedSLs = NormalizeInt(_maxAllowedSLs, 0); _maxAllowedTrades = NormalizeInt(_maxAllowedTrades, 0); // restrictionsPeriod = _period; maxAllowedTPs = _maxAllowedTPs; maxAllowedSLs = _maxAllowedSLs; maxAllowedTrades = _maxAllowedTrades; } // // Tools ... /** * Do All Position Managing Senaros ... */ void Manage() { // // Handle Restrictions ... HandleRestrictions(); // // Check Contains Trades for Managing ... bool has = HasChild(trades); if (!has) { return; } // // Update Required Infos of Trades ... 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; } // // Do Trade 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 ... // // Check Force Close ... if (IsValid(forceCloseAt)) { // datetime forceCloseTime = ParseDateTimeFromTimeString(forceCloseAt); bool canForceClose = IsTimeInRange( TimeCurrent(), forceCloseTime // ) && !isForceCloseAtTime; if (canForceClose) { // isForceCloseAtTime = HandleForceCloseTrades(ownPosition); // // Prevent Moving Forward ... return; } } // // 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 ... XTarget targets[]; Copy( trades[idx].signal.targets, targets // ); int targetsCount = ArraySize(targets); has = IsValidSize(targetsCount); if (has) { // for (int j = 0; j < targetsCount; j++) { // XTarget 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.target : iPosition.sl > jTarget.target; if (!has) { continue; } // double targetDelta = isLong ? jTarget.target + (targetDistance * points) : jTarget.target - (targetDistance * points); // // Can Risk Free ... bool canRF = jTarget.doRF && jTarget.target > 0 && (isLong ? jTarget.target > iPosition.entry : jTarget.target < iPosition.entry) && (isLong ? iPosition.price > targetDelta : iPosition.price < targetDelta); // // Can Partial Close ... bool canPC = jTarget.volumeMultiplier > 0 && jTarget.target > 0 && (isLong ? jTarget.target > iPosition.entry : jTarget.target < iPosition.entry) && (isLong ? iPosition.price > targetDelta : iPosition.price < targetDelta); bool canRFPC = canRF || canPC; if (canRFPC) { // // Handle Risk Free ... bool isRFDone = false; if (canRF) { // double tp = iPosition.tp; double sl = jTarget.target; string comment = "RF On Target ..."; isRFDone = trader.Modify( iPosition.ticket, sl, tp, comment // ); } // // Handle Partail Close ... bool isPCDone = false; if (canPC) { // double volume = jTarget.volumeMultiplier * iPosition.volume; volume = NormalizeVolume(volume, iPosition.symbol); string comment = "RF On Target ..."; isPCDone = trader.ClosePartial( iPosition.ticket, volume, comment // ); } // // Handle Report Action ... bool isRFPCDone = isRFDone || isPCDone; if (isRFPCDone) { // trades[idx].signal.appliedTargetIDX = j; trades[idx].signal.isTargetApplied = true; // string message = prefix + ToString(iPosition.type) + " Position: " + ToString(iPosition.ticket) + (isRFDone ? " RF" : "") + (isPCDone ? " PC" : "") + " On Traget: " + ToString(jTarget.target) + " Successfully ..."; HandleReportProtector(message); } } } } } } } /** * Check Specified Symbol is Active for * Analysing or Open Trades ... */ bool IsActiveSession(string symbol) { // bool result = false; // // Validate Args ... result = IsValid(symbol); if (!result) { return result; } // // Check Symbol Session Exists or not ... int symbolSessionIDX = -1; bool isSymbolSessionExists = FindSymbolSession( symbolSessionIDX, symbol // ); // // Chack Trade Session Validating ... if (isSymbolSessionExists) { result = sessions[symbolSessionIDX].CanTrade(); } // return result; } /** * 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"); SendAlert(message); } // SaveSignal(signal); // // 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; } // // Check Symbol Info Exists or not ... int symbolInfoIDX = -1; bool isSymbolInfoExists = FindSymbolIndex( symbolInfoIDX, signal.symbol // ); // // Check Symbol Session Exists or not ... int symbolSessionIDX = -1; bool isSymbolSessionExists = FindSymbolSession( symbolSessionIDX, signal.symbol // ); // // Chack Trade Session Validating ... if (isSymbolSessionExists) { // result = sessions[symbolSessionIDX].CanTrade(); if (!result) { // executionResult = X_SIGNAL_EXECUTION_FAILED_DELAY_NOT_PASSED; // return result; } } // // Check Trade Permissions Based on SL(s) Count ... bool hasRestrictions = restrictionsPeriod != XCATBEA_TRADE_RESTRICATION_NONE; if (hasRestrictions) { // // Only Can Check Restrictions when // Symbol Info Exists ... if (isSymbolInfoExists) { // bool isTPAllowed = maxAllowedTPs == 0 ? true : symbols[symbolInfoIDX].managedTPs < maxAllowedTPs; bool isSLAllowed = maxAllowedSLs == 0 ? true : symbols[symbolInfoIDX].managedSLs < maxAllowedSLs; bool isTradeAllowed = maxAllowedTrades == 0 ? true : symbols[symbolInfoIDX].managedTrades < maxAllowedTrades; // result = isTPAllowed && isSLAllowed && isTradeAllowed; } // // Report Error ... if (!result) { // executionResult = X_SIGNAL_EXECUTION_FAILED_MAX_REACHED; // return result; } } // return result; } /** * Execute Specified Signal ... * * @param signal: XSignal instance reference ... * @param conditions: XCATBEAStrategyConditions instance reference ... * * @return ( bool ) */ bool Execute( XSignal &signal, XCATBEAStrategyConditions &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 + ")"); } // SendAlert(message); } // if (result) { // // Draw Signal ... if (drawSignal) { // XCSignalObject *iObj; isExists = drawer.DrawSignal( signal, iObj // ); // if (isExists) { // mObjects.Add(iObj); ZeroMemory(iObj); } } // // Create Symbol Info Struct ... XCATBEASymbolInfo iSymbolInfo; // iSymbolInfo.trades = 1; iSymbolInfo.managedTrades = 1; iSymbolInfo.symbol = signal.symbol; // bool isLong = IsLong(signal.type); if (isLong) { iSymbolInfo.longs = 1; } else { iSymbolInfo.shorts = 1; } // // Fill Balance for Restrications ... bool hasRestrictions = HasRestrictions(); if (hasRestrictions) { // // Read Balance ... double balance = trader.mAccount.GetBalance(); iSymbolInfo.managedBalance = balance; iSymbolInfo.managedStart = TimeCurrent(); } // AddOrUpdateSymbolInfo(iSymbolInfo); // // Create Trade Struct ... XCATBEATrade 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; } /** * Retrieve Specified Positions ... * * @param positions: XPosition instance Reference Array, which holds Founded Positions ... * @param symbol: string, Specified Symbol Name ... * @param provider: string, Specified Provider Name ... * @param period: ENUM_TIMEFRAMES, Specified Period ... * * @return ( int ) */ int GetPositions( XPosition &positions[], string symbol, string provider, ENUM_TIMEFRAMES period // ) { // int result = 0; // Clean(positions); // bool has = IsValid(symbol) && IsValid(period) && IsValid(provider); if (!has) { return result; } // result = trader.GetPositions( positions, symbol, provider, period, NULL, // All Types ... true, // Filter By Magic ... true // Force Clean ... ); // result = ArraySize(positions); // 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 = XCATBEA_TRADE_FINAL_TP; // // Save Trade ... SaveWin(trades[idx]); SaveTrade(trades[idx]); // // Handle Balance Reporting ... string prefix = "TP: " + ToString(trades[idx].profit); HandleReportBalance(prefix); // // Handle Symbol Info ... // XCATBEASymbolInfo iSymbolInfo; // iSymbolInfo.profits = 1; iSymbolInfo.managedTPs = 1; iSymbolInfo.symbol = trades[idx].signal.symbol; iSymbolInfo.managedProfits = trades[idx].profit; // AddOrUpdateSymbolInfo(iSymbolInfo); // 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 = XCATBEA_TRADE_FINAL_SL; // // Save Trade ... SaveTrade(trades[idx]); // // Handle Balance Reporting ... string prefix = "SL: " + ToString(trades[idx].profit); HandleReportBalance(prefix); // // Handle Symbol Info ... // bool isInProfit = trades[idx].profit > 0; if (isInProfit) { SaveWin(trades[idx]); } else { SaveLost(trades[idx]); } // XCATBEASymbolInfo iSymbolInfo; // if (isInProfit) { // iSymbolInfo.profits = 1; iSymbolInfo.managedTPs = 1; } else { // iSymbolInfo.losses = 1; iSymbolInfo.managedSLs = 1; } iSymbolInfo.symbol = trades[idx].signal.symbol; iSymbolInfo.managedProfits = trades[idx].profit; // AddOrUpdateSymbolInfo(iSymbolInfo); // 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 ? XCATBEA_TRADE_FINAL_CLOSE_IN_PROFIT : XCATBEA_TRADE_FINAL_CLOSE_IN_LOSE; // // Save Trade ... SaveTrade(trades[idx]); if (isInProfit) { SaveWin(trades[idx]); } else { SaveLost(trades[idx]); } // // Handle Balance Reporting ... string prefix = "Force Close " + (isInProfit ? "Profit" : "Loss") + ": " + ToString(trades[idx].profit); HandleReportBalance(prefix); // // Handle Symbol Info ... // XCATBEASymbolInfo iSymbolInfo; // if (isInProfit) { // iSymbolInfo.profits = 1; iSymbolInfo.managedTPs = 1; } else { // iSymbolInfo.losses = 1; iSymbolInfo.managedSLs = 1; } iSymbolInfo.symbol = trades[idx].signal.symbol; iSymbolInfo.managedProfits = trades[idx].profit; // AddOrUpdateSymbolInfo(iSymbolInfo); // RemoveTrade(idx); } /** * Handle Position Partially Closed Event ... * * @param ticket: ULONG ... * @param profit: double ... * @param comment: string ... */ void HandlePartiallyClosed( const ulong ticket, const double profit, const string comment // ) { // // Handle Balance Reporting ... string prefix = "Partially Closed: " + ToString(ticket); // // Handle Balance Reporting ... HandleReportBalance(prefix); } /** * Handle Force Close Trades ... */ bool HandleForceCloseTrades( XPosition &positions[] // ) { // bool result = false; // result = HasChild(positions); if (!result) { return result; } // string prefix = "Protector: "; string comment = "Force Close ..."; // int closedsCount = trader.Close( positions, comment // ); result = IsValidSize(closedsCount); if (result) { // string message = prefix + "Force Close (" + ToString(closedsCount) + ") Trades at Specified Time ..."; HandleReportProtector(message); } // return result; } // // TODO: Complete this ... void HandleGuard() { } /** * 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++) { // XCATBEATrade 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++) { // XCATBEATrade 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 GetTradeFilePath(XCATBEATrade &trade) { // string fileName = trade.GetFileName(); // return GetTradeFilePath(fileName); } // string GetTradeFilePath(string fileName) { // string result = NULL; // string mPath = collector.Path(); // result = // mPath + "\\" + "Trades" + "\\" + fileName + ".x121.log" // ; // return result; } // string GetSignalFilePath(string fileName) { // string result = NULL; // string mPath = collector.Path(); // result = // mPath + "\\" + "Signals" + "\\" + fileName + ".x121.log" // ; // return result; } // string GetWinsFilePath(XCATBEATrade &trade) { // string result = NULL; // string mPath = collector.Path(); // result = // mPath + "\\" + "Conditions" + "\\" + trade.signal.symbol + "\\" + ToString(trade.signal.period) + "_wins.x121.log" // ; // return result; } // string GetLostsFilePath(XCATBEATrade &trade) { // string result = NULL; // string mPath = collector.Path(); // result = // mPath + "\\" + "Conditions" + "\\" + trade.signal.symbol + "\\" + ToString(trade.signal.period) + "_losts.x121.log" // ; // return result; } // string GetRestrictionsFilePath(string symbol) { // string result = NULL; // if (!IsValid(symbol)) { return result; } // string mPath = collector.Path(); // result = // mPath + "\\" + "Restrictions" + "\\" + symbol + ".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; SendAlert(message); } // void HandleReportProtector(string message = NULL) { // if (!reportProtector) { return; } // SendAlert(message); } // // Save Trades ... bool SaveTrade( XCATBEATrade &trade, bool includeSummary = true, bool includeSignal = true, bool includeConditions = true // ) { // bool result = false; // if (!mSaveTrades) { return result; } // string filePath = GetTradeFilePath(trade); result = IsValid(filePath); if (!result) { return result; } // string content = trade.ToString( includeSummary, includeSignal, includeConditions // ); // result = collector.Save( filePath, content // ); // return result; } // // Save Signals ... bool SaveSignal(XSignal &signal) { // bool result = false; // // Check Signal Save is Enabled ... if (!mSaveSignals) { return result; } // // Check Signal Validation ... if (!signal.IsValid()) { return result; } // // Retrieve Signal File Name ... string signalFileName = signal.GetFileName(); result = IsValid(signalFileName); if (!result) { return result; } // // Retriev and Validate File Name ... string filePath = GetSignalFilePath(signalFileName); result = IsValid(filePath); if (!result) { return result; } // // Generate Content ... string content = ""; content += ToString("----------------") + "\n" + ToString("Signal: ") + "\n" + ToString("----------------") + "\n" + signal.ToString() + "\n"; content += ToString("----------------") + "\n" + ToString("Conditions: ") + "\n" + ToString("----------------") + "\n" + signal.conditions + "\n"; // // Save Content to File ... result = collector.Save( filePath, content // ); // // Return Result ... return result; } // bool SaveWin(XCATBEATrade &trade) { // bool result = false; // if (!mSaveWins) { return result; } // string filePath = GetWinsFilePath(trade); result = IsValid(filePath); if (!result) { return result; } // // Preparing Conditions Content ... string content = trade.GetConditionsString(); // result = collector.Append( filePath, content // ); // return result; } // bool SaveLost(XCATBEATrade &trade) { // bool result = false; // if (!mSaveLosts) { return result; } // string filePath = GetLostsFilePath(trade); result = IsValid(filePath); if (!result) { return result; } // // Preparing Conditions Content ... string content = trade.GetConditionsString(); // result = collector.Append( filePath, content // ); // return result; } // bool SaveRestrictions(XCATBEASymbolInfo &info) { // bool result = false; // if (!mSaveRestrictions) { return result; } // result = info.IsValid(); if (!result) { return result; } // string filePath = GetRestrictionsFilePath(info.symbol); result = IsValid(filePath); if (!result) { return result; } // string content = info.GetRestrictionDescription(restrictionsPeriod); // result = collector.Append( filePath, content // ); // return result; } // // Symbol Info ... // bool FindSymbolIndex( int &index, XCATBEASymbolInfo &item // ) { // bool result = false; // index = -1; // result = item.IsValid(); if (!result) { return result; } // result = HasChild(symbols); if (!result) { return result; } // index = FindIndex( item, symbols // ); // result = IsValidIndex(index); // return result; } // bool FindSymbolIndex( int &index, string symbol // ) { // bool result = false; // index = -1; // result = IsValid(symbol) && HasChild(symbols); if (!result) { return result; } // int count = ArraySize(symbols); for (int i = 0; i < count; i++) { // result = symbols[i].symbol == symbol; if (result) { // index = i; break; } } // result = IsValidIndex(index); // return result; } // void AddOrUpdateSymbolInfo(XCATBEASymbolInfo &item) { // bool has = item.IsValid(); if (!has) { return; } // int index = -1; bool isExists = FindSymbolIndex( index, item // ); if (!isExists) { // AddRef( item, symbols // ); } else { // symbols[index].longs += item.longs; symbols[index].shorts += item.shorts; symbols[index].trades += item.trades; symbols[index].losses += item.losses; symbols[index].profits += item.profits; symbols[index].managedSLs += item.managedSLs; symbols[index].managedTPs += item.managedTPs; symbols[index].managedTrades += item.managedTrades; symbols[index].managedProfits += item.managedProfits; // // Handle Remove on Managed SL based On Managed TP ... if (item.managedTPs > 0) { // symbols[index].managedSLs -= item.managedTPs; if (symbols[index].managedSLs < 0) { symbols[index].managedSLs = 0; } } // // Handle Remove on Managed TP based On Managed SL ... if (item.managedSLs > 0) { // symbols[index].managedTPs -= item.managedSLs; if (symbols[index].managedTPs < 0) { symbols[index].managedTPs = 0; } } } } // void ResetSymbolsRestrictions() { // bool has = HasChild(symbols); if (has) { // string prefix = "Protector: "; // datetime start = TimeCurrent(); double balance = trader.mAccount.GetBalance(); bool hasRestrictions = HasRestrictions(); if (!hasRestrictions) { // balance = 0; start = NULL; } // int count = ArraySize(symbols); for (int i = 0; i < count; i++) { // XCATBEASymbolInfo iInfo = symbols[i]; if (reportRestrictions) { // string message = iInfo.GetRestrictionDescription(restrictionsPeriod); SendAlert(message); } SaveRestrictions(iInfo); // symbols[i].managedSLs = 0; symbols[i].managedTPs = 0; symbols[i].managedTrades = 0; symbols[i].managedProfits = 0; symbols[i].managedStart = start; symbols[i].managedBalance = balance; // iInfo.Clean(); } // string message = prefix + "Reset Symbols Restrictions"; HandleReportProtector(message); } } // void HandleRestrictions() { // bool isNewDay = timeTracker.IsNewDay(); bool isNewHour = timeTracker.IsNewHour(); bool isNewWeek = timeTracker.IsNewWeek(); bool isNewMonth = timeTracker.IsNewMonth(); // // Reset Force Closed Flag on new Day Starts ... if (isNewDay) { isForceCloseAtTime = false; } // bool canResetRestrictions = ( // // Hourly ... (isNewHour && restrictionsPeriod == XCATBEA_TRADE_RESTRICATION_HOURLY) // || // // Daily ... (isNewDay && restrictionsPeriod == XCATBEA_TRADE_RESTRICATION_DAILY) // || // // Weekly ... (isNewWeek && restrictionsPeriod == XCATBEA_TRADE_RESTRICATION_WEEKLY) // || // // Monthly ... (isNewMonth && restrictionsPeriod == XCATBEA_TRADE_RESTRICATION_MONTHLY) // ) // ; if (canResetRestrictions) { ResetSymbolsRestrictions(); } } // // Symbol Session ... // bool FindSymbolSession( int &index, string symbol // ) { // bool result = false; // index = -1; // result = IsValid(symbol) && HasChild(sessions); if (!result) { return result; } // int count = ArraySize(sessions); for (int i = 0; i < count; i++) { // result = sessions[i].symbol == symbol; if (result) { // index = i; break; } } // result = IsValidIndex(index); // return result; } // void AddTradingSession( XCATBEATradeSession &session, XCATBEASymbolSession &symbolSession // ) { // bool has = session.IsValid() && symbolSession.IsValid(); if (!has) { return; } // // Check Session Exists in Symbol Session ... bool isExists = false; if (HasChild(symbolSession.sessions)) { // int count = ArraySize(symbolSession.sessions); for (int i = 0; i < count; i++) { // isExists = symbolSession.sessions[i].end == session.end && symbolSession.sessions[i].start == session.start; if (isExists) { break; } } } // if (!isExists) { // AddRef( session, symbolSession.sessions // ); } } // // Private ... private: // // Props ... // bool allowTrade; bool allowLongs; bool allowShorts; // string forceCloseAt; bool isForceCloseAtTime; // int maxAllowedTPs; int maxAllowedSLs; int maxAllowedTrades; ENUM_XCATBEA_TRADE_RESTRICTION_PERIOD restrictionsPeriod; bool HasRestrictions() { return restrictionsPeriod != XCATBEA_TRADE_RESTRICATION_NONE; } // bool mSaveWins; bool mSaveLosts; bool mSaveTrades; bool mSaveSignals; bool mSaveRestrictions; // bool reportTrades; bool reportSignals; bool reportProtector; bool reportRestrictions; bool reportAfterTradesBalance; // CArrayObj mObjects; // XCATBEATrade trades[]; XCATBEASymbolInfo symbols[]; XCATBEASymbolSession sessions[]; }; //