/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // ---------------------------------------------- // Name: XSCBaseEA // Description: provides all Base EA 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.base.class.mq5" #include "../Classes/x-saherelm.xalert.class.mq5" #include "../Classes/x-saherelm.xtrade.class.mq5" // // Define On Signal Event Handler Type ... typedef void (*TOnSignal)(XSignal &signals[]); // // Class Definition ... class XSCBaseEA : public XSCBase { // // Public ... public: // // Props ... // // Constructor ... XSCBaseEA( // // XTrade Class Requirements ... int slippage, // Specify Slippage ulong magicNumber, // Specify Magic Number int manageInterval, // Manager Check Intervals Seconds double maxAllowedSpread, // Max Allowed Spred for Opening Trades int maxAllowedPositions, // Max Allowed Positions double maxAllowedDrawdownFactor, // Max Allowed Drawdown Factor // // Position Management ... bool allowLong = true, // Allow Long Trades bool allowShort = true, // Allow Short Trades double minProfitPerTrade = 0, // Min Profit Per Trade based On Volume Factor (Hedge) double minProfitPerVolumeFactor = 0, // Min Volume Factor for Calculating Profit (Hedge) // // Event Handlers ... TOnStopLoss onStopLossTriggered = NULL, // On StopLoass Triggered Event Handler TOnTakeProfit onTakeProfitTriggered = NULL, // On TakeProfit Triggered Event Handler TOnDealsChanged onDealsChangedHandler = NULL, // On Deals Changed Event Handler TOnOrdersChanged onOrdersChangedHandler = NULL, // On Orders Changed Event Handler TOnPositionsChanged onPositionsChangedHandler = NULL, // On Positions Changed Event Handler TOnTradeStateChanged onTradeStateChangedHandler = NULL, // On Trade State Changed Event Handler // // Log Handler ... bool enableAlerts = true, // Enable Alerts bool logAlerts = true, // Log Alerts bool terminalAlerts = false, // Terminal Alerts bool mailAlerts = false, // Mail Alerts bool pushAlerts = false // Push Alerts ) { // // Instance XSCTrade Class ... mTrader = new XSCTrade( slippage, magicNumber, manageInterval, maxAllowedSpread, maxAllowedPositions, maxAllowedDrawdownFactor // ); // mIsInTestMode = IsRunningOnTestMode(); // // Position Management ... mAllowLong = allowLong; mAllowShort = allowShort; mMinProfitPerTrade = minProfitPerTrade; mMinProfitPerVolumeFactor = minProfitPerVolumeFactor; // // Set Event Handlers ... mOnStopLossTriggered = onStopLossTriggered; mOnTakeProfitTriggered = onTakeProfitTriggered; mOnDealsChangedHandler = onDealsChangedHandler; mOnOrdersChangedHandler = onOrdersChangedHandler; mOnPositionsChangedHandler = onPositionsChangedHandler; mOnTradeStateChangedHandler = onTradeStateChangedHandler; // // Add Event Listeners to XTrade Class if they Provided ... if (mOnStopLossTriggered != NULL) { mTrader.AddOnStopLossEventHandler(mOnStopLossTriggered); } // if (mOnTakeProfitTriggered != NULL) { mTrader.AddOnTakeProfitEventHandler(mOnTakeProfitTriggered); } // if (mOnDealsChangedHandler != NULL) { mTrader.AddOnDealsChangedEventHandler(mOnDealsChangedHandler); } // if (mOnOrdersChangedHandler != NULL) { mTrader.AddOnOrdersChangedEventHandler(mOnOrdersChangedHandler); } // if (mOnPositionsChangedHandler != NULL) { mTrader.AddOnPositionsChangedEventHandler(mOnPositionsChangedHandler); } // if (mOnTradeStateChangedHandler != NULL) { mTrader.AddOnTradeStateChangedEventHandler(mOnTradeStateChangedHandler); } // // Instantiate Alert Class ... mAlert = new XSCAlert( GetTag(), enableAlerts, logAlerts, terminalAlerts, mailAlerts, pushAlerts // ); } // // Deconstractor ... ~XSCBaseEA() { // delete mTrader; delete mAlert; } // // Properties Getter(s) / Setter(s) ... // // Retrieve Slippage ... int GetSlippage() { return mTrader.GetSlippage(); } // // Retrieve Magic Number ... ulong GetMagicNumber() { return mTrader.GetMagicNumber(); } // // Retrieve Max Allowed Spread for Trading ... double GetMaxAllowedSpread() { return mTrader.GetMaxAllowedSpread(); } // // Retrieve Max Allowed Same Time Positions Count ... int GetMaxAllowedPositions() { return mTrader.GetMaxAllowedPositions(); } // // Retrieve Max Allowed Drawdown for Opening new Trades ... double GetMaxAllowedDrawdownFactor() { return mTrader.GetMaxAllowedDrawdownFactor(); } // bool GetAllowLong() { return mAllowLong; } // void SetAllowLong(bool value) { mAllowLong = value; } // bool GetAllowShort() { return mAllowShort; } // void SetAllowShort(bool value) { mAllowShort = value; } // double GetMinProfitPerTrade() { return mMinProfitPerTrade; } // void SetMinProfitPerTrade(double value) { // if (value <= 0) { value = 0; } // if (value == mMinProfitPerTrade) { return; } // mMinProfitPerTrade = value; } // double GetMinProfitPerVolumeFactor() { return mMinProfitPerVolumeFactor; } // void SetMinProfitPerVolumeFactor(double value) { // if (value <= 0) { value = 0; } // if (value == mMinProfitPerVolumeFactor) { return; } // mMinProfitPerVolumeFactor = value; } // // Alert ... // bool GetLogAlerts() { return mAlert.GetLogAlerts(); } // void SetLogAlerts(bool value) { mAlert.SetLogAlerts(value); } // bool GetEnableAlerts() { return mAlert.GetEnableAlerts(); } // void SetEnableAlerts(bool value) { mAlert.SetEnableAlerts(value); } // bool GetPushAlerts() { return mAlert.GetPushAlerts(); } // void SetPushAlerts(bool value) { mAlert.SetPushAlerts(value); } // bool GetMailAlerts() { return mAlert.GetMailAlerts(); } // void SetMailAlerts(bool value) { mAlert.SetMailAlerts(value); } // bool GetTerminalAlerts() { return mAlert.GetTerminalAlerts(); } // void SetTerminalAlerts(bool value) { mAlert.SetTerminalAlerts(value); } // // Event Listeners ... void AddOnSignalEventHandler(TOnSignal listener) { // Add( listener, mOnSignalEventHandlers // ); } // // Overrides ... // // Customize Token ... virtual string GetToken() { return GetSpecificToken(this); } // virtual string GetTag() { return this.GetToken(); } // // Virtual Functions ... // // Handle Chart Events ... virtual void OnChartEvent( const int id, // event ID const long &lparam, // long type event parameter const double &dparam, // double type event parameter const string &sparam // string type event parameter ) { } // // OnTick Handler ... virtual void OnTick() { // // Check Trade Events ... OnTrade(); // // First Check For Any Guard Actions ... // then Handle Guards if Provided ... XGuard guards[]; bool hasGuard = CheckForGuard(guards); if (hasGuard) { DoGuards(guards); } // // Second Check for Position Management or Equity Management // Actions like Supports or etc ... bool isIgnored = HandleStateManagement(); if (isIgnored) { return; } // // Second Check For Signals ... // then Execute Signals if Provided ... XSignal signals[]; int signalsCount = RequestForSignal(signals); if (signalsCount <= 0) { return; } // // Execute Signals ... HandleSignalsExecution(signals); } // // Handle StopLoss Event ... virtual void OnStopLossTriggered(const XDeal &deal) {} // // Handle TakeProfit Event ... virtual void OnTakeProfitTriggered(const XDeal &deal) {} // // Handle Deals Changed Event ... virtual void OnDealsChangedHandler(int count) {} // // Handle Order Changed Event ... virtual void OnOrdersChangedHandler(int count) {} // // Handle Position Changed Event ... virtual void OnPositionsChangedHandler(int count) {} // // Handle Trade State Changed ... virtual void OnTradeStateChangedHandler(const XOnTradeHandlerState &state) {} // // Handle On Trade ... virtual void OnTrade() { // XOnTradeHandlerState state = mTrader.HandleOnTrade(); // if ( !state.hasNewDeal && !state.hasNewOrder && !state.hasNewPosition && !state.hasNewHistoryOrder) { return; } // // STATE ... // // Notfy Trade State Changed Event ... OnTradeStateChangedHandler(state); // // DEALS ... if (state.hasNewDeal) { // // Notfy Deals Changed Event ... OnDealsChangedHandler(state.newDeals); // // Retrieve Last Deal ... XDeal deals[]; mTrader.GetDeals(deals); int dealsCount = ArraySize(deals); if (dealsCount > 0) { // XDeal lastDeal = deals[0]; // if (lastDeal.reason == DEAL_REASON_TP) { OnTakeProfitTriggered(lastDeal); } else if (lastDeal.reason == DEAL_REASON_SL) { OnStopLossTriggered(lastDeal); } } } // // ORDERS ... if (state.hasNewOrder || state.hasNewHistoryOrder) { // // Notfy Orders Changed Event ... OnOrdersChangedHandler(state.newOrders + state.newHistoryOrders); } // // POSITIONS ... if (state.hasNewPosition) { // // Notfy Positions Changed Event ... OnPositionsChangedHandler(state.newPositions); } } // // Retrieve Dept Of Market ... virtual void OnBookEvent(const string &symbol) { // // TODO: Implement this ... } // // Check For any Guard Actions ... virtual bool CheckForGuard(XGuard &guards[]) { return false; } // // When a Guard Notified to Do Support Signal ... // this Method Calls For Retrieve Support Signal ... // if it's Provided, Execute it ... virtual bool RequestForSupport( XSignal &support, // Holds Support Signal, if Provided string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period // Specified Time Frame ) { return false; } // // Handle Position(s)/ Order(s) or EQM Supporting Action ... // if returns false, prevent for process Tick ... virtual bool HandleStateManagement() { return false; } // // Here we Have to Check Market based // on our Strategy for Trading ... // and if a Good Entry founded ... // model it as an XSignal struct and passed it to result ... virtual int RequestForSignal( XSignal &signals[] // Holds Signals ... ) { return 0; } // // Execute Provided Signals ... virtual void HandleSignalsExecution(XSignal &signals[]) { // int signalsCount = ArraySize(signals); if (signalsCount <= 0) { return; } // // Filter Signals Based on their Types ... XSignal filteredSignals[]; // // Try to Filter Signals ... for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = signals[i]; // bool isLong = IsLong(iSignal.type); // if ((isLong && !GetAllowLong()) || (!isLong && !GetAllowShort())) { continue; } // AddRef( iSignal, filteredSignals // ); } // int filteredSignalsCount = ArraySize(filteredSignals); if (filteredSignalsCount <= 0) { return; } // // Try to Execute Signals ... ENUM_X_SIGNAL_EXECUTION_RESULT reasons[]; int executedSignalsCount = mTrader.ExecuteSignals( filteredSignals, reasons // ); // string message = NULL; if (executedSignalsCount == 0) { message = "(" + ToString(filteredSignalsCount) + ") Signals Execution Failed ..."; } else if (executedSignalsCount < filteredSignalsCount) { // int diff = filteredSignalsCount - executedSignalsCount; // message = "Failed to Execute (" + ToString(diff) + ") Signals ..."; } else if (executedSignalsCount == filteredSignalsCount) { message = "(" + ToString(executedSignalsCount) + ") Signals Executed Successfully ..."; } // if (IsValid(message)) { mAlert.Alert(message); } } // // Protected ... protected: // // Props ... // // Signal Event Listeners ... void NotifyOnSignalEventHandlers(XSignal &signals[]) { // int listenersCount = ArraySize(mOnSignalEventHandlers); if (listenersCount <= 0) { return; } // for (int i = 0; i < listenersCount; i++) { // TOnSignal iListener = mOnSignalEventHandlers[i]; // iListener(signals); } } // // Position Management ... // // Calculate Required Profit for Hedging ... double CalculateRequiredProfitForHedge( XPosition &positions[] // Source ) { // double result = 0; // int positionsCount = ArraySize(positions); if (positionsCount <= 0) { return result; } // bool isHedgingEnable = mMinProfitPerTrade > 0 && mMinProfitPerVolumeFactor > 0; if (!isHedgingEnable) { return result; } // for (int i = 0; i < positionsCount; i++) { // XPosition iPosition = positions[i]; // if (mMinProfitPerTrade > 0 && mMinProfitPerVolumeFactor > 0) { // result += ((iPosition.volume / mMinProfitPerVolumeFactor) * mMinProfitPerTrade) + (-1 * iPosition.swap); } } // return result; } // // Calculate Positions Profit Summary ... double CalculatePositionsProfit( XPosition &positions[] // Source ) { // double result = 0; // int positionsCount = ArraySize(positions); if (positionsCount <= 0) { return result; } // for (int i = 0; i < positionsCount; i++) { // XPosition iPosition = positions[i]; // result += iPosition.profit; } // return result; } // // Guards ... // // Do All Provided Guards ... void DoGuards(XGuard &guards[]) { // int guardsCount = ArraySize(guards); if (guardsCount <= 0) { return; } // for (int i = 0; i < guardsCount; i++) { // XGuard iGuard = guards[i]; // switch (iGuard.action) { // // Hedge In Profit Specified Positions ... case X_GUARD_ACTION_HEDGE: HandleGuardHedgeAction( iGuard.provider, iGuard.symbol, iGuard.type, iGuard.period); break; // // Trail Stop Loss in Specified Positions ... case X_GUARD_ACTION_TRAIL_STOP: HandleGuardTrailingStopAction( iGuard.provider, iGuard.symbol, iGuard.type, iGuard.period, iGuard.dblPayLoad); break; // // Force Close Specified Positions ... case X_GUARD_ACTION_FORCE_CLOSE: HandleGuardForceCloseAction( iGuard.provider, iGuard.symbol, iGuard.type, iGuard.period); break; // // Generate and Place Support Signals ... case X_GUARD_ACTION_ADD_SUPPORT_SIGNAL: HandleGuardAddSupportPositionAction( iGuard.provider, iGuard.symbol, iGuard.type, iGuard.period); break; // // Delete All Placed Pending Orders ... case X_GUARD_ACTION_CANCEL_PENDING_ORDERS: HandleGuardCancelPendingOrdersAction( iGuard.provider, iGuard.symbol, iGuard.type, iGuard.period); break; } } } // // Close All Specified Provider's Positions in Profit Summary ... void HandleGuardHedgeAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period, // Specified Time Frame double minProfitForHedging = 1 // Specified Profit for Hedge ) { // XPosition positions[]; int positionsCount = mTrader .GetPositions( positions, symbol, provider, period, type // ); if (positionsCount <= 1) { return; } // double profit = CalculatePositionsProfit(positions); double requiredProfit = CalculateRequiredProfitForHedge(positions); // bool isReadyForHedge = profit >= requiredProfit; if (!isReadyForHedge) { return; } // string comment = "Guard Hedge " + provider; int closed = mTrader.Close( positions, comment // ); if (closed > 0) { // string message = "Guard Hedge " + provider + "'s Positions on Profit: " + ToString(profit); // mAlert.Alert(message); } } // // Close Max In Profit Trade Guard Action ... void HandleGuardCloseInProfitAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period, // Specified Time Frame ENUM_X_POSITION_SELECT_METHODS method = NULL // Select Method ) { // XPosition positions[]; int positionsCount = mTrader .GetInProfitPositions( positions, symbol, provider, period, type, method // ); if (positionsCount <= 0) { return; } // string comment = "Close InProfit ..."; mTrader.Close( positions, comment // ); // string message = "Guard Close (" + ToString(positionsCount) + ") Postion(s) In Profit ..."; // mAlert.Alert(message); } // // Close Max In Drawdown Trade Guard Action ... void HandleGuardCloseInDrawdownAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period, // Specified Time Frame ENUM_X_POSITION_SELECT_METHODS method = NULL // Select Method ) { // XPosition positions[]; int positionsCount = mTrader .GetInDrawdownPositions( positions, symbol, provider, period, type, method // ); if (positionsCount <= 0) { return; } // string comment = "Close InDrawdown ..."; mTrader.Close( positions, comment // ); // string message = "Guard Close (" + ToString(positionsCount) + ") Position(s) In Drawdown ..."; // mAlert.Alert(message); } // // Force Close Specified Set Of Position ... void HandleGuardForceCloseAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period, // Specified Time Frame ulong ticket = 0 // Specific Ticket ) { // string comment = ""; // // Close All Positions ... if (ticket == 0) { // XPosition positions[]; int positionsCount = mTrader.GetPositions( positions, symbol, provider, period, type // ); if (positionsCount <= 0) { return; } // comment = provider + " Force Close Guard ..."; // mTrader.Close( positions, comment // ); } // // Close Specific Position ... else { // XPosition position; bool hasPosition = mTrader .GetPosition( ticket, position // ); // if (hasPosition) { // comment = provider + "(" + ToString(ticket) + ")" + " Force Close Guard ..."; // mTrader.Close( ticket, comment // ); } } // if (IsValid(comment)) { mAlert.Alert(comment); } } // // Trailing Specified Set Of Positions Stop Losses ... void HandleGuardTrailingStopAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period, // Specified Time Frame double sl // Desired SL Value ... ) { // if (sl <= 0) { return; } // NormalizePrice( sl, symbol // ); // XPosition positions[]; int positionsCount = mTrader .GetPositions( positions, symbol, provider, period, type // ); if (positionsCount <= 0) { return; } // int modified = 0; for (int i = 0; i < positionsCount; i++) { // XPosition iPosition = positions[i]; // string comment = PrepareSLTrailTag(iPosition.comment); // if (iPosition.sl == sl) { continue; } // bool isModified = mTrader.Modify( iPosition.ticket, sl, iPosition.tp, comment); if (isModified) { modified++; } } // if (modified > 0) { // string message = "(" + ToString(modified) + ") Position's Stop Trailed Successfully ..."; // mAlert.Alert(message); } } // // Add Support Signal for Specified Positions ... void HandleGuardAddSupportPositionAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period // Specified Time Frame ) { // XSignal support; bool hasSupport = RequestForSupport( support, provider, symbol, type, period // ); // if (!hasSupport || !support.IsValid()) { return; } // // TODO: May be need to add Support Tag to comments ... // ENUM_X_SIGNAL_EXECUTION_RESULT reason = NULL; bool isExecuted = mTrader.ExecuteSignal( support, reason); // string comment = NULL; if (!isExecuted) { comment = "Support Execution Failed doue " + ToString(reason); } else { comment = "Guard Support Signal Executed Successfully ..."; } // if (IsValid(comment)) { mAlert.Alert(comment); } } // // Cancel all Placed Orders ... void HandleGuardCancelPendingOrdersAction( string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period // Specified Time Frame ) { // XOrder orders[]; int ordersCount = mTrader.GetOrders( orders, symbol, provider, period, ToOrderType(type), ORDER_STATE_PLACED, true // Filter by Magic ... // ); if (ordersCount <= 0) { return; } // int canceledOrdersCount = mTrader.CancelOrders(orders); // if (canceledOrdersCount > 0) { // string message = "Guard Cancel (" + ToString(canceledOrdersCount) + ") Pending Orders ..."; // mAlert.Alert(message); } } // // Private ... private: // // Props ... // // Trade Class Instance ... XSCTrade *mTrader; XSCAlert *mAlert; // // Check in Test Mode or not ... bool mIsInTestMode; // // Signal Event Listeners ... TOnSignal mOnSignalEventHandlers[]; // // Position Management ... bool mAllowLong; // Allow Long Trades bool mAllowShort; // Allow Short Trades double mMinProfitPerTrade; // Min Profit Per Trade based On Volume Factor (Hedge) double mMinProfitPerVolumeFactor; // Min Volume Factor for Calculating Profit (Hedge) // // Event Handlers ... TOnStopLoss mOnStopLossTriggered; TOnTakeProfit mOnTakeProfitTriggered; TOnDealsChanged mOnDealsChangedHandler; TOnOrdersChanged mOnOrdersChangedHandler; TOnPositionsChanged mOnPositionsChangedHandler; TOnTradeStateChanged mOnTradeStateChangedHandler; }; //