/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Class Library // ---------------------------------------------- // Name: XSCX121EA // Description: provides all X121 EA requirements ... // - X5 Provider; // // // 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.xea.class.mq5" #include "../Classes/x-saherelm.x121.provider.class.mq5" // // Define On Signal Event Handler Type Specified for X5 ... typedef void (*TX121OnSignal)(X121ProviderDescriptor &descriptor); // // Position Protect Structure ... struct XProtectedPosition { // ulong ticket; // Position Ticket string symbol; // Position Symbol string provider; // Position Signaller ENUM_TIMEFRAMES period; // Position Time Frame ENUM_POSITION_TYPE type; // Position Type double volume; // Position Volume double entry; // Position Entry double sl; // Position Stop Loss double tp; // Position Take Profit datetime openAt; // Position Opening Time // // Working Variables ... double recoveryZoneStep; // Used Recovery Zone Step int lastRecoveryLevel; // Last Recovery Level datetime lastRecoveryTime; // Last Recovery Time double lastVolume; // Last Recovery Volume ulong tickets[]; // Recovery Trades Tickets // // Constructor ... XProtectedPosition() { Clean(); } // // Tools ... // // Initialized ... bool Init( double mRecoveryZoneStep, XPosition &mPosition // ) { // bool result = false; // // Validate Params ... result = // mPosition.IsValid() && mRecoveryZoneStep > 0 // ; if (!result) { return result; } // Clean(); // sl = mPosition.sl; tp = mPosition.tp; type = mPosition.type; entry = mPosition.entry; ticket = mPosition.ticket; symbol = mPosition.symbol; period = mPosition.period; volume = mPosition.volume; openAt = mPosition.openAt; provider = mPosition.provider; recoveryZoneStep = mRecoveryZoneStep; // result = IsValid(); // return result; } // // Cleanup ... void Clean() { // ticket = 0; symbol = NULL; period = NULL; provider = NULL; volume = 0; entry = 0; sl = 0; tp = 0; openAt = NULL; lastRecoveryLevel = 0; lastVolume = 0; lastRecoveryTime = NULL; Clean(tickets); } // // Validate ... bool IsValid() { // bool result = false; // result = // ticket > 0 && volume > 0 && entry > 0 && tp > 0 && sl > 0 && IsSpecifiedValid(symbol) && IsSpecifiedValid(provider) && IsSpecifiedValid(period) // ; // return result; } // // Check Protected Type ... bool IsLong() { // bool result = false; // result = IsValid(); if (!result) { return result; } // result = IsLong(this.type); // return result; } // // Retrieve Tick Based on Protected ... bool GetTick( MqlTick &tick // ) { // bool result = false; // // Validate ... result = IsValid(); if (!result) { return result; } // result = GetTick( this.symbol, tick // ); // return result; } // // Retrieve Current Exit Price based on Protected ... double GetExit() { // double result = GetExit( this.symbol, this.type // ); // return result; } // // Retrieve Current Entry Price based on Protected ... double GetEntry() { // double result = GetEntry( this.symbol, this.type // ); // return result; } // // Calculate Recovery Step Price ... double GetRecoveryAreaPrice() { // double result = 0; // // Validate ... if (!IsValid()) { return result; } // double stepPrice = PointToPrice( recoveryZoneStep, symbol // ); // bool isLong = IsLong(type); // result = isLong ? entry - stepPrice : entry + stepPrice; // return result; } // // Check Protected Has Recovery or not ... // it it's true ... fill signal by proper info ... bool GetRecoverySignal( XSignal &signal // Result ... ) { // bool result = false; // signal.Clean(); // // Validate ... result = IsValid(); if (!result) { return result; } // // Check Position is Reoverable or not ... bool isLong = IsLong(); double recoveryPrice = GetRecoveryAreaPrice(); // int currRecoveryLevel = lastRecoveryLevel + 1; // double mExit = GetExit(); double mEntry = GetEntry(); double recoveryDiff = MathAbs(mEntry - recoveryPrice); // // First Check Opposite Direction ... bool canOppositDirectionRecover = // // Check State for Opposit Recovery ... lastRecoveryLevel % 2 == 0 && // (isLong ? mExit < recoveryPrice : mExit > recoveryPrice) // ; // // Check Same Direction Recovery ... bool canSameDirectionRecover = // // Since for Same Direction Recover we have to at leaset 1 Opposite Recovery ... lastRecoveryLevel % 2 == 1 && // (isLong ? mEntry >= entry : mEntry <= entry) // ; // // Check Protected Can Recoverable or not ... result = // lastRecoveryLevel < 5 && (canSameDirectionRecover || canOppositDirectionRecover) // ; if (!result) { return result; } // // Check Last Recovery Time ... int age = !IsValid(this.lastRecoveryTime) ? -1 : iBarShift( this.symbol, this.period, this.lastRecoveryTime, false // ); result = age == -1 ? true : age >= 5; if (!result) { return result; } // // Now we have to Prepare Signal based on Recovery Mode ... // // Recovery Position Type ... ENUM_POSITION_TYPE mType = canSameDirectionRecover ? this.type : GetOpposit(this.type); // bool isMLong = IsLong(mType); // // Recovery Position Entry Price ... mEntry = GetEntry( this.symbol, mType // ); // // SL and TP ... double mTP = canSameDirectionRecover ? this.tp : this.sl; mTP = isMLong ? mTP + recoveryDiff : mTP - recoveryDiff; // double mSL = canSameDirectionRecover ? this.sl : this.tp; mSL = isMLong ? mSL - recoveryDiff : mSL + recoveryDiff; // // Volume ... // For Volume Calculating we have to use Recovery Level ... // double mVolume = // lastVolume == 0 // ? volume * 1 // : lastVolume * 1; double mVolume = canSameDirectionRecover ? volume : volume * 2; // result = signal.Prepare( this.symbol, this.provider, this.period, mType, X_ORDER_MODE_MARKET, mEntry, mVolume, mSL, mTP // ); // return result; } }; // // a Position Protector Class ... class XSCPositionProtector : public XSCBaseAlert { // // Public ... public: // // Props ... // bool enable; // // Protected Positions ... XProtectedPosition protecteds[]; // // Trader Class Instance ... XSCTrade *mTrader; // // Constructor ... void XSCPositionProtector() { // Clean(protecteds); // SetRecoveryStepDivider(5); SetRecoveryStepPeriod(PERIOD_D1); } // // Deconstructor ... void ~XSCPositionProtector() { delete mTrader; } // // Setter(s) / Getter(s) ... // void SetRecoveryStepPeriod(ENUM_TIMEFRAMES value) { this.recoveryStepPeriod = value; } // ENUM_TIMEFRAMES GetRecoveryStepPeriod() { return this.recoveryStepPeriod; } // void SetRecoveryStepDivider(int value) { // if (value <= 3) { value = 3; } // this.recoveryStepDivider = value; } // int GetRecoveryStepDivider() { return this.recoveryStepDivider; } // void SetMinRequiredProfitPerTrade(double value) { // if (value < 0) { value = 0; } // this.minRequiredProfitPerTrade = value; } // double GetMinRequiredProfitPerTrade() { return this.minRequiredProfitPerTrade; } // void SetMinRequiredProfitPerTradeVolumeFactor(double value) { // if (value < 0) { value = 0; } // this.minRequiredProfitPerTradeVolumeFactor = value; } // double GetMinRequiredProfitPerTradeVolumeFactor() { return this.minRequiredProfitPerTradeVolumeFactor; } // // Tools ... // // Initial Class ... bool Init( bool mEnable, XSCTrade *trader, double mMinRequiredProfitPerTrade = 0.5, double mMinRequiredProfitPerTradeVolumeFactor = 0.01 // ) { // bool result = false; // this.enable = mEnable; // result = trader != NULL; if (!result) { return result; } // this.mTrader = trader; // SetMinRequiredProfitPerTrade(mMinRequiredProfitPerTrade); SetMinRequiredProfitPerTradeVolumeFactor(mMinRequiredProfitPerTradeVolumeFactor); // return result; } // // Handle Trades for Processing ... // this used for Automating Recovery Positions ... // we have to call this in our EA Class ... void HandleState(const XOnTradeHandlerState &state) { // if (!enable) { return; } // if ( !state.hasNewDeal && !state.hasNewOrder && !state.hasNewPosition && !state.hasNewHistoryOrder) { return; } // // Here we Can Handle new Positions ... // To Protect ... // // Detect New Positions ... if (!state.hasNewPosition) { // // This means a Position SL or TP or Close ... return; } // ulong lastPositionTicket = mTrader.GetLastOpenPositionTicket(); if (lastPositionTicket <= 0) { return; } // // Retrieve XPosition ... XPosition position; bool isRetrieved = mTrader.GetPosition( lastPositionTicket, position // ); if (!isRetrieved) { return; } // // Now we Have Position Struct ... // - First Check Position is new Regular Position or // it is a Support Position; // - then We have to Decide what to Do ... // // Check Position is New Or Not ... ulong parentTicket = ExtractEQMSupportedTicket(position.comment); if (!NotEmptyZero(parentTicket)) { // AddPosition(position); } else { // UpdatePosition( parentTicket, position // ); } } // // Here we Process all Protected Positions ... // for Handling Zone Recovery ... void Process() { // if (!enable) { return; } // int protectedsCount = ArraySize(protecteds); if (IsValidSize(protectedsCount)) { // // Loop through Exists ... for (int i = 0; i < protectedsCount; i++) { // // Do Protection ... HandlePositionsProtecting(protecteds[i]); } } // string comment = XEQMSupportToken + " Hege ..."; // // Handle Hedging All Positions ... bool allowHedge = AllowHedge(); if (allowHedge) { // XPosition positions[]; int positionsCount = mTrader.GetPositions(positions); if (!IsValidSize(positions) || positionsCount <= 1) { // // Here We Can Close if Still Positions is EQM Support ... bool canClosePosition = // positionsCount == 1 && positions[0].provider == XEQMSupportToken && positions[0].profit > (-1 * positions[0].swap) + (minRequiredProfitPerTrade * (positions[0].volume / minRequiredProfitPerTradeVolumeFactor)) // ; if (canClosePosition) { // bool isClosed = mTrader.Close( positions[0].ticket, comment // ); } return; } // double profit = SpecifiedCalculatePositionsProfit(positions); double requiredProfit = SpecifiedCalculateRequiredProfitForHedge( positions, minRequiredProfitPerTrade, minRequiredProfitPerTradeVolumeFactor // ); bool canHedge = SpecifiedIsPositionsReadyForHedge( positions, minRequiredProfitPerTrade, minRequiredProfitPerTradeVolumeFactor // ); if (!canHedge) { // ProtectMargin(); return; } // ENUM_X_SIGNAL_EXECUTION_RESULT states[]; int closed = mTrader.Close( positions, comment // ); if (IsValidSize(closed)) { Clean(protecteds); } } } // // Sync Positions ... // // Remove Support ... bool Remove(ulong ticket) { // bool result = false; // if (!enable) { return result; } // int protectedIDX = FindProtectedIndex(ticket); result = protectedIDX >= 0; if (!result) { return result; } // // Close all Protected Support Positions if Exists ... string comment = "EQM Close In Profit ..."; // int ticketsCount = ArraySize(protecteds[protectedIDX].tickets); if (IsValidSize(ticketsCount)) { // for (int i = 0; i < ticketsCount; i++) { // XPosition iPosition; bool isRetrieved = mTrader.GetPosition( protecteds[protectedIDX].tickets[i], iPosition // ); if (!isRetrieved) { continue; } // bool isClosed = mTrader.Close( iPosition.ticket, comment // ); } } // result = ArrayRemove( protecteds, protectedIDX, 1 // ); // // TODO: Update Collection here ... // return result; } // // Protected ... protected: // // Props ... double minRequiredProfitPerTrade; // Minimum Required Profit Per Trade for Hedging double minRequiredProfitPerTradeVolumeFactor; // Calculate Required Profit Based on Volume Factor for Hedging int recoveryStepDivider; // Recovery Step Divider ENUM_TIMEFRAMES recoveryStepPeriod; // Recovery Step Period // // Tools ... // // Here we recieve a new Position and // Prepare it for Protecting ... void AddPosition(XPosition &position) { // // - Recieve Last Day Up and Low Boundary // - Divided to 5 // - Find Recovery Zone Step // - Calculate Recoery Zone for this Specific Position // - Prepare it's Structure // - Add it to Collection // // Validate Params ... if (!position.IsValid()) { return; } // double recoveryStep = CalculateRecoveryZoneStep(position); if (recoveryStep <= 0) { return; } // XProtectedPosition item; bool isInited = item.Init( recoveryStep, position // ); if (!isInited) { return; } // AddRef( item, protecteds // ); // // TODO: Also Here We Can Collect Data ... } // // Here we recieve a Support Position and // need to Update Parent Protected Position's // info ... void UpdatePosition( ulong parentTicket, // Parent Position Ticket XPosition &position // Support Position ) { // Print("Update Protected Position: " + ToString(parentTicket)); } // // Calculate Position Recovery one Step ... double CalculateRecoveryZoneStep(XPosition &position) { // double result = 0; // // Validate Position ... if (!position.IsValid()) { return result; } // // Validate Position must have TP and SL ... if (position.tp == 0 || position.sl == 0) { return result; } // // Calculate Position Risk at Point ... double point = GetPoints(position.symbol); int digits = GetDigits(position.symbol); double riskPoint = NormalizeDouble(MathAbs(position.entry - position.sl), digits) / point; // // Retrieve Bar for finding Recovery Step ... XOHCL rBar; bool isInited = rBar.Init( position.symbol, recoveryStepPeriod, 1 // ); if (!isInited) { return result; } // // Calculate Recovery Step ... double riskPointRecoveryStep = riskPoint / 2; double recoveryStep = (NormalizeDouble(MathAbs(rBar.high - rBar.low), digits) / point) / recoveryStepDivider; result = MathMin(riskPointRecoveryStep, recoveryStep); // return result; } // // Here we Implement all Protecting Senarios here ... void HandlePositionsProtecting(XProtectedPosition &item) { // // Check protected Validation ... if (!item.IsValid()) { return; } // // Check Has Recovery Signal Or Not ... XSignal signal; bool hasRecovery = item.GetRecoverySignal(signal); if (!hasRecovery) { return; } // signal.provider = XEQMSupportToken; string comment = GenerateEQMSupportTag(item.ticket); signal.comment = comment; // // Remove Support Signal TP and SL ... signal.tp = 0; signal.sl = 0; // ENUM_X_SIGNAL_EXECUTION_RESULT state; bool isExecuted = mTrader.ExecuteSignal( signal, state, ORDER_TIME_GTC, NULL, false // Ignore Policies ... ); if (isExecuted) { // // Do What we Want ... item.lastRecoveryLevel++; item.lastVolume = signal.volume; item.lastRecoveryTime = TimeCurrent(); Add( signal.positionId, item.tickets // ); // int protectedIDX = FindProtectedIndex(item.ticket); if (protectedIDX < 0) { return; } // protecteds[protectedIDX] = item; // // TODO: Update Collect here ... } } // // Check Hedging is Enabled ... bool AllowHedge() { // bool result = // minRequiredProfitPerTrade > 0 && minRequiredProfitPerTradeVolumeFactor > 0 // ; // return result; } // // Protecting Margin by Free Coveraged Positions ... void ProtectMargin() { // double freeMargin = mTrader.mAccount.GetFreeMargin(); // double balance = mTrader.mAccount.GetBalance(); double equity = mTrader.mAccount.GetEquity(); // double selectedBalance = MathMin(balance, equity); // // Retrieve All Positions ... XPosition positions[]; int positionsCount = mTrader.GetPositions(positions); if (!IsValidSize(positionsCount)) { return; } // bool canForceHedging = positionsCount >= 25 || freeMargin <= selectedBalance / 2; if (!canForceHedging) { return; } // // First Check Hedging By Half of Required Profit ... double profit = SpecifiedCalculatePositionsProfit(positions); double requiredProfit = SpecifiedCalculateRequiredProfitForHedge( positions, minRequiredProfitPerTrade, minRequiredProfitPerTradeVolumeFactor // ); bool canHedge = // profit > requiredProfit / 2 // ; if (canHedge) { // string comment = XEQMSupportToken + " Hege ..."; int closed = mTrader.Close( positions, comment // ); // return; } // // TODO: Enable this if we want ... return; // // Retrieve In Drawdown Positions ... XPosition inDPositions[]; int inDPositionsCount = ExtractInDrawdownPositions( positions, inDPositions /// ); if (!IsValidSize(inDPositionsCount)) { return; } // // Retrieve In Profit Positions ... XPosition inPPositions[]; int inPPositionsCount = ExtractInProfitPositions( positions, inPPositions // ); if (!IsValidSize(inPPositionsCount)) { return; } // // if we can't Hedge all Positions ... // now we are looking to pair Positions for hedge ... // - Select Max In Drawdown Position; // - Looking for Coverage it inside In Profit Positions; // - Clease All of them ... int maxInDIDX = FindMaxDrawdownIndex(inDPositions); if (maxInDIDX < 0) { return; } // string comment = XEQMSupportToken + " Force Hege ..."; // XPosition maxInDCoverages[]; int maxInDCoveragesCount = FindCoverageDrawdownPosition( inDPositions[maxInDIDX], inPPositions, maxInDCoverages, minRequiredProfitPerTrade, minRequiredProfitPerTradeVolumeFactor // ); if (!IsValidSize(maxInDCoveragesCount)) { return; } // bool isClosedMaxInD = mTrader.Close( inDPositions[maxInDIDX].ticket, comment // ); if (isClosedMaxInD) { // int closed = mTrader.Close( maxInDCoverages, comment // ); } } // // Protected Collection Management ... // int CountProtecteds() { return ArraySize(protecteds); } // int FindProtectedIndex(XProtectedPosition &item) { // int result = -1; // if (!item.IsValid()) { return result; } // result = FindProtectedIndex(item.ticket); // return result; } // int FindProtectedIndex(ulong ticket) { // int result = -1; // if (!NotEmptyZero(ticket)) { return result; } // int protectedsCount = CountProtecteds(); if (!IsValidSize(protectedsCount)) { return result; } // for (int i = 0; i < protectedsCount; i++) { // if (protecteds[i].ticket == ticket) { // result = i; break; } } // return result; } }; // // Class Definition ... class XSCX121EA : public XSCBaseEA { // // Public ... public: // // Props ... // // Constructors ... XSCX121EA( // // XTrade Class Requirements ... int slippage, // Specify Slippage ulong magicNumber, // Specify Magic Number // // Time Management ... // TODO: ... // // XSCTrade 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 // // Custom Event Handler ... TOnSignal onSignalHandler = NULL // On Signal Event Handler ) : XSCBaseEA(slippage, magicNumber, onStopLossTriggered, onTakeProfitTriggered, onDealsChangedHandler, onOrdersChangedHandler, onPositionsChangedHandler, onTradeStateChangedHandler // ) { // mProtector = new XSCPositionProtector(); mProtector.Init(false, mTrader); } // // Deconstructor ... ~XSCX121EA() { delete mProtector; } // // Properties Getter(s) / Setter(s) ... // // Add X5 Specified Signal Event Handler ... void AddOnSignalEventHandler(TX121OnSignal listener) { // Add( listener, mX121OnSignalEventHandlers // ); } // // Add Specified X5 Provider ... bool AddProvider(X121ProviderDescriptor &descriptor) { // bool result = false; // // Validate Inputs ... result = descriptor.Init(); if (!result) { return result; } // AddRef( descriptor, mDescriptors // ); // return result; } // // Overrides ... // // Customize Token ... string GetToken() override { return GetSpecificToken(this); } // string GetTag() override { return this.GetToken(); } // void Draw() override { // int descriptorsCount = CountDescriptors(); if (descriptorsCount <= 0) { return; } // for (int i = 0; i < descriptorsCount; i++) { // X121ProviderDescriptor iDescriptor = mDescriptors[i]; // iDescriptor.provider.Draw(); } } // // Check Provider for any Guards ... bool CheckForGuard(XGuard &guards[]) override { // bool result = false; // Clean(guards); // int descriptorsCount = CountDescriptors(); result = descriptorsCount > 0; if (!result) { return result; } // // Loop Through Descriptors for Collecting Guards ... for (int i = 0; i < descriptorsCount; i++) { // X121ProviderDescriptor iDescriptor = mDescriptors[i]; // XGuard iGuards[]; int iGuardsCount = iDescriptor.HasGuard(iGuards); if (!IsValidSize(iGuardsCount)) { continue; } // Copy( iGuards, guards, false // ); } // result = ArraySize(guards) > 0; // return result; } // // Request Provider to Collect all // Potentially Signals and then // filters theme here and passed them // for Executing ... int RequestForSignal( XSignal &signals[] // Holds Signals ... ) override { // bool result = 0; // Clean(signals); // int descriptorsCount = CountDescriptors(); if (descriptorsCount <= 0) { return result; } // for (int i = 0; i < descriptorsCount; i++) { // X121ProviderDescriptor iDescriptor = mDescriptors[i]; // if (iDescriptor.provider.CanIgnoreProcess()) { continue; } // int iSignalsCount = iDescriptor.HasSignal(); if (IsValidSize(iSignalsCount)) { // // Here we Can double check Signals by Conditions // for Score Base Filtering ... iDescriptor.provider.SetWaitsUntilNewBar(true); // Copy( iDescriptor.signals, signals, false // ); // NotifyX121OnSignalEventHandlers(iDescriptor); } } // result = ArraySize(signals); // return result; } // // Request for Support Signals using Guard ... bool RequestForSupport( XSignal &support, // Holds Support Signal, if Provided string provider, // Positions Provided by Specific Provider string symbol, // Trading Symbol ENUM_X_POSITION_TYPE type, // Trading Type ENUM_TIMEFRAMES period // Specified Time Frame ) override { // // TODO: Implement this ... // // Support Senario ... // Check Support Position Exists or not ... // Check Positions for Support based on Types ... // Update Untriggered Positions ... return false; } // // Here we Manage Signals for Executing ... // - Check Policies based on their Signaller ... // - Check Same Time Open Positions ... // - Check Signal Age for new Trade ... // and etc ... int HandleSignalManagement(XSignal &signals[]) override { // int result = 0; // int signalsCount = ArraySize(signals); if (!IsValidSize(signalsCount)) { return result; } // XSignal tmpSignals[]; Copy( signals, tmpSignals // ); // Clean(signals); // for (int i = 0; i < signalsCount; i++) { // XSignal iSignal = tmpSignals[i]; // // Find Provider Descriptor Which Issued this Signal ... int iProviderIDX = FindDescriptorIndex( iSignal.symbol, iSignal.period); if (!IsValidIndex(iProviderIDX)) { // string message = "Couldn't find Signal Descriptor ..."; Log(message); // continue; } // // Now we Have to Find Signaller Which Issued this Signal ... int iSignallerIDX = mDescriptors[iProviderIDX] .FindSignallerIndex(iSignal.provider); if (!IsValidIndex(iSignallerIDX)) { // string message = "Couldn't find Signal Provider ..."; Log(message); // continue; } // // Check Position Type is Enabled or not ... bool isLong = IsLong(iSignal.type); bool isAllowedType = isLong ? mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowLong : mDescriptors[iProviderIDX].signallers[iSignallerIDX].allowShort; if (!isAllowedType) { // string message = "ignore Signal due Type Policy ..."; Log(message); // continue; } // // Retrieve Long and Short Signals of same Provider ... XPosition longs[]; XPosition shorts[]; mTrader.GetPositions( longs, shorts, iSignal.symbol, iSignal.provider, iSignal.period // ); int longsCount = ArraySize(longs); int shortsCount = ArraySize(shorts); // // Check Max Number of Positions ... bool isMaxNumberOfPositionsPassed = isLong ? mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong <= 0 ? true : longsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedLong : mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort <= 0 ? true : shortsCount < mDescriptors[iProviderIDX].signallers[iSignallerIDX].maxAllowedShort; if (!isMaxNumberOfPositionsPassed) { // string message = "ignore Signal due Max Allowed Positions Reached ..."; Log(message); // continue; } // // Check Delay Between Two Same Type Signals ... // Check Open Next Behaviour ... if (longsCount > 0 || shortsCount > 0) { // // Check if Signaller Configured for Check Delay between Same type Signals ... if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals > 0) { // bool isDelayPassed = true; // if (longsCount > 0) { // XPosition youngestLong; int youngestLongAge = GetYoungest( youngestLong, longs // ); // if (isLong) { isDelayPassed = youngestLongAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals; } } // if (shortsCount > 0) { // XPosition youngestShort; int youngetsShortAge = GetYoungest( youngestShort, shorts // ); // if (!isLong) { isDelayPassed = youngetsShortAge >= mDescriptors[iProviderIDX].signallers[iSignallerIDX].delayBetweenTwoSameTypeSignals; } } // if (!isDelayPassed) { // string message = "ignore Signal due Delay Between Same Type Policy ..."; Log(message); // continue; } } // // Check Next Position ... if (mDescriptors[iProviderIDX].signallers[iSignallerIDX].openNextPositionOnProfit) { // bool isNextPassed = true; // if (isLong && longsCount > 0) { // double profit = SpecifiedCalculatePositionsProfit(longs); isNextPassed = profit > 0; } // if (!isLong && shortsCount > 0) { // double profit = SpecifiedCalculatePositionsProfit(shorts); isNextPassed = profit > 0; } // // Check Ignore for Opposit Signals ... if (!isNextPassed && ((isLong && shortsCount > longsCount) || (!isLong && longsCount > shortsCount))) { isNextPassed = mDescriptors[iProviderIDX].signallers[iSignallerIDX].ignoreProfitForOppositeSignals; } // if (!isNextPassed) { // string message = "ignore Signal due Next Must be In Profit Policy ..."; Log(message); // continue; } } } // // Finally Add Filtered Signals into Result ... AddRef( iSignal, signals // ); } // result = ArraySize(signals); // return result; } // // Handle State Management .... // here we can manage current state ... // - Check for Long Positions for each Signaller to Close ... // - Handle Hedging Signaller's Positions if it's enabled ... // - Force Closing Position when Specified Time reached ... // - Check Start and End time or Trading Dates ... // - handle Trailing or Risk free Signals based on several conditions ... // and etc ... bool HandleStateManagement(XSignal &signals[]) override { // const bool result = false; // // Here we Implement Account Protector ... HandleAccountProtect(); // // Check Descriptor ... int descriptorsCount = CountDescriptors(); if (!IsValidSize(descriptorsCount)) { return result; } // // Loop Through Descriptors ... for (int i = 0; i < descriptorsCount; i++) { // // Check Signallers ... int signallersCount = mDescriptors[i].CountSignallers(); if (!IsValidSize(signallersCount)) { continue; } // // Loop Through Signallers ... for (int j = 0; j < signallersCount; j++) { // // Retrieve Specified Signaller's Position ... XPosition positions[]; int positionsCount = mTrader.GetPositions( positions, mDescriptors[i].symbol, mDescriptors[i].signallers[j].GetName(), mDescriptors[i].period); if (!IsValidSize(positionsCount)) { continue; } // // Check Signaller Enable Hedge or not and Handle it if enabled ... if (positionsCount > 1 && mDescriptors[i].signallers[j].AllowHedge()) { // // Check Profits Enough for Hedge or not ... double profit = SpecifiedCalculatePositionsProfit(positions); bool isReadyForHedge = positionsCount > 1 && SpecifiedIsPositionsReadyForHedge( positions, mDescriptors[i].signallers[j].minRequiredProfitPerTrade, mDescriptors[i].signallers[j].minRequiredProfitPerTradeVolumeFactor // ); if (isReadyForHedge) { // string comment = "Close due Hedge ..."; int closeds = mTrader.Close( positions, comment // ); // if (closeds > 0) { // string message = "Hedge (" + ToString(closeds) + ") Positions In Profit: " + ToString(profit); // Alert(message); // break; } } } // // Check Closing Long Age Position's Enabled or not ... if (mDescriptors[i].signallers[j].maxAllowedOpenPositionAge > 0) { // // Handle Close Long Time Trades ... // // Find Oldest ... XPosition oldest; int oldestAge = GetOldest( oldest, positions // ); if (oldestAge >= mDescriptors[i].signallers[j].maxAllowedOpenPositionAge) { // string comment = "Close due Long Age ..."; bool isClosed = mTrader.Close( oldest.ticket, comment // ); // if (isClosed) { // string message = "Position (" + ToString(oldest.ticket) + ") Closed due Long Age Policy ..."; Alert(message); } } } } } // // if Returns true, Signal Execution failed ... return result; } // void OnStopLossTriggered(const XDeal &deal) override { // // Remove Position Protecting ... mProtector.Remove(deal.positionId); } // void OnTakeProfitTriggered(const XDeal &deal) override { // // Remove Position Protecting ... mProtector.Remove(deal.positionId); } // void OnTradeStateChangedHandler( const XOnTradeHandlerState &state // ) override { // // Calling Protector to Handle State ... mProtector.HandleState(state); } // void HandleAccountProtect() { // // Position Protector Calls to Process State ... mProtector.Process(); } // // Tools ... // // Protected ... protected: // // Tools ... // void NotifyX121OnSignalEventHandlers(X121ProviderDescriptor &descriptor) { // int listenersCount = ArraySize(mX121OnSignalEventHandlers); if (listenersCount <= 0) { return; } // for (int i = 0; i < listenersCount; i++) { // TX121OnSignal iListener = mX121OnSignalEventHandlers[i]; // iListener(descriptor); } } // // Protect Specified Position ... void ProtectPosition(XPosition &position) { // // Here i Can Protect Position ... // This Protect Include One Position in Market Mode ... // If Provided ... // the Positions Selection must passed Some conditions ... // string message = "For (" + position.symbol + ") Max In Drawdown Position is: (" + ToString(position.ticket) + "), by Profit: " + ToString(position.profit); // mAlert.Alert(message); } // // Private ... private: // // Props ... // // Collection of Signal Event Listeners ... TX121OnSignal mX121OnSignalEventHandlers[]; // // a Collection of X5 Provider Descriptors ... X121ProviderDescriptor mDescriptors[]; // int CountDescriptors() { return ArraySize(mDescriptors); } // // Find Specifc Descriptor ... int FindDescriptorIndex( string symbol, ENUM_TIMEFRAMES period // ) { // int result = -1; // if (!IsValid(symbol) || !IsValid(period)) { return result; } // int descriptorsCount = CountDescriptors(); if (!IsValidSize(descriptorsCount)) { return result; } // for (int i = 0; i < descriptorsCount; i++) { // X121ProviderDescriptor iDescriptor = mDescriptors[i]; // bool isPassed = // iDescriptor.symbol == symbol && iDescriptor.period == period // ; if (isPassed) { // result = i; break; } } // return result; } template int FindDescriptorIndex( T &item // ) { return FindDescriptorIndex( item.symbol, item.period // ); } // // Trails Holding ... // XTrail mSLTrails[]; XTrail mTPTrails[]; // int CountSLTrails() { return ArraySize(mSLTrails); } int CountTPTrails() { return ArraySize(mTPTrails); } // bool RemoveTrail(ulong ticket) { // bool isSLRemoved = RemoveSLTrail(ticket); bool isTPRemoved = RemoveTPTrail(ticket); // bool result = isSLRemoved || isTPRemoved; // return result; } bool RemoveSLTrail(ulong ticket) { // bool result = false; // int idx = FindSLTrailIndex(ticket); result = idx >= 0; if (!result) { return result; } // result = ArrayRemove( mSLTrails, idx, 1 // ); // return result; } bool RemoveTPTrail(ulong ticket) { // bool result = false; // int idx = FindTPTrailIndex(ticket); result = idx >= 0; if (!result) { return result; } // result = ArrayRemove( mTPTrails, idx, 1 // ); // return result; } // int FindSLTrailIndex(ulong ticket) { // int result = -1; // int trailsCount = CountSLTrails(); if (ticket <= 0 || trailsCount <= 0) { return result; } // for (int i = 0; i < trailsCount; i++) { // XTrail iTrail = mSLTrails[i]; // if (iTrail.ticket == ticket) { // result = i; break; } } // return result; } int FindTPTrailIndex(ulong ticket) { // int result = -1; // int trailsCount = CountTPTrails(); if (ticket <= 0 || trailsCount <= 0) { return result; } // for (int i = 0; i < trailsCount; i++) { // XTrail iTrail = mTPTrails[i]; // if (iTrail.ticket == ticket) { // result = i; break; } } // return result; } // bool GetSLTrail( ulong ticket, XTrail &trail // ) { // bool result = false; // int idx = FindSLTrailIndex(ticket); result = idx >= 0; if (!result) { return result; } // trail = mSLTrails[idx]; // return result; } bool GetTPTrail( ulong ticket, XTrail &trail // ) { // bool result = false; // int idx = FindTPTrailIndex(ticket); result = idx >= 0; if (!result) { return result; } // trail = mTPTrails[idx]; // return result; } // void AddSLTrail( XTrail &trail // ) { // if (!trail.IsValid()) { return; } // AddRef( trail, mSLTrails // ); } void AddTPTrail( XTrail &trail // ) { // if (!trail.IsValid()) { return; } // AddRef( trail, mTPTrails // ); } // // Position Protector ... XSCPositionProtector *mProtector; // }; // // Tools ... //