/////////////////////////////////////////////////////// // // SaherElm IT Center MQL5 Expert Advisor // ---------------------------------------- // Name: XEA TEST // Description: this is an expert controller // advisors which provides a mechanism for use them ... // // // Maintainer: // ------------ // Hadi Khazaee Asl (hadi_khazaee_asl@yahoo.com) // ////////////////////////////////////////////////////// // // Global Properties ... #property copyright "Copyright 2023, SaherElm IT Center" #property link "https://saherelm.ir" #property version "1.00" #property description "SaherElm XEA EA" #property strict // // START Definitions ... // // string mSupportToken = "SP"; string mProviderToken = "PRV"; string mRiskFreeToken = "RF"; // enum ENUM_X_SIGNAL_PROVIDERS { NONE = 0, XTRND = 1, XICHI = 2, XTest = 10, }; // // END Definitions ... // // // START Inputs ... // // // EA ... input group "Common"; input bool xEAEnable = true; // Enable EA input int xEASlippage = 10; // Slippgae input ulong xEAMagicNumber = 1604056; // Magic Number // // Signals ... input group "Signals"; input bool xEAEnableLongs = true; // Enable Long Trades input bool xEAEnableShorts = true; // Enable Short Trades // // Alerts ... input group "Alerts"; input bool xEAEnableAlerts = true; // Enable Events Alert input bool xEAEnableHourlyAlerts = false; // Send Alert on each new Hour input bool xEAEnableDailyAlerts = false; // Send Alert on each new Day input bool xEAEnableWeeklyAlerts = false; // Send Alert on each new Week input bool xEAEnableMonthlyAlerts = false; // Send Alert on each new Month // // END Inputs ... // // // START Including Providers ... // // // Classes ... #include "../../Classes/x-saherelm.xtrade.class.mq5" #include "../../Classes/x-saherelm.xaccount.class.mq5" #include "../../Classes/x-saherelm.xdatacollector.class.mq5" // // Test Provider Library ... #include "../../Providers/Test/x121.test.provider.mq5" // // END Including Providers ... // // // START Global Definitions: Variables, Properties and etc ... // // bool isXEANewHour; bool isXEANewDay; bool isXEANewWeek; bool isXEANewMonth; // // Determines EA is Running On Test Mode or not ... bool xEAIsTestMode = false; // // Check prevent Double Signalling on Same Candle ... bool xEAWaitUntilNextCandle = false; // XSCTrade *xEATrade; XSCAccount *xEAAccount; XSCDataCollector *xEACollector; // // END Global Definitions: Variables, Properties and etc ... // // // START Event Handlers ... // // // Initialization ... int OnInit() { // logTag = "XEA"; drawPrefix = logTag; alertPrefix = logTag; // // Reset Timings ... ResetTiming(); // // Validate Inputs ... if (!ValidateInputs()) { return INIT_PARAMETERS_INCORRECT; } // // Init Provider ... if (!XTestPRVDOnInit()) { return INIT_FAILED; } // xEAIsTestMode = IsRunningOnTestMode(); // xEACollector = new XSCDataCollector("XTestEA"); xEACollector.CleanStore(); // // Initialize Trade Class ... xEATrade = new XSCTrade( xEASlippage, xEAMagicNumber); // // Attach Event Handlers ... xEATrade.AddOnStopLossEventHandler(OnStopLossTriggered); xEATrade.AddOnTakeProfitEventHandler(OnTakeProfitTriggered); xEATrade.AddOnDealsChangedEventHandler(OnDealsChangedHandler); xEATrade.AddOnOrdersChangedEventHandler(OnOrdersChangedHandler); // // Initialize Account Class ... xEAAccount = new XSCAccount(); // // Init Succeed ... return INIT_SUCCEEDED; } // // DeInitialization ... void OnDeinit(const int reason) { // // REASON_PROGRAM 0 The EA has stopped working calling the ExpertRemove() function // REASON_REMOVE 1 Program removed from a chart // REASON_RECOMPILE 2 Program recompiled // REASON_CHARTCHANGE 3 A symbol or a chart period is changed // REASON_CHARTCLOSE 4 Chart closed // REASON_PARAMETERS 5 Inputs changed by a user // REASON_ACCOUNT 6 Another account has been activated or reconnection to the trade server has occurred due to changes in the account settings // REASON_TEMPLATE 7 Another chart template applied // REASON_INITFAILED 8 The OnInit() handler returned a non-zero value // REASON_CLOSE 9 Terminal closed // // Here we can handle De Initialization Reasons ... delete xEATrade; delete xEAAccount; delete xEACollector; // // De Initialize XEA Providers ... XTestPRVDOnDeInit(); } // // On Tick Handler ... void OnTick() { // // Handle Monthly Alert ... bool isFirstMonth = xMonth == -1; isXEANewMonth = IsNewMonth(); bool canMonthlyAlert = isXEANewMonth && xEAEnableAlerts && xEAEnableMonthlyAlerts; if (canMonthlyAlert) { // string message = "New Month ..."; // SendAlert(message); } // // Handle Weekly Alert ... bool isFirstWeek = xDayOfWeek == -1; isXEANewWeek = IsNewWeek(); bool canWeeklyAlert = isXEANewWeek && xEAEnableAlerts && xEAEnableWeeklyAlerts; if (canWeeklyAlert) { // string message = "New Week ..."; // SendAlert(message); } // // Handle Daily Alert ... bool isFirstDay = xDay == -1; isXEANewDay = IsNewDay(); bool canDailyAlert = isXEANewDay && xEAEnableAlerts && xEAEnableDailyAlerts; if (canDailyAlert) { // string message = "New Day ..."; // SendAlert(message); } // // Handle Hourly Alert ... bool isFirstHour = xHour == -1; isXEANewHour = IsNewHour(); bool canHourlyAlert = isXEANewHour && xEAEnableAlerts && xEAEnableHourlyAlerts; if (canHourlyAlert) { // string message = "New Hour ..."; // SendAlert(message); } // // Prevent Going Forward if user Disable it ... if (xEAEnable) { // // Call All Providers OnTick Method ... XTestPRVDOnTick(); // // Do all Guard Mechanism ... HandleGuard(); // // Check Can Ignore Tick or not .. bool canIgnoreTick = xEAIsTestMode ? !IsNewCandle() : !IsNewCandle() && xEAWaitUntilNextCandle; if (canIgnoreTick) { return; } // xEAWaitUntilNextCandle = false; // XSignal signal; bool hasSignal = XTestPRVDProcessSignals(signal); if (hasSignal) { // xEAWaitUntilNextCandle = true; OnSignalRecieved(signal); } } } // // Handle Trade Events ... void OnTrade() { // // Process Trades ... xEATrade.HandleOnTrade(); } // // END Event Handlers ... // // // START Functions ... // // // Validate Input Args for Initialization ... bool ValidateInputs() { // // TODO: Fix this ... bool result = true; // // Validate Args ... result = // xEASlippage > 0 && xEAMagicNumber > 0 // ; // // Logging State ... string message = "Validation of (" + logTag + ") Inputs " + (result ? "Succeeded" : "Failed") + " ..."; LogMessage(message); // return result; } // // Handle Signal Recieved Event ... void OnSignalRecieved( XSignal &signal // Recieved Signal ... ) { // // Ignore Disabled Signals ... if ((signal.type == POSITION_TYPE_BUY && !xEAEnableLongs) || (signal.type == POSITION_TYPE_SELL && !xEAEnableShorts)) { return; } // if (StringLen(signal.symbol) == 0 || signal.period == NULL) { return; } // xEACollector.CollectSignal(signal); // // Count Open Positions ... int openPositionsCount = xEATrade.PositionsCount(); // string mTagId = GenerateTag(signal); // // Check Last Signal ... datetime barTime = iTime( signal.symbol, signal.period, 0); // // Retrieve Tradable Balance ... double availableBalance = xEAAccount.GetEquity(); // // Verify Available Balance ... double balance = xEAAccount.GetBalance(); // // Here i Must Calculate TP and SL based on R2R and riskable Factor ... bool isLong = IsLong(signal.type); // // Check Order Mode ... if (signal.mode == NULL) { // // Set Market Execution as Default ... signal.mode = X_ORDER_MODE_MARKET; } // // Calculate Entry Price ... if (signal.entry <= 0) { // signal.entry = GetEntry( signal.symbol, signal.type); } // // R2R ... if (signal.r2r <= 0) { // // Set Default Risk to Reward Ratio to 1 ... signal.r2r = 1; } // // Calculate TP and SL ... double mPoint = GetPoints(signal.symbol); // // Normalize SL Price ... if (signal.sl > 0) { signal.sl = NormalizePrice(signal.sl, signal.symbol); } // // Retrieve Symbol Point Value ... double pointValue = xEAAccount.GetPointValue(signal.symbol); // // Calculate Risk ... double riskPrice = MathAbs(signal.entry - signal.sl); riskPrice = NormalizePrice(riskPrice, signal.symbol); // double riskPoints = PriceToPoint(riskPrice); riskPoints = NormalizeDouble(riskPoints, 0); // // Calculating Reward based On Ratio ... double rewardPoints = riskPoints * signal.r2r; double rewardPrice = PointToPrice(rewardPoints); rewardPrice = NormalizePrice(rewardPrice, signal.symbol); // // Calculate TP Price ... double tpValue = isLong ? signal.entry + rewardPrice : signal.entry - rewardPrice; // // Calculate SL Price ... double slValue = isLong ? signal.entry - riskPrice : signal.entry + riskPrice; // // Balance Amount which we risks on each Provider's Trade ... double amount = availableBalance * signal.riskAmount; // // Calculating Trade Volume based on risk Points and Amount ... double volume = 0.02; // xEAAccount.CalculateVolume( // signal.symbol, // amount, // riskPoints); volume = NormalizeVolume(volume, signal.symbol); // // Set Caculated TP, SL and Volume ... signal.tp = tpValue; signal.sl = slValue; signal.volume = volume; // string comment = ""; int signalProvidersCount = ArraySize(signal.providers); if (signalProvidersCount > 0) { // string prvs = ""; for (int i = 0; i < signalProvidersCount; i++) { // string p = signal.providers[i]; // if (StringLen(prvs) > 0) { prvs += ","; } // prvs += p; } // comment = "PRV(" + prvs + ")"; } // // Here before Open Signal Position ... // and after complete TP SL and Volume Calculations ... // Recieve a List Of Supported Positions, based on retrieve Support and Resistance ares // for Signal ... XSignal supportSignals[]; xEATrade.GenerateSupportSignals( supportSignals, signal); int supportSignalsCount = ArraySize(supportSignals); // bool isPositionOpen = xEATrade.ExecuteSignal( signal, comment); // if (isPositionOpen) { // // Retrieve Position Ticket ... ulong ticket = xEATrade.GetLastOpenPositionTicket(); if (ticket == 0) { return; } // if (supportSignalsCount >= 0) { // // Here we Must Place Orders ... string comment = mSupportToken + "(" + (string)ticket + ")"; // // CleanSupport Positions SLs ... // for (int i = 0; i < supportSignalsCount; i++) { // // // XSignal iSignal = supportSignals[i]; // // // // iSignal.sl = 0; // } // // TODO: Add Expiration Date, Volume Calculation ... // int executedSignals = xEATrade.ExecuteSignals( // supportSignals, // comment); } // // Draw Signal ... ReDrawSignal(ticket, signal); // // Alert Position ... LogMessage("Position: " + (string)ticket + " was Open ..."); } else { DrawSupportResistance(1, signal.supportAndResistance); } } // // Handle StopLoss Event ... void OnStopLossTriggered(const XDeal &deal) { // string msg = "SL: " + (string)deal.positionId; LogMessage(msg); // xEACollector.CollectLog(msg); // xEACollector.CollectDeal(deal); } // // Handle TakeProfit Event ... void OnTakeProfitTriggered(const XDeal &deal) { // string msg = "TP: " + (string)deal.positionId; LogMessage(msg); // xEACollector.CollectLog(msg); // xEACollector.CollectDeal(deal); } // void OnDealsChangedHandler(int count) { // string msg = "Deals Changed: " + (string)count; LogMessage(msg); xEACollector.CollectLog(msg); } // void OnOrdersChangedHandler(int count) { // string msg = "Orders Changed: " + (string)count; LogMessage(msg); xEACollector.CollectLog(msg); } // void HandleGuard() { // // Handle Force Close Positions ... // HandleForceClosing(); // // Trailing Stops and Make Positions Risk Free ... // HandleRiskFree(); // // When all Positions Profit Going Positive ... // HandlePyramidHedge(); // // When a Position running in profit long time ... // HandleCloseOldInProfitTrades(); } // // Force Close Provider's Signals on Special Conditions ... void HandleForceClosing() { // // Force Close all XTRND Short Positions ... bool canCloseAllXTRNDShortPositions = // XTRNDForceCloseAllShorts() // ; if (canCloseAllXTRNDShortPositions) { // XPosition positions[]; GetSpecificProviderPositions( positions, _Symbol, _Period, XTRND, POSITION_TYPE_SELL); int positionsCount = ArraySize(positions); if (positionsCount > 0) { // string comment = "Force Close XTRND Shorts ..."; // xEATrade.ClosePositions( positions, comment); // DrawForceCloseEvent(); } } // // Force Close all XTRND Long Positions ... bool canCloseAllXTRNDLongPositions = // XTRNDForceCloseAllLongs() // ; if (canCloseAllXTRNDLongPositions) { // XPosition positions[]; GetSpecificProviderPositions( positions, _Symbol, _Period, XTRND, POSITION_TYPE_BUY); int positionsCount = ArraySize(positions); if (positionsCount > 0) { // string comment = "Force Close XTRND Shorts ..."; // xEATrade.ClosePositions( positions, comment); // DrawForceCloseEvent(); } } } // // Make Positions Risk Free and Trailing Stops ... void HandleRiskFree() { // double distanceFactorForRiskFree = 0.3; // XPosition inProfitPositions[]; xEATrade.GetInProfitPositions(inProfitPositions); int inProfitPositionsCount = ArraySize(inProfitPositions); if (inProfitPositionsCount <= 0) { return; } // for (int i = 0; i < inProfitPositionsCount; i++) { // XPosition iPosition = inProfitPositions[i]; // // Only Make Positions Risk Free based On their SL or TP ... if (iPosition.tp <= 0 && iPosition.sl <= 0) { continue; } // bool isLong = IsLong(iPosition.type); // bool isRFBefore = StringFind( iPosition.comment, "RF") > 0; // double lastRFPrice = iPosition.entry; string lastRFPriceStr = ""; if (isRFBefore) { // lastRFPriceStr = ExtractString( iPosition.comment, mRiskFreeToken + "(", ")" ); if (StringLen(lastRFPriceStr) > 0) { lastRFPrice = (double) lastRFPriceStr; } } // double tpDistancePrice = MathAbs(lastRFPrice - iPosition.tp); double rfPrice = (tpDistancePrice * distanceFactorForRiskFree); // bool canRiskFree = MathAbs(lastRFPrice - iPosition.price) > rfPrice; if (!canRiskFree) { continue; } // double tp = isLong ? iPosition.tp + rfPrice : iPosition.tp - rfPrice; double sl = isLong ? iPosition.tp - rfPrice : iPosition.sl - rfPrice; // string comment = iPosition.comment; if (isRFBefore) { // StringReplace( comment, lastRFPriceStr, "" ); } // comment += mRiskFreeToken + "(" + (string)iPosition.price + ")"; // bool isModified = xEATrade.Modify( iPosition.ticket, sl, tp, comment); if (isModified) { // string msg = "Make Position (" + (string)iPosition.ticket + ") Risk Free ..."; LogMessage(msg); } } } // // Pyramid Hedging ... void HandlePyramidHedge() { // int positionsCount = xEATrade.PositionsCount(); if (positionsCount <= 1) { return; } // double minProfitForHedging = positionsCount * 1; double positionsProfit = xEATrade.GetPositionsProfit(); // if (positionsProfit < minProfitForHedging) { return; } // string comment = "Pyramid Hedging ..."; // xEATrade.CloseAllPositions(comment); } // // Close In Profit Old Trades ... void HandleCloseOldInProfitTrades() { // int oldAge = 50; double minProfit = 1; bool onlySupports = false; // XPosition inProfitPositions[]; xEATrade.GetInProfitPositions(inProfitPositions); int inProfitPositionsCount = ArraySize(inProfitPositions); if (inProfitPositionsCount <= 0) { return; } // for (int i = 0; i < inProfitPositionsCount; i++) { // XPosition iPosition = inProfitPositions[i]; if (iPosition.profit < minProfit || (iPosition.tp > 0 && MathAbs(iPosition.entry - iPosition.price) < MathAbs(iPosition.entry - iPosition.tp) / 2)) { continue; } // // Check Position Age ... int iPositionBarIndex = xEATrade.GetPositionOpenBarIndex(iPosition.ticket); int iPositionAge = MathAbs(iPositionBarIndex - 0); if (iPositionAge < oldAge) { continue; } // string comment = "Close On Old In Profit Trade ..."; // bool isPositionClosed = false; // if (onlySupports) { // // Check Position is Support or not ... bool isSupport = IsSupportPosition(iPosition); if (isSupport) { // // Close Position ... isPositionClosed = xEATrade.Close( iPosition.ticket, comment); } } else { // // Close Position ... isPositionClosed = xEATrade.Close( iPosition.ticket, comment); } // // Alert Events ... string msg = "Position (" + (string)iPosition.ticket + ") Closed due InProfit Old Event ..."; LogMessage(msg); } } // // Tools and Usefull Functions ... // // // Convert Position to Signal ... XSignal ToSignalInfo( XPosition &position) { // XSignal result; // result.tp = position.tp; result.sl = position.sl; result.type = position.type; result.entry = position.entry; result.symbol = position.symbol; result.period = position.period; result.volume = position.volume; // return result; } // string ToString(ENUM_X_SIGNAL_PROVIDERS provider) { // return EnumToString(provider); } // ENUM_X_SIGNAL_PROVIDERS ToProvider(string provider) { // ENUM_X_SIGNAL_PROVIDERS result; // if (ToString(XTest) == provider) { result = XTest; } else if (ToString(XTRND) == provider) { result = XTRND; } else if (ToString(XICHI) == provider) { result = XICHI; } else { result = NONE; } // return result; } // // Extract Position Provider ... ENUM_X_SIGNAL_PROVIDERS ExtractProviderFromComment(string comment) { // ENUM_X_SIGNAL_PROVIDERS result = NONE; // if (StringLen(comment) <= 0) { return result; } // string providerStr = ExtractString( comment, mProviderToken + "(", ")"); if (StringLen(providerStr) <= 0) { return result; } // result = ToProvider(providerStr); // return result; } // // Retrieve Specific Type of Providers Positions ... void GetSpecificProviderPositions( XPosition &result[], // Holds Result string symbol, // Trading Symbol ENUM_TIMEFRAMES period, // Trading Period ENUM_X_SIGNAL_PROVIDERS provider, // Position Provider ENUM_POSITION_TYPE type = NULL // Position Type ) { // Clean(result); // if (StringLen(symbol) == 0 || provider == NONE) { return; } // int positionsCount = xEATrade.PositionsCount(); if (positionsCount <= 0) { return; } // XPosition symbolPeriodPositions[]; xEATrade.GetPositions( symbolPeriodPositions, symbol, period); int symbolPeriodPositionsCount = ArraySize(symbolPeriodPositions); if (symbolPeriodPositionsCount <= 0) { return; } // for (int i = 0; i < symbolPeriodPositionsCount; i++) { // XPosition iPosition = symbolPeriodPositions[i]; // ENUM_X_SIGNAL_PROVIDERS iProvider = ExtractProviderFromComment(iPosition.comment); if (iProvider == NONE || iProvider != provider) { continue; } // if (type == NULL) { // Add( iPosition, result); } else if (type == iPosition.type) { // Add( iPosition, result); } } } // // Determines a Position is Support Position or not ... bool IsSupportPosition(XPosition &position) { // bool result = false; // if (StringLen(position.comment) <= 0) { return result; } // int supTokenPos = StringFind( position.comment, mSupportToken + "("); result = supTokenPos >= 0; // return result; } // // END Functions ... // // // Draw Signal ... void ReDrawSignal(const ulong ticket, XSignal &signal) { // // return; // DrawSupportResistance(1, signal.supportAndResistance); // bool isLong = IsLong(signal.type); // string prefix = (string)ticket + "_" + ToString(signal.period); // // Retrieve Time ... datetime time = iTime( signal.symbol, signal.period, 0); // // Retrieve 3 Time ... datetime time3 = iTime( signal.symbol, signal.period, 3); // color posColor = isLong ? clrSpringGreen : clrTomato; color tpColor = clrSpringGreen; color slColor = clrTomato; color entryColor = clrGold; // ENUM_LINE_STYLE vLineStyle = STYLE_DOT; ENUM_LINE_STYLE tpLineStyle = STYLE_SOLID; ENUM_LINE_STYLE slLineStyle = STYLE_SOLID; ENUM_LINE_STYLE entryLineStyle = STYLE_SOLID; // int lineWidth = 2; // long chartID = ChartID(); // // Vertical Line ... string vlName = prefix + "_" + (string)time; // DrawVerticalLine( chartID, vlName, 0, time, posColor, vLineStyle, lineWidth); // // TP ... double profit = (MathAbs(signal.tp - signal.entry)) * signal.volume; string tpName = prefix + "_TP_" + (string)profit; // DrawTrendLine( chartID, tpName, 0, time3, signal.tp, time, signal.tp, tpColor, tpLineStyle, lineWidth); // // SL ... double lost = (MathAbs(signal.sl - signal.entry)) * signal.volume; string slName = prefix + "_SL_" + (string)lost; // DrawTrendLine( chartID, slName, 0, time3, signal.sl, time, signal.sl, slColor, slLineStyle, lineWidth); // // ENTRY ... string entryName = prefix + "_ENTRY_" + (string)lost; // DrawTrendLine( chartID, entryName, 0, time3, signal.entry, time, signal.entry, entryColor, entryLineStyle, lineWidth); } // // Alert Force Close Event on Chart ... void DrawForceCloseEvent() { // datetime barTime = GetCandleTime( _Symbol, _Period, 1); // long chartID = ChartID(); string vlName = "XTRND Force Close: " + (string)barTime; // DrawVerticalLine( chartID, vlName, 0, barTime, clrDarkViolet, STYLE_SOLID, 2); } // void DrawSupportResistance( int bar_index, XOHCLSupRes &data, string prefix = "", color supportColor = clrLime, color resistanceColor = clrRed) { // // return; // datetime barTime = GetCandleTime( _Symbol, _Period, bar_index); // datetime prevBarTime = GetCandleTime( _Symbol, _Period, bar_index + 5); // long chID = ChartID(); int subWindiw = 0; int lineWidth = 2; // string mPrefix = prefix + "_" + (string)barTime + "_"; // // SUPPORTS ... // int supCount = ArraySize(data.sup); if (supCount > 0) { // for (int i = 0; i < supCount; i++) { // double iSup = data.sup[i]; // string supTlName = mPrefix + "SUP[" + (string)(i + 1) + "]_(" + (string)iSup + ")"; // // Draw Support ... DrawTrendLine( chID, supTlName, subWindiw, prevBarTime, iSup, barTime, iSup, supportColor, STYLE_DOT, lineWidth, true); } } // // RESISTANCES ... // int resCount = ArraySize(data.res); if (resCount > 0) { // for (int i = 0; i < resCount; i++) { // double iRes = data.res[i]; // string resTlName = mPrefix + "RES[" + (string)(i + 1) + "]_(" + (string)iRes + ")"; // // Draw Support ... DrawTrendLine( chID, resTlName, subWindiw, prevBarTime, iRes, barTime, iRes, resistanceColor, STYLE_DOT, 2, true); } } }