/////////////////////////////////////////////////////// // // 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[]); // // Tokens ... string XINProfitToken = "INP"; string XINDrawdownToken = "IND"; string XProfitsToken = "PFS"; string XTimesToken = "TMS"; string XPricessToken = "PCS"; // enum ENUM_X_PROFIT_TRACK_FIELDS { X_PROFIT_TRACK_PROFIT, X_PROFIT_TRACK_LONGS_PROFIT, X_PROFIT_TRACK_SHORTS_PROFIT, }; // // Model Account Profits ... struct XProfitTrack { // int longs; // Number of Long ... int shorts; // Number of Short ... // double profit; // Profit of All ... double longsProfit; // Profits of Longs ... double shortsProfit; // Profits of Shorts ... // double profitFrom; // Profit of All ... double longsProfitFrom; // Profits of Longs ... double shortsProfitFrom; // Profits of Shorts ... // double volume; // Volume ... double longsVolume; // Volume of Longs ... double shortsVolume; // Volume of Shorts ... // datetime time; // Check Time ... // XProfitTrack() { Clean(); } // // Tools ... // // Initialization ... bool Init(XSCTrade *mTrader) { // bool result = false; // XPosition positions[]; int positionsCount = mTrader.GetPositions(positions); result = positionsCount > 0; if (!result) { return result; } // for (int i = 0; i < positionsCount; i++) { // XPosition iPosition = positions[i]; // bool isLong = IsLong(iPosition.type); // if (isLong) { // longs++; longsVolume += iPosition.volume; longsProfit += iPosition.profit; } else { // shorts++; shortsVolume += iPosition.volume; shortsProfit += iPosition.profit; } // profit += iPosition.profit; volume += iPosition.volume; } // result = IsValid(); if (result) { time = TimeCurrent(); } // return result; } // double SelectField(ENUM_X_PROFIT_TRACK_FIELDS field) { // double result = profit; // switch (field) { // case X_PROFIT_TRACK_LONGS_PROFIT: result = longsProfit; break; // case X_PROFIT_TRACK_SHORTS_PROFIT: result = shortsProfit; break; // default: case X_PROFIT_TRACK_PROFIT: result = profit; break; } // return result; } // // Cleanup ... void Clean() { // longs = 0; shorts = 0; profit = 0; volume = 0; profitFrom = 0; longsVolume = 0; longsProfit = 0; shortsVolume = 0; shortsProfit = 0; longsProfitFrom = 0; shortsProfitFrom = 0; // time = NULL; } // // Validate ... bool IsValid() { // bool result = false; // result = // (longs > 0 || shorts > 0) // && // volume > 0 // ; // return result; } // }; // // Track Specific Position ... struct XPositionTrack { // ulong ticket; // Position Ticket string symbol; // Position Symbol ENUM_POSITION_TYPE type; // Position Type ENUM_TIMEFRAMES period; // Position Period // double price[]; // Check Price ... double profit[]; // Check Profit ... datetime time[]; // Check Time ... // double tp; // Current TP double sl; // Current SL double volume; // Current Volume double entry; // Position Entry datetime openAt; // Position Open Time // int inProfitCount; // Number of Cross in DrawDown int inDrawdownCount; // Number of Crossed in Profit // // Constructor ... XPositionTrack() { Clean(); } // // Tools ... // // Initialization ... bool Init( XPosition &position // Source Position ) { // bool result = false; // Clean(); // result = position.IsValid(); if (!result) { return result; } // ticket = position.ticket; symbol = position.symbol; period = position.period; type = position.type; tp = position.tp; sl = position.sl; entry = position.entry; volume = position.volume; openAt = position.openAt; // datetime cTime = TimeCurrent(); Add( cTime, time // ); // Add( position.profit, profit // ); // Add( position.price, price // ); // result = IsValid(); // return result; } // bool Update( XPosition &position // Source Position ) { // bool result = false; // // Check Current Model is Valid or not ... result = IsValid(); if (!result) { return result; } // // Check Position is a Valid Model ... result = position.IsValid(); if (!result) { return result; } // // Check Current Model belongs to Given Position ... result = // entry == position.entry && openAt == position.openAt && symbol == position.symbol && period == position.period && type == position.type && ticket == position.ticket // ; if (!result) { return result; } // tp = position.tp; sl = position.sl; volume = position.volume; // datetime cTime = TimeCurrent(); // SetArrayStates(false); // Add( cTime, time // ); // Add( position.profit, profit // ); // Add( position.price, price // ); // SetArrayStates(true); // result = IsValid(); if (!result) { return result; } // // Fill In Profit or Drawdown Counters ... // bool isCrossInProfit = IsCrossedInProfit(); if (isCrossInProfit) { inProfitCount++; } // bool isCrossedInDrawdown = IsCrossedInDrawdown(); if (isCrossedInDrawdown) { inDrawdownCount++; } // return result; } // // Cleanup ... void Clean() { // ticket = 0; type = NULL; symbol = NULL; period = NULL; // tp = 0; sl = 0; volume = 0; entry = 0; openAt = NULL; // inProfitCount = 0; inDrawdownCount = 0; // Clean(price); Clean(profit); Clean(time); } // // Validate ... bool IsValid() { // bool result = false; // result = // ticket > 0 && type != NULL && period != NULL && volume > 0 && entry > 0 && openAt > 0 // ; // return result; } // void SetArrayStates(bool state) { // ArraySetAsSeries(price, state); ArraySetAsSeries(profit, state); ArraySetAsSeries(time, state); } // bool IsCrossedInProfit() { // bool result = false; // result = IsValid(); if (!result) { return result; } // result = ArraySize(time) > 1; if (!result) { return result; } // result = // profit[0] > 0 && profit[1] < 0 // ; // return result; } // bool IsCrossedInDrawdown() { // bool result = false; // result = IsValid(); if (!result) { return result; } // result = ArraySize(time) > 1; if (!result) { return result; } // result = // profit[0] < 0 && profit[1] > 0 // ; // return result; } // // Collector Tools ... // string GetToken() { return GetToken(this); } // bool IsModelValid() { return IsValid(); } // bool IsSameAs(XPositionTrack &track) { // bool result = false; // result = // track.IsValid() && type == track.type && ticket == track.ticket && symbol == track.symbol && period == track.period && openAt == track.openAt // ; // return result; } // int FindIndex( const XPositionTrack &values[] // Collection ) { // return FindIndex( this, values // ); } // // Converts a Model to String Representation ... string ToModelString() { // string result = NULL; // if (!IsValid()) { return result; } // // For Modeling a PositionTrack Object ... // - tp // - sl // - type // - entry // - ticket // - symbol // - period // - openAt // - volume // - inProfitCount // - inDrawdownCount // - time[] // - price[] // - profit[] // // Ticket ... string ticketStr = Surround(XIDToken, ticket); // // Symbol ... string SymbolStr = Surround(XSymbolToken, symbol); // // Type ... string typeStr = Surround(XTypeToken, type); // // Period ... int iPeriodInt = (int)period; string periodStr = Surround(XPeriodToken, iPeriodInt); // // TP ... string tpStr = Surround(XTPToken, tp); // // SL ... string slStr = Surround(XSLToken, sl); // // Volume ... string volumeStr = Surround(XVolumeToken, volume); // // Entry ... string entryStr = Surround(XEntryToken, entry); // // OpenAt ... string timeStr = Surround(XTimeToken, openAt); // // InProfitCount ... string inProfitCountStr = Surround(XINProfitToken, inProfitCount); // // InDrawdownCount ... string inDrawdownCountStr = Surround(XINDrawdownToken, inDrawdownCount); // // Time(s) ... string timesContentStr = ToString(time); string timesStr = Surround(XTimesToken, timesContentStr); // // Price(s) ... string pricesContentStr = ToString(price); string pricesStr = Surround(XPricessToken, pricesContentStr); // // Profit(s) ... string profitsContentStr = ToString(profit); string profitsStr = Surround(XProfitsToken, profitsContentStr); // string token = GetToken(); // result = // token + "[" + // ticketStr + SymbolStr + typeStr + periodStr + tpStr + slStr + volumeStr + entryStr + timeStr + inProfitCountStr + inDrawdownCountStr + timesStr + pricesStr + profitsStr + // "]" // ; // return result; } // // Pres and Fill Model based on it's String Representation ... bool ParseModel(string value) { // bool result = false; // Clean(); // result = IsValid(value); if (!result) { return result; } // // For Modeling a PositionTrack Object ... // - tp // - sl // - type // - entry // - ticket // - symbol // - period // - openAt // - volume // - inProfitCount // - inDrawdownCount // - time[] // - price[] // - profit[] // // TP ... tp = ParseDoubleSurrounded(value, XTPToken); // // SL ... tp = ParseDoubleSurrounded(value, XSLToken); // // Type ... type = ((ENUM_POSITION_TYPE)ParseIntSurrounded(value, XTypeToken)); // // Entry ... entry = ParseDoubleSurrounded(value, XEntryToken); // // Ticket ... ticket = ParseLongSurrounded(value, XIDToken); // // Symbol ... symbol = ParseStringSurrounded(value, XSymbolToken); // // Period ... period = ((ENUM_TIMEFRAMES)ParseIntSurrounded(value, XPeriodToken)); // // Open At ... openAt = ParseTimeSurrounded(value, XTimeToken); // // Volume ... volume = ParseDoubleSurrounded(value, XVolumeToken); // // InProfitCount ... inProfitCount = ParseIntSurrounded(value, XINProfitToken); // // InDrawdownCount ... inDrawdownCount = ParseIntSurrounded(value, XINDrawdownToken); // // Time(s) ... ParseTimeArraySurrounded( time, value, XTimesToken // ); // // Price(s) ... ParseDoubleArraySurrounded( price, value, XPricessToken // ); // // Profit(s) ... ParseDoubleArraySurrounded( profit, value, XProfitsToken // ); // result = IsValid(); // return result; } }; // // Type Definitions for XPosition Tracker Events ... typedef void (*TOnPositionState)(const XPositionTrack &track); // // an Struct for Holding Event Handled Items for Position Tracker ... struct XPositionTrackerState { // XPositionTrack inProfits[]; XPositionTrack inDrawdowns[]; // XPositionTrack onProfits[]; XPositionTrack onDrawdowns[]; // XPositionTrackerState() { Clean(); } // // Tools ... // // Cleanup ... void Clean() { // Clean(inProfits); Clean(inDrawdowns); // Clean(onProfits); Clean(onDrawdowns); } }; // // Position Tracker Class ... class XSCPositionTracker { // // Public ... public: // // Props ... // // Constructor ... void XSCPositionTracker(bool cleanStore = false) { // XSCDataCollector mBase; // if (cleanStore) { mBase.Clear(); } } // // Tools ... // // Cleaning Store ... void Clear() { // XSCDataCollector mBase; // mBase.Clear(); } // // Collect All Store ... int Collect(XPositionTrack &result[]) { // int mResult = 0; // XSCDataCollector mBase; // mResult = mBase.Collect(result); // return mResult; } // // Add Item To Store ... bool Add(XPositionTrack &item) { // bool result = false; // XSCDataCollector mBase; // result = mBase.Add(item); // return result; } // // Add Item(s) To Store ... int Add(XPositionTrack &items[]) { // int result = 0; // XSCDataCollector mBase; // result = mBase.Add(items); // return result; } // // Remove an Item from Store ... bool Remove(XPositionTrack &item) { // bool result = false; // XSCDataCollector mBase; // result = mBase.Remove(item); // return result; } // // Custom Functions ... // // Sync all Content using Specific Trader Class ... int Sync( XPositionTrackerState &state, // Hold all Changes State ... XSCTrade *mTrader // XSCTrade Instance for Synchronize ... ) { // int result = 0; // XPosition positions[]; int positionsCount = mTrader.GetPositions(positions); // ulong trackTickets[]; XPositionTrack trackers[]; int trackersCount = Collect(trackers); // // Nothing to Do ... if (positionsCount <= 0 && trackersCount <= 0) { return result; } // // Collect Exists Trackers Ticket's For Managing ... for (int i = 0; i < trackersCount; i++) { // XPositionTrack iTrack = trackers[i]; // bool isValid = iTrack.IsValid(); if (isValid) { // AddSpecific( iTrack.ticket, trackTickets // ); } } // // Clear Tracker Store for Update Later ... Clear(); // // Collect All Required to Add/Update Trackers here ... XPositionTrack syncedTracks[]; // // a Flag for Synchronization Performance ... bool hasTracker = trackersCount > 0; // // Loop through Position for Managing Trackers ... for (int i = 0; i < positionsCount; i++) { // XPosition iPosition = positions[i]; // // if there isn't any Track before ... if (!hasTracker) { // XPositionTrack iTrack; bool isInited = iTrack.Init(iPosition); if (isInited) { // AddRef( iTrack, syncedTracks // ); // FillState( iTrack, state // ); } // continue; } // // if there was some tracks ... // // Try to Find Position Ticket in Track Tickets ... int iTicketIdx = FindIndex( iPosition.ticket, trackTickets // ); bool isTrackedBefore = iTicketIdx >= 0; // // Add new Track ... if (!isTrackedBefore) { // XPositionTrack iTrack; bool isInited = iTrack.Init(iPosition); if (isInited) { // AddRef( iTrack, syncedTracks // ); // FillState( iTrack, state // ); } } // // Update Exists Track ... else { // XPositionTrack iTrack = trackers[iTicketIdx]; bool isUpdated = iTrack.Update(iPosition); if (isUpdated) { // AddRef( iTrack, syncedTracks // ); // FillState( iTrack, state // ); } } } // // Check Synced Tracks ... int syncedTracksCount = ArraySize(syncedTracks); if (syncedTracksCount <= 0) { return result; } // result = Add(syncedTracks); // return result; } // // Event Handlers ... // // Add Profit Event Handler ... void AddOnProfitEventHandler(TOnPositionState handler) { // AddSpecific( handler, mOnProfitEventHandlers // ); } // // Add Drawdown Event Handler ... void AddOnDrawdownEventHandler(TOnPositionState handler) { // AddSpecific( handler, mOnDrawdownEventHandlers // ); } // // Protected ... protected: // // Tools ... // // Event Handlers ... // // OnProfit ... void NotifyOnProfitEvent(XPositionTrack &track) { // int count = ArraySize(mOnProfitEventHandlers); if (count <= 0) { return; } // for (int i = 0; i < count; i++) { // TOnPositionState iHandler = mOnProfitEventHandlers[i]; iHandler(track); } } // // OnDrawdown ... void NotifyOnDrawdownEvent(XPositionTrack &track) { // int count = ArraySize(mOnDrawdownEventHandlers); if (count <= 0) { return; } // for (int i = 0; i < count; i++) { // TOnPositionState iHandler = mOnDrawdownEventHandlers[i]; iHandler(track); } } // // Private ... private: // // Props ... // TOnPositionState mOnProfitEventHandlers[]; TOnPositionState mOnDrawdownEventHandlers[]; // void FillState( XPositionTrack &track, XPositionTrackerState &state // ) { // // In Profit ... bool isInProfit = track.profit[0] > 0; if (isInProfit) { // AddRef( track, state.inProfits // ); } // // In Drawdown ... bool isInDrawdown = track.profit[0] < 0; if (isInDrawdown) { // AddRef( track, state.inDrawdowns // ); } // // On Profit ... bool isOnProfit = track.IsCrossedInProfit(); if (isOnProfit) { // AddRef( track, state.onProfits // ); } // // On Drawdown ... bool isOnDrawdown = track.IsCrossedInDrawdown(); if (isOnDrawdown) { // AddRef( track, state.onDrawdowns // ); } } }; // // 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 double staticVolume, // Static Volume for Positions // // 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; mStaticVolume = staticVolume; // // 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 // ); // // Instantiate XPositionTracker Class ... mPositionTracker = new XSCPositionTracker(); // // Do Synchronisation after Instantiate everything ... XPositionTrackerState state; mPositionTracker.Sync( state, mTrader // ); } // // Deconstractor ... ~XSCBaseEA() { // delete mTrader; delete mAlert; delete mPositionTracker; } // // 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(); } // double GetStaticVolume() { return mStaticVolume; } // void SetStaticVolume(double value) { // if (value <= 0) { value = 0.01; } // mStaticVolume = value; } // 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() { // // Draw ... Draw(); // // Check Trade Events ... OnTrade(); // HandleProfitManageMent(); // // First Check For Any Guard Actions ... // then Handle Guards if Provided ... XGuard guards[]; bool hasGuard = CheckForGuard(guards); if (hasGuard) { DoGuards(guards); } // XSignal signals[]; // // Second Check for Position Management or Equity Management // Actions like Supports or etc ... bool isIgnored = HandleStateManagement(signals); if (isIgnored) { return; } // // Second Check For Signals ... // then Execute Signals if Provided ... 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 ... } // // Draw On Chart if anything is required ... virtual void Draw() {} // // 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(XSignal &signals[]) { return false; } // // Regulary Filtered Signals passed to this // and here we can do Additional Signal Filtering // for Signal Management such as (Providers and etc) ... virtual int HandleSignalManagement(XSignal &signals[]) { // int result = ArraySize(signals); // return result; } // // 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; } // int managedSignalsCount = HandleSignalManagement(filteredSignals); if (managedSignalsCount <= 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 ..."; // for (int j = 0; j < ArraySize(reasons); j++) { // string iReason = ToString(reasons[j]); // message += "\n" + ToString(j) + ": " + iReason; } } 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); } } // virtual void OnGoingToProfit(XProfitTrack &track) {} virtual void OnLongsGoingToProfit(XProfitTrack &track) {} virtual void OnShortsGoingToProfit(XProfitTrack &track) {} // virtual void OnGoingToDrawdown(XProfitTrack &track) {} virtual void OnLongsGoingToDrawdown(XProfitTrack &track) {} virtual void OnShortsGoingToDrawdown(XProfitTrack &track) {} // virtual void OnPositionGoingInProfit(XPositionTrack &track) {} virtual void OnPositionGoingToDrawdown(XPositionTrack &track) {} // // Protected ... protected: // // Props ... // // Trade Class Instance ... XSCTrade *mTrader; XSCAlert *mAlert; XSCPositionTracker *mPositionTracker; // // Check in Test Mode or not ... bool mIsInTestMode; // // Tools ... // // 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 = SpecifiedCalculateRequiredProfitForHedge( positions, mMinProfitPerTrade, mMinProfitPerVolumeFactor // ); // return result; } // // Calculate Positions Profit Summary ... double CalculatePositionsProfit( XPosition &positions[] // Source ) { // double result = SpecifiedCalculatePositionsProfit(positions); // return result; } // // Profit Tracker ... // XProfitTrack mProfitTracks[]; // bool GetProfitTrack( XProfitTrack &track, int index = 0 // ) { // bool result = false; // if (index < 0) { index = 0; } // int tracksCount = CountProfitTracks(); result = tracksCount > 0 && index < tracksCount; if (!result) { return result; } // track.Clean(); // track = mProfitTracks[index]; // result = track.IsValid(); // return result; } // int CountProfitTracks() { return ArraySize(mProfitTracks); } // void AddProfitTrack() { // XProfitTrack track; bool isValid = track.Init(mTrader); if (!isValid) { return; } // ArraySetAsSeries(mProfitTracks, false); // AddRef( track, mProfitTracks // ); // ArraySetAsSeries(mProfitTracks, true); } // // Extract Specified Field Vales of XProfit Track Struct ... int ExtractProfitTrackField( double &result[], ENUM_X_PROFIT_TRACK_FIELDS field, int start = 0, int count = 10 // ) { // Clean(result); // if (start < 0) { start = 0; } // int end = start + count; // int tracksCount = CountProfitTracks(); if (end > tracksCount) { end = tracksCount; } // for (int i = start; i < end; i++) { // XProfitTrack iTrack; bool isValid = GetProfitTrack( iTrack, i // ); // if (!isValid) { continue; } // double iValue = iTrack.SelectField(field); Add( iValue, result // ); } // int mResult = ArraySize(result); // return mResult; } // double AverageProfitTrackField( ENUM_X_PROFIT_TRACK_FIELDS field, int start = 0, int count = 10 // ) { // double result = EMPTY_VALUE; // double items[]; int itemsCount = ExtractProfitTrackField( items, field, start, count // ); if (itemsCount <= 0) { return result; } // result = GetAverage(items); // return result; } // bool IsProfitTrackFieldIncreasing( ENUM_X_PROFIT_TRACK_FIELDS field, int start = 0, int count = 10 // ) { // bool result = false; // double items[]; int itemsCount = ExtractProfitTrackField( items, field, start, count // ); if (itemsCount <= 0) { return result; } // int from = itemsCount - 1; int to = 0; // result = IsIncreasing( from, to, items // ); // return result; } // bool IsProfitTrackFieldDecreasing( ENUM_X_PROFIT_TRACK_FIELDS field, int start = 0, int count = 10 // ) { // bool result = false; // double items[]; int itemsCount = ExtractProfitTrackField( items, field, start, count // ); if (itemsCount <= 0) { return result; } // int from = itemsCount - 1; int to = 0; // result = IsDecreasing( from, to, items // ); // return result; } // void HandleProfitManageMent() { // // Check Tracking Interval ... // static datetime lastProfitTracked = NULL; datetime cTime = TimeCurrent(); // int profitManagementInterval = PeriodSeconds(PERIOD_M5); int timeDiff = (int)cTime - (int)lastProfitTracked; // bool canManage = // lastProfitTracked == NULL ? true : timeDiff >= profitManagementInterval // ; if (!canManage) { return; } // lastProfitTracked = cTime; // // Do Track ... // AddProfitTrack(); // int profitsCount = CountProfitTracks(); if (profitsCount <= 1) { return; } // XProfitTrack cT; bool isValid = GetProfitTrack( cT, 0 // ); if (!isValid) { return; } // XProfitTrack pT; isValid = GetProfitTrack( pT, 1 // ); if (!isValid) { return; } // cT.profitFrom = pT.profit; cT.longsProfitFrom = pT.longsProfit; cT.shortsProfitFrom = pT.shortsProfit; // bool isGoningToProfit = // cT.profit > 0 && pT.profit <= 0 // ; if (isGoningToProfit) { // OnGoingToProfit(cT); } // bool isLongsGoningToProfit = // cT.longsProfit > 0 && pT.longsProfit <= 0 // ; if (isLongsGoningToProfit) { OnLongsGoingToProfit(cT); } // bool isShortsGoningToProfit = // cT.shortsProfit > 0 && pT.shortsProfit <= 0 // ; if (isShortsGoningToProfit) { OnShortsGoingToProfit(cT); } // bool isGoningToDrawdown = // cT.profit < 0 && pT.profit >= 0 // ; if (isGoningToDrawdown) { OnGoingToDrawdown(cT); } bool isLongsGoningToDrawdown = // cT.longsProfit < 0 && pT.longsProfit >= 0 // ; if (isLongsGoningToDrawdown) { OnLongsGoingToDrawdown(cT); } bool isShortsGoningToDrawdown = // cT.shortsProfit < 0 && pT.shortsProfit >= 0 // ; if (isShortsGoningToDrawdown) { OnShortsGoingToDrawdown(cT); } } // // Synchronize Positions and Position Tracker ... void SyncPositionTracker() { // // Check Syncing Interval ... // static datetime lastSyncPositionTracker = NULL; datetime cTime = TimeCurrent(); // int syncInterval = PeriodSeconds(PERIOD_M5); int lastDiff = (int)cTime - (int)lastSyncPositionTracker; // bool canSync = // lastSyncPositionTracker == NULL ? true : lastDiff >= syncInterval // ; if (!canSync) { return; } // lastSyncPositionTracker = cTime; // // Do Sync ... // XPositionTrackerState state; int synced = mPositionTracker.Sync( state, mTrader // ); if (synced > 0) { // // Check State and Do What we Want ... int onProfitsCount = ArraySize(state.onProfits); int onDrawdownCount = ArraySize(state.onDrawdowns); // // Do What we want whe a Position Dropped to Prefit ... if (onProfitsCount > 0) { // for (int i = 0; i < onProfitsCount; i++) { // XPositionTrack iTrack = state.onProfits[i]; // OnPositionGoingInProfit(iTrack); } } // // Do what we want when a Position Dropped to Drawdown ... if (onDrawdownCount > 0) { // for (int i = 0; i < onDrawdownCount; i++) { // XPositionTrack iTrack = state.onDrawdowns[i]; // OnPositionGoingToDrawdown(iTrack); } } } } // // 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 ... // // 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) double mStaticVolume; // Static Volume for Positions // // Event Handlers ... TOnStopLoss mOnStopLossTriggered; TOnTakeProfit mOnTakeProfitTriggered; TOnDealsChanged mOnDealsChangedHandler; TOnOrdersChanged mOnOrdersChangedHandler; TOnPositionsChanged mOnPositionsChangedHandler; TOnTradeStateChanged mOnTradeStateChangedHandler; }; //