From c1aa4fe8882e2bfcf2809f720314d5bbd647e698 Mon Sep 17 00:00:00 2001 From: Hadi Khazaee Asl Date: Sun, 26 May 2024 19:53:36 +0330 Subject: [PATCH] last ... --- Classes/x-saherelm.x121.xea.class.mq5 | 401 +++++---- Classes/x-saherelm.xea.class.mq5 | 1075 ++++++++++++++++++++++++- Experts/x-test.x121ea.mq5 | 14 +- Libraries/x-saherelm.common.lib.mq5 | 12 + 4 files changed, 1288 insertions(+), 214 deletions(-) diff --git a/Classes/x-saherelm.x121.xea.class.mq5 b/Classes/x-saherelm.x121.xea.class.mq5 index 4599854a..fc9b47a9 100644 --- a/Classes/x-saherelm.x121.xea.class.mq5 +++ b/Classes/x-saherelm.x121.xea.class.mq5 @@ -296,22 +296,55 @@ public: return result; } + // + // Synch Position Tracker ... + SyncPositionTracker(); + // HandleEQMState(signals); // - HandleHedging(positions); + HandleEQMHedge(positions); // HandleTrailManagement(positions); + // + // Close all Positions on 22 ... + // TODO: Make it Configurable ... + int closeHour = -1; + MqlDateTime cTime = GetCurrentTime(); + if (closeHour >= 0 && cTime.hour == closeHour) + { + // + string comment = "EQM Close on Time ..."; + + // + XPosition positions[]; + int positionsCount = mTrader.GetPositions(positions); + if (positionsCount <= 0) + { + return result; + } + + // + int closed = mTrader.Close( + positions, + comment // + ); + if (closed > 0) + { + mAlert.Alert(comment); + } + } + // // Must false ... return result; } // - void HandleHedging(XPosition &positions[]) + void HandleEQMHedge(XPosition &positions[]) { // // TODO: @@ -329,12 +362,20 @@ public: // Hedge ... if (positionsCount >= numberOfPositionsForHege) { + // + int maxAllowedPositions = GetMaxAllowedPositions(); + // double profit = CalculatePositionsProfit(positions); double requiredProfit = CalculateRequiredProfitForHedge(positions); // - bool isReadyForHedge = profit >= requiredProfit; + bool isReadyForHedge = + positionsCount < maxAllowedPositions + ? profit >= requiredProfit + : positionsCount >= maxAllowedPositions && profit >= (requiredProfit * 0.50) + ? true + : false; if (!isReadyForHedge) { return; @@ -385,66 +426,72 @@ public: { eqmMessage += "- Max Allowed Positions Reached ..."; } - mAlert.Alert(eqmMessage); + // mAlert.Alert(eqmMessage); // - int descriptorsCount = CountDescriptors(); - if (descriptorsCount <= 0) - { - return; - } + // Retrieve InDrawdown Positions Count ... + // Separate based on Directions and Symbols ... + // Accept Only One Signal based on each symbol Indirect ... + // for Fittion Positions to HedgeEnable in Long Time Positions ... - // - XSignal supportSignals[]; - for (int i = 0; i < descriptorsCount; i++) - { - // - X121ProviderDescriptor iDescriptor = mDescriptors[i]; + // // + // int descriptorsCount = CountDescriptors(); + // if (descriptorsCount <= 0) + // { + // return; + // } - // - XSignal iSupportSignals[]; - int iSupportSignalsCount = iDescriptor - .provider - .GenerateEQMSupportSignals( - iSupportSignals // - ); - if (iSupportSignalsCount <= 0) - { - continue; - } + // // + // XSignal supportSignals[]; + // for (int i = 0; i < descriptorsCount; i++) + // { + // // + // X121ProviderDescriptor iDescriptor = mDescriptors[i]; - // - Copy( - iSupportSignals, - supportSignals, - false // - ); - } + // // + // XSignal iSupportSignals[]; + // int iSupportSignalsCount = iDescriptor + // .provider + // .GenerateEQMSupportSignals( + // iSupportSignals // + // ); + // if (iSupportSignalsCount <= 0) + // { + // continue; + // } - // - int supportSignalsCount = ArraySize(supportSignals); - if (supportSignalsCount <= 0) - { - return; - } + // // + // Copy( + // iSupportSignals, + // supportSignals, + // false // + // ); + // } - // - ENUM_X_SIGNAL_EXECUTION_RESULT states[]; - int executedSupports = mTrader.ExecuteSignals( - supportSignals, - states, - ORDER_TIME_GTC, - NULL, - false // Ignore Policies ... - ); - if (executedSupports > 0) - { - // - string message = "EQM Execute (" + ToString(executedSupports) + ") Support Signals ..."; + // // + // int supportSignalsCount = ArraySize(supportSignals); + // if (supportSignalsCount <= 0) + // { + // return; + // } - // - mAlert.Alert(message); - } + // // + // ENUM_X_SIGNAL_EXECUTION_RESULT states[]; + // int executedSupports = mTrader.ExecuteSignals( + // supportSignals, + // states, + // ORDER_TIME_GTC, + // NULL, + // false // Ignore Policies ... + // ); + // if (executedSupports > 0) + // { + // // + // string message = "EQM Execute (" + ToString(executedSupports) + ") Support Signals ..."; + + // // + // mAlert.Alert(message); + // } } // @@ -455,7 +502,8 @@ public: // Make them Configurable ... int levelsOnClose = 6; int requiredAgeForSLTrail = 0; - int requiredDistanceForSLTrail = 30; + int requiredDistanceForSLTrail = 100; + return; // int positionsCount = ArraySize(positions); @@ -474,7 +522,7 @@ public: // bool canManageState = // - false + true // ; @@ -518,18 +566,19 @@ public: // double slDiff = PointToPrice(priceDistance, iPosition.symbol); - - // - double sl = - IsLong(iPosition.type) - ? iPosition.sl + slDiff - : iPosition.sl - slDiff; + double appliedSL = slDiff / 2; // // // double sl = // IsLong(iPosition.type) - // ? iPosition.entry + (slDiff / 2) - // : iPosition.entry - (slDiff / 2); + // ? iPosition.sl + slDiff + // : iPosition.sl - slDiff; + + // + double sl = + IsLong(iPosition.type) + ? iPosition.entry + appliedSL + : iPosition.entry - appliedSL; // bool isInit = trail.Init( @@ -608,6 +657,7 @@ public: // double slDiff = PointToPrice(priceDistance, iPosition.symbol); + double appliedSL = slDiff / 2; // double selectedValue = @@ -623,145 +673,8 @@ public: // double sl = isLong - ? selectedValue + slDiff - : selectedValue - slDiff; - - // - // Update Trail Values ... - bool isUpdated = trail.Update( - sl, - iPosition.profit // - ); - if (!isUpdated) - { - continue; - } - } - - // - if (trail.IsValid()) - { - // - string comment = trail.GenerateTag(); - bool isModified = mTrader.Modify( - trail.ticket, - trail.after, - iPosition.tp, - comment // - ); - if (isModified) - { - // - RemoveTrail(trail.ticket); - - // - AddSLTrail(trail); - - // - string message = "Position (" + ToString(trail.ticket) + ") Trailed SL on Level: " + ToString(trail.level); - mAlert.Alert(message); - } - } - } - else if (iPosition.profit < 0) - { - // - XTrail trail; - bool isTrailedBefore = GetSLTrail( - iPosition.ticket, - trail // - ); - - // - if (!isTrailedBefore) - { - // - // Add First Time ... - - // - // Check Position Age ... - int iPositionAge = iPosition.GetAge(iPosition.period); - if (iPositionAge < requiredAgeForSLTrail) - { - continue; - } - - // - // Check Position Profit Distance ... - double priceDistanceInPoint = PriceToPoint(MathAbs(iPosition.profit) / iPosition.volume, iPosition.symbol); - double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol); - if (priceDistance < requiredDistanceForSLTrail) - { - continue; - } - - // - double slDiff = PointToPrice(priceDistance, iPosition.symbol); - - // - double sl = - IsLong(iPosition.type) - ? iPosition.sl + slDiff - : iPosition.sl - slDiff; - - // - bool isInit = trail.Init( - iPosition.ticket, - iPosition.type, - iPosition.profit, - iPosition.sl, - sl // - ); - if (!isInit) - { - continue; - } - } - else - { - // - // Update Exists ... - - // - // Check Profit ... - bool isProfitPassed = iPosition.profit > trail.profit; - if (!isProfitPassed) - { - continue; - } - - // - // Check Age Distance from Last ... - int age = iBarShift( - iPosition.symbol, - iPosition.period, - trail.time, - false // - ); - if (age < requiredAgeForSLTrail) - { - continue; - } - - // - double priceDistanceInPoint = PriceToPoint((MathAbs(iPosition.profit) - MathAbs(trail.profit)) / iPosition.volume, iPosition.symbol); - double priceDistance = PointToPrice(priceDistanceInPoint, iPosition.symbol); - - // - double slDiff = PointToPrice(priceDistance, iPosition.symbol); - - // - double selectedValue = - // - iPosition.sl - // - ; - - // - double sl = - IsLong(iPosition.type) - ? selectedValue + slDiff - : selectedValue - slDiff; + ? selectedValue + appliedSL + : selectedValue - appliedSL; // // Update Trail Values ... @@ -926,11 +839,89 @@ public: } // - // Profit Management ... + // Handle Position Changed Event ... + void OnPositionsChangedHandler(int count) + { + // + // Synchronize Tracker ... + SyncPositionTracker(); + } // // Tools ... + // + // Position Tracker ... + + // + // Synchronize Positions and Position Tracker ... + void SyncPositionTracker() + { + // + 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; + } + + // + 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]; + + // + string message = "Position (" + ToString(iTrack.ticket) + ", " + iTrack.symbol + ") Dropped in Profit ..."; + mAlert.Alert(message); + } + } + + // + // 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]; + + // + string message = "Position (" + ToString(iTrack.ticket) + ", " + iTrack.symbol + ") Dropped in Drawdown ..."; + mAlert.Alert(message); + } + } + } + } + // // Protected ... protected: diff --git a/Classes/x-saherelm.xea.class.mq5 b/Classes/x-saherelm.xea.class.mq5 index dae37156..912d1fd1 100644 --- a/Classes/x-saherelm.xea.class.mq5 +++ b/Classes/x-saherelm.xea.class.mq5 @@ -29,6 +29,14 @@ // 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 { @@ -205,6 +213,1058 @@ struct XProfitTrack // }; +// +// 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 ... @@ -329,6 +1389,18 @@ public: pushAlerts // ); + + // + // Instantiate XPositionTracker Class ... + mPositionTracker = new XSCPositionTracker(); + + // + // Do Synchronisation after Instantiate everything ... + XPositionTrackerState state; + mPositionTracker.Sync( + state, + mTrader // + ); } // @@ -338,6 +1410,7 @@ public: // delete mTrader; delete mAlert; + delete mPositionTracker; } // @@ -847,7 +1920,6 @@ public: string iReason = ToString(reasons[j]); // - // ToString(GetSpread(filteredSignals[j].symbol)) + "," message += "\n" + ToString(j) + ": " + iReason; } } @@ -891,6 +1963,7 @@ protected: // Trade Class Instance ... XSCTrade *mTrader; XSCAlert *mAlert; + XSCPositionTracker *mPositionTracker; // // Check in Test Mode or not ... diff --git a/Experts/x-test.x121ea.mq5 b/Experts/x-test.x121ea.mq5 index ac14b12f..974bb9f4 100644 --- a/Experts/x-test.x121ea.mq5 +++ b/Experts/x-test.x121ea.mq5 @@ -31,16 +31,16 @@ // // Inputs ... -long x121EAMagicNumber = 78692110; // Magic Number -int x121EASlippage = 10; // Slippgae -string x121EASymbols = "EURUSDb"; // Symbols +long x121EAMagicNumber = 78692110; // Magic Number +int x121EASlippage = 10; // Slippgae +string x121EASymbols = "EURUSDb,USDCHFb,USDJPYb,GBPUSDb"; // Symbols // bool x121EAAllowLong = true; // Allow Long Trades bool x121EAAllowShort = true; // Allow Short Trades int x121EAManageInterval = 0; // Manager Check Intervals Seconds -int x121EAMaxAllowedPositions = 5; // Max Allowed Positions +int x121EAMaxAllowedPositions = 10; // Max Allowed Positions double x121EAMaxAllowedSpread = 0; // Max Allowed Spred for Opening Trades -double x121EAMinProfitPerTrade = 0.5; // Min Profit Per Trade based On Volume Factor (Hedge) +double x121EAMinProfitPerTrade = 5; // Min Profit Per Trade based On Volume Factor (Hedge) double x121EAMinProfitPerVolumeFactor = 0.01; // Min Volume Factor for Calculating Profit (Hedge) double x121EAMaxAllowedDrawdownFactor = 0.002; // Max Allowed Drawdown Factor // @@ -230,9 +230,7 @@ bool InitialEA() iDescriptor.symbol = iSymbol; iDescriptor.period = iPeriod; iDescriptor.allowLong = x121EAAllowLong; - // - // TODO: - iDescriptor.allowShort = false; // x121EAAllowShort; + iDescriptor.allowShort = x121EAAllowShort; Copy( signallers, iDescriptor.signallers); diff --git a/Libraries/x-saherelm.common.lib.mq5 b/Libraries/x-saherelm.common.lib.mq5 index f533700a..87784476 100644 --- a/Libraries/x-saherelm.common.lib.mq5 +++ b/Libraries/x-saherelm.common.lib.mq5 @@ -4217,6 +4217,18 @@ int Add( return result; } template +int AddSpecific( + T item, // item want to add + T &buffer[] // Destination buffer +) +{ + // + return Add( + item, + buffer // + ); +} +template int AddRef( T &item, // item want to add T &buffer[] // Destination buffer