//+------------------------------------------------------------------+ //| Twin Ranger | //| Copyright 2024, Kriben. | //| https://www.mql5.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, Kriben." #property version "1.00" #property strict #include #include #include #include #define ERR_OTHER_ERROR 2 #define HISTORY_BARS 20 #define MagicNumber 101010 #define OP_BUY 0 #define OP_SELL 1 #define ORDER_TYPE_BUY OP_BUY #define ORDER_TYPE_SELL OP_SELL #define MAX_BARS 2000 #define SECONDS_IN_MINUTE 60 #define SECONDS_IN_HOUR (SECONDS_IN_MINUTE * 60) #define SYMBOL_SPREAD_FACTOR 2 CTrade trade; CPositionInfo PositionInfo; // Variable Initialization double Trend[], TrendColor[], TrendDirection[]; double supertrendUp[], supertrendDown[], trend[]; int rates_total, counted_bars; int supertrendHandle; int pineMTATRNormalise; double highPrices[]; double lowPrices[]; double pipDistance; datetime openTimeBuy = 0; datetime openTimeSell = 0; enum ENUM_ORDER_DIRECTION { Long, Short }; enum enum_candle_to_check { Current, Previous }; // Trade Settings input bool FractionalPositionSizing = true; // -----Dynamic Position Sizing----- input double riskPercentage = 7.5; // Default Acc. Risk % input double riskPercentageLow = 5.0; // High Volatility Acc. Risk % input ENUM_ORDER_DIRECTION OrderDirectionChoice = Long; // Choose Trade Direction // RSI Settings input bool EnableRSIFilter = true; // -----Enable RSI----- input int iRSIPeriod = 10; // RSI Period input int iRSIHigh = 2; // RSI Upper Value input int iRSILow = 75; // RSI Lower Value // Supertrend Settings input bool EnableSupertrendFilter = true; // -----Enable Supertrend----- input int supertrendATRPeriod = 14; // Supertrend ATR Period input double supertrendATRMultiplier = 50.0; // Supertrend ATR Multiplier int Shift = 0; // Indicator shift, positive or negative input enum_candle_to_check TriggerCandle = Previous; // Candle to check // Twin Range Settings input bool EnableTwinRangeFilter = true; // -----Enable Twin Range----- input int fastPeriod = 60; // Twin Range Fast Period input double fastRange = 5.0; // Twin Range Fast Range input int slowPeriod = 4; // Twin Range Slow Period input double slowRange = 2.0; // Twin Range Slow Range // Twin ATR Range Settings input bool EnableATRFilter = true; // -----Enable Twin ATR----- input int atrLengthA = 80; // ATR 1 input ENUM_MA_METHOD atrMethodA = MODE_EMA; // Smoothing Method input int atrLengthB = 20; // ATR 2 input ENUM_MA_METHOD atrMethodB = MODE_EMA; // Smoothing Method // Moving Average Settings input bool EnableMovingAverageFilter = true; // -----Enable Moving Average----- input int maATR = 2; // Calculate Moving Average Period input ENUM_MA_METHOD movingAverageMethod = MODE_SMA; // Moving Average Smoothing ENUM_APPLIED_PRICE maAppliedPrice = PRICE_CLOSE; // Moving Average Smoothing Close // Trade Risk Settings ENUM_APPLIED_PRICE takeProfitPriceType = PRICE_CLOSE; // TP Calculations Source input int slLengthA = 1; // --Long Stop Loss Multiplier input double slMultiplierA = 50.0; // --Long Stop Loss % input int stopLossTicks = 10; // --Adjust Tick Value for Stop Loss input int tpATRLengthA = 1.0; // --Long Take Profit ATR Length input int tpMultiplierLong = 90.0; // --Long Take Profit Multiplier input int tpTicks = 1; // --Adjust Tick Value for Take Profit input int slLengthB = 14; // --Short Stop Loss Multiplier input double slMultiplierB = 0.5; // --Short Stop Loss % input int tpATRLengthB = 1.0; // --Short Take Profit ATR Length input double tpMultiplierShort = 1.0; // --Short Take Profit Multiplier // Trading Time Range Settings input bool EnableTradingTimeRange = true; // -----Enable Trading Time Range----- input int startHour = 0; // Start Trading Hour input int startMinute = 0; // Start Trading Minutes input int endHour = 23; // End Trading Hour input int endMinute = 0; // End Trading Minutes input bool isMonday = true; // --Trade On Monday input bool isTuesday = true; // --Trade On Tuesday input bool isWednesday = true; // --Trade On Wednesday input bool isThursday = true; // --Trade On Thursday input bool isFriday = true; // --Trade On Friday input bool isSaturday = false; // --Trade On Saturday input bool isSunday = false; // --Trade On Sunday // Risk Management input bool EnableEATimeoutClose = true; // -----Close Trade Safety Timer----- input int tradeCloseTime = 30; // Close Trades After (mins) input bool EnablemaxConsecutiveLosses = true;// -----Max Losses EA Disabler----- input int maxConsecutiveLosses = 3; // Max Consecutive Losses To Disable EA input bool LimitMaxOrders = true; // -----Limit Max Open Order----- input int maxAllowedOrders = 1; // Max Open Trade / Time // Misc. Settings input bool EnableMinPipDistance = true; // -----Check TP Min Distance----- input double minPipDistance = 10.0; // Min Price/TP Pip Distance input bool EnablePineMT5Normalise = true; // --Enable Pine/MT5 ATR Normalise input bool EnableDebugLogging = false; // --Enable Debug Logging // Requests history for the last days and returns false in case of failure bool GetTradeHistory(int days, ulong &ticket, string &orderSymbol, long &orderMagicNumber, double &profit) { datetime to = TimeCurrent(); datetime from = to - days * PeriodSeconds(PERIOD_D1); ResetLastError(); if(!HistorySelect(from, to)) { Print(__FUNCTION__, " HistorySelect=false. Error code=", GetLastError()); return false; } return true; } // Count open positions, also detects buy and sell orders bool CountOpenPositions(int &countBuy, int &countSell, bool LimitOrders) { countBuy = 0; countSell = 0; int total = PositionsTotal(); for (int i = total - 1; i >= 0; i--) { ulong positionTicket = PositionGetTicket(i); if (positionTicket <= 0) { Print("Failed to get ticket"); return false; } if (!PositionSelectByTicket(positionTicket)) { Print("Failed to select position"); return false; } long magic; if (!PositionGetInteger(POSITION_MAGIC, magic)) { Print("Failed to get magic"); return false; } if (magic == MagicNumber) { long type; if (!PositionGetInteger(POSITION_TYPE, type)) { Print("Failed to get type"); return false; } if (type == POSITION_TYPE_BUY) { countBuy++; } else if (type == POSITION_TYPE_SELL) { countSell++; } } } // If LimitOrders is enabled, check the maximum allowed orders based on the direction if (LimitOrders) { int orderDirection = DetermineOrderDirection(); if (orderDirection == Long) { return countBuy < maxAllowedOrders; } else if (orderDirection == Short) { return countSell < maxAllowedOrders; } } return true; } // Function to calculate and set pineMTATRNormalise int SetPineMTATRNormalise() { if (!EnablePineMT5Normalise) return 1; // Return 1 if normalization is disabled // Calculate the number of bars per chart time int mt5BarsPerChartTime = iBars(_Symbol, _Period); // Calculate the dynamic normalization factor pineMTATRNormalise = rates_total / mt5BarsPerChartTime; return pineMTATRNormalise; } //+------------------------------------------------------------------+ //| Trading Schedule | //+------------------------------------------------------------------+ bool IsTradingDay() { MqlDateTime currentTime; if (!TimeToStruct(TimeCurrent(), currentTime)) { Print("Error: Unable to get current day."); LOG(LOG_LEVEL_FATAL, "Error when trying to get current day."); return false; } int currentDay = currentTime.day_of_week; // Define trading days array (starting from Sunday) bool tradeOnDay[7] = {isSunday, isMonday, isTuesday, isWednesday, isThursday, isFriday, isSaturday}; return tradeOnDay[currentDay]; } bool IsTradingTime() { MqlDateTime currentTime; // Retrieve the current time if (!TimeToStruct(TimeCurrent(), currentTime)) { Print("Error: Unable to get current time."); LOG(LOG_LEVEL_FATAL, "Error when trying to get current time."); return false; } // Extract components of the current time int currentHour = currentTime.hour; int currentMinute = currentTime.min; int currentSecond = currentTime.sec; int dayOfWeek = currentTime.day_of_week; // Check if it's a trading day (Monday to Friday) bool isTradingDay = (dayOfWeek >= 1 && dayOfWeek <= 5); // Calculate time in seconds for start and end times int startTimeInSeconds = (startHour * SECONDS_IN_HOUR) + (startMinute * SECONDS_IN_MINUTE); int endTimeInSeconds = (endHour * SECONDS_IN_HOUR) + (endMinute * SECONDS_IN_MINUTE); // Calculate current time in seconds int currentTimeInSeconds = (currentHour * SECONDS_IN_HOUR) + (currentMinute * SECONDS_IN_MINUTE) + currentSecond; // Check if the current time is within the trading time range return isTradingDay && (currentTimeInSeconds >= startTimeInSeconds) && (currentTimeInSeconds <= endTimeInSeconds); } bool ItsTradingTime() { bool tradingCondition = IsTradingDay() && IsTradingTime(); if (tradingCondition) { Print("Twin Ranger EA: Trading On"); } else { Print("Twin Ranger EA: Not Trading Time. IsTradingDay=", IsTradingDay(), ", IsTradingTime=", IsTradingTime()); } return tradingCondition; } // Function to determine order direction int DetermineOrderDirection() { return OrderDirectionChoice; } ENUM_ORDER_TYPE ConvertOrderDirectionToType(ENUM_ORDER_DIRECTION direction) { if (direction == Long) { return ORDER_TYPE_BUY; } else { return ORDER_TYPE_SELL; } } //+------------------------------------------------------------------+ //| Twin Range Filter | //+------------------------------------------------------------------+ // Calculate True Range (TR) double CalculateTrueRange() { double highPrice = iHigh(_Symbol, _Period, 0); double lowPrice = iLow(_Symbol, _Period, 0); double closePrice = iClose(_Symbol, _Period, 0); // Adjust prices for different bar size double prevClose = iClose(_Symbol, _Period, 1); double prevHigh = iHigh(_Symbol, _Period, 1); double prevLow = iLow(_Symbol, _Period, 1); double trueRange1 = highPrice - lowPrice; double trueRange2 = MathAbs(highPrice - prevClose); double trueRange3 = MathAbs(lowPrice - prevClose); return MathMax(MathMax(trueRange1, trueRange2), trueRange3); } // Smoothed Average Range double SmoothedAverageRange(double source, int period, double multiplier) { double weightPeriod = period * 2 - 1; double averageRange = iMA(_Symbol, _Period, period, 0, MODE_EMA, PRICE_CLOSE); double smoothedRange = iMA(_Symbol, _Period, (int)weightPeriod, 0, MODE_EMA, PRICE_CLOSE) * multiplier * averageRange; return smoothedRange; } // Range Filter double RangeFilter(double price, double smrng) { if (price > iClose(_Symbol, _Period, 1)) { if (smrng == 0.0) { Print("Error in Range Filter: Smoothing Range is zero."); return 0.0; } double diff = price - smrng; return (diff < iClose(_Symbol, _Period, 1)) ? iClose(_Symbol, _Period, 1) : diff; } else { if (smrng == 0.0) { Print("Error in Range Filter: Smoothing Range is zero."); return 0.0; } return (price + smrng > iClose(_Symbol, _Period, 1)) ? iClose(_Symbol, _Period, 1) : price + smrng; } } // Calculate Twin Range Filter bool CalculateTwinRangeFilter() { double source = iClose(_Symbol, _Period, 0); // Calculate True Range (TR) double trueRange = CalculateTrueRange(); // Smoothed Average Range double smrng1 = SmoothedAverageRange(source, fastPeriod, fastRange); double smrng2 = SmoothedAverageRange(source, slowPeriod, slowRange); double smrng = (smrng1 + smrng2) / 2; // Range Filter double localFilter = RangeFilter(source, smrng); // Main logic for Twin Range Filter bool twinRangeBuy = (source > localFilter && source > iClose(_Symbol, _Period, 1) && trueRange > 0); bool twinRangeSell = (source < localFilter && source < iClose(_Symbol, _Period, 1) && trueRange > 0); return (twinRangeBuy || twinRangeSell); } // Twin ATR Calculations double Atr2(const int length, const ENUM_MA_METHOD ATRmethod) // ATR 1 { double atrValue = iMA(_Symbol, 0, length, 0, ATRmethod, PRICE_CLOSE); if (atrValue == INVALID_HANDLE) { Print("Error in Twin Atr 2: Invalid handle."); return 0.0; } return atrValue; } double Atr3(const int length, const ENUM_MA_METHOD ATRmethod) // ATR 2 { double atrValue = iMA(_Symbol, 0, length, 0, ATRmethod, PRICE_CLOSE); if (atrValue == INVALID_HANDLE) { Print("Error in Twin Atr 3: Invalid handle."); return 0.0; } return atrValue; } // Modify CalculateTwinATRFilter function bool CalculateTwinATRFilter() { datetime time[]; double open[]; double High[]; double Low[]; double Close[]; long tick_volume[]; long volume[]; int spread[]; // Calculate Twin Range ATR double atr2Value = Atr2(fastPeriod, atrMethodA); double atr3Value = Atr3(slowPeriod, atrMethodB); bool twinATRBuy = (atr2Value < atr3Value); bool twinATRSell = (atr2Value > atr3Value); // Return true if your Twin ATR conditions apply, otherwise return false return (twinATRBuy || twinATRSell); } //+------------------------------------------------------------------+ //| Supertrend Filter | //+------------------------------------------------------------------+ bool CalculateSupertrendFilter() { int limit = rates_total - counted_bars - 1; if (limit > MAX_BARS) { limit = MAX_BARS; if (rates_total < MAX_BARS + 2 + supertrendATRPeriod) limit = rates_total - 2 - supertrendATRPeriod; if (limit <= 0) { Print("Need more historical data to calculate Supertrend"); LOG(LOG_LEVEL_ERROR, "Need more historical data to calculate Supertrend."); return false; } } if (limit > rates_total - 2 - supertrendATRPeriod) limit = rates_total - 2 - supertrendATRPeriod; double supertrendATRBuffer[]; ArraySetAsSeries(supertrendATRBuffer, true); CopyBuffer(supertrendHandle, 0, 0, limit + 1, supertrendATRBuffer); // Call the function to set pineMTATRNormalise pineMTATRNormalise = SetPineMTATRNormalise(); int i; // Declare i outside the loop for (i = limit; i >= 0; i--) { bool flag, flagh; Trend[i] = EMPTY_VALUE; TrendDirection[i] = EMPTY_VALUE; double atr = supertrendATRBuffer[i]; double medianPrice = (supertrendUp[i] + supertrendDown[i]) / 2; supertrendUp[i] = medianPrice + supertrendATRMultiplier * pineMTATRNormalise * atr; supertrendDown[i] = medianPrice - supertrendATRMultiplier * pineMTATRNormalise * atr; // Call the function to set pineMTATRNormalise pineMTATRNormalise = SetPineMTATRNormalise(); trend[i] = 1; if (trend[i] > supertrendUp[i + 1]) { trend[i] = 1; } else if (trend[i] < supertrendDown[i + 1]) { trend[i] = -1; } else if (trend[i + 1] == 1) { trend[i] = 1; } else if (trend[i + 1] == -1) { trend[i] = -1; } if ((trend[i] < 0) && (trend[i + 1] > 0)) { flag = true; } else { flag = false; } if ((trend[i] > 0) && (trend[i + 1] < 0)) { flagh = true; } else { flagh = false; } if ((trend[i] > 0) && (supertrendDown[i] < supertrendDown[i + 1])) { supertrendDown[i] = supertrendDown[i + 1]; } else if ((trend[i] < 0) && (supertrendUp[i] > supertrendUp[i + 1])) { supertrendUp[i] = supertrendUp[i + 1]; } if (flag) { supertrendUp[i] = medianPrice + supertrendATRMultiplier * pineMTATRNormalise * atr; } else if (flagh) { supertrendDown[i] = medianPrice - supertrendATRMultiplier * pineMTATRNormalise * atr; } if (trend[i] == 1) { Trend[i] = supertrendDown[i]; } else if (trend[i] == -1) { Trend[i] = supertrendUp[i]; } if (trend[i] == 1) TrendColor[i] = 0; else if (trend[i] == -1) TrendColor[i] = 1; TrendDirection[i] = TrendColor[i]; } bool supertrendBuy = (trend[i] == 1); bool supertrendSell = (trend[i] == -1); // Check the trend direction using historical data return (supertrendBuy || supertrendSell); } //+------------------------------------------------------------------+ //| Calculate Alternate Timeline MA Filter | //+------------------------------------------------------------------+ bool CalculateMAFilter() { // Input parameters for the MA filter ENUM_TIMEFRAMES timeframe = EnableMovingAverageFilter ? (ENUM_TIMEFRAMES)_Period : _Period; // Set to _Period if moving average filter is disabled ENUM_MA_METHOD maMethod = EnableMovingAverageFilter ? movingAverageMethod : (ENUM_MA_METHOD)0; // Set to (ENUM_MA_METHOD)0 if moving average filter is disabled ENUM_APPLIED_PRICE appliedPrice = EnableMovingAverageFilter ? maAppliedPrice : (ENUM_APPLIED_PRICE)0; // Set to (ENUM_APPLIED_PRICE)0 if moving average filter is disabled // Call the function to set pineMTATRNormalise pineMTATRNormalise = SetPineMTATRNormalise(); int maPeriod = maATR * pineMTATRNormalise; int maShift = 0; // Set to 0 to calculate MA for the current bar if (timeframe < _Period) { Print("Error: MA Timeframe should be greater or equal to the chart timeframe."); LOG(LOG_LEVEL_FATAL, "Error: MA Timeframe should be greater or equal to the chart timeframe."); return false; // Return an appropriate value or handle the error accordingly } // Fetch the historical Close value and store it double currentClose = iClose(_Symbol, _Period, 0); if (currentClose == INVALID_HANDLE) { Print("Error: MA calculation failed."); LOG(LOG_LEVEL_FATAL, "Error: MA calculation failed."); return false; // Return an appropriate value or handle the error accordingly } // Calculate the MA on the specified timeframe with the chosen applied price double maAltTimeline = iMA(_Symbol, timeframe, maPeriod, maShift, maMethod, appliedPrice); bool maBuy = (currentClose > maAltTimeline); bool maSell = (currentClose < maAltTimeline); // Check the trend direction using historical data return (maBuy || maSell); } //+------------------------------------------------------------------+ //| Calculate RSI Filter | //+------------------------------------------------------------------+ bool CalculateRSIFilter() { double ask = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_ASK), _Digits); double bid = NormalizeDouble(SymbolInfoDouble(_Symbol, SYMBOL_BID), _Digits); double rsiValue = iRSI(_Symbol, _Period, iRSIPeriod, PRICE_CLOSE); bool rsiBuy = (rsiValue > iRSIHigh); bool rsiSell = (rsiValue < iRSILow); // Return true if your RSI conditions apply, otherwise return false return (rsiBuy || rsiSell); } // Define eaVolatilityThreshold as a global variable double eaVolatilityThreshold; // Function to calculate historical volatility based on standard deviation of high and low prices double CalculateHistoricalVolatility(int period) { // Copy high and low prices into arrays CopyHigh(_Symbol, _Period, 0, period, highPrices); CopyLow(_Symbol, _Period, 0, period, lowPrices); double sum = 0.0; double sumSquared = 0.0; int count = 0; // Loop through historical prices for(int i = 0; i < period - 1; i++) { double priceToday = highPrices[i]; double priceYesterday = lowPrices[i + 1]; // Calculate daily price change double priceChange = priceToday - priceYesterday; // Accumulate values for standard deviation calculation sum += priceChange; sumSquared += priceChange * priceChange; count++; } // Calculate standard deviation double mean = sum / count; double variance = sumSquared / count - mean * mean; double standardDeviation = MathSqrt(variance); // Calculate annualized volatility double annualizedVolatility = standardDeviation * MathSqrt(252); // Assuming 252 trading days in a year // Update eaVolatilityThreshold based on historical volatility if(annualizedVolatility >= 2.0) { eaVolatilityThreshold = 2.0; } else if(annualizedVolatility >= 1.5) { eaVolatilityThreshold = 1.5; } else { eaVolatilityThreshold = 1.0; } return annualizedVolatility; } double CalculatestopLoss() { int orderDirection = DetermineOrderDirection(); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double spreadAdjustment = (double)SymbolInfoInteger(_Symbol, SYMBOL_SPREAD); // Add validation for ATR lengths if (slLengthA <= 0 || slLengthB <= 0) { Print("Error: Stop Loss ATR lengths should be greater than 0."); LOG(LOG_LEVEL_ERROR, "Error: Stop Loss ATR lengths should be greater than 0."); return 0.0; } // Add validation for ATR lengths if (slLengthA <= 0 || slLengthB <= 0) { return 0.0; Print("Error: ATR lengths should be greater than 0."); } if (orderDirection == Long) { double highestHigh = iHigh(_Symbol, _Period, iHighest(_Symbol, _Period, MODE_HIGH, slLengthB, 0)); return highestHigh + MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - highestHigh - spreadAdjustment) * (stopLossTicks * tickSize); } if (orderDirection == Short) { double lowestLow = iLow(_Symbol, _Period, iLowest(_Symbol, _Period, MODE_LOW, slLengthA, 0)); return lowestLow - MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_BID) - lowestLow + spreadAdjustment) * (stopLossTicks * tickSize); } return 0.0; } //+------------------------------------------------------------------+ //| Calculate Take Profit | //+------------------------------------------------------------------+ double CalculateTakeProfit() { double price; int priceType = takeProfitPriceType; // Call the function to set pineMTATRNormalise pineMTATRNormalise = SetPineMTATRNormalise(); // Add validation for ATR lengths if (tpATRLengthA <= 0 || tpATRLengthB <= 0) { Print("Error: Take Profit ATR lengths should be greater than 0."); LOG(LOG_LEVEL_ERROR, "Error: Take ProfitATR lengths should be greater than 0."); return 0.0; } switch (priceType) { case PRICE_CLOSE: price = iClose(_Symbol, _Period, 0); break; case PRICE_OPEN: price = iOpen(_Symbol, _Period, 0); break; case PRICE_HIGH: price = iHigh(_Symbol, _Period, 0); break; case PRICE_LOW: price = iLow(_Symbol, _Period, 0); break; default: Print("Invalid price type. Using default (CLOSE)."); price = iClose(_Symbol, _Period, 0); } double highestHigh = iHigh(_Symbol, _Period, iHighest(_Symbol, _Period, MODE_HIGH, tpATRLengthA * pineMTATRNormalise, 0)); double lowestLow = iLow(_Symbol, _Period, iLowest(_Symbol, _Period, MODE_LOW, tpATRLengthB * pineMTATRNormalise, 0)); int orderDirection = DetermineOrderDirection(); if (orderDirection == Long) { return price + MathAbs(price - highestHigh) * tpMultiplierLong + tpTicks * SymbolInfoDouble(_Symbol, SYMBOL_POINT) + SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SYMBOL_SPREAD_FACTOR; } else if (orderDirection == Short) { return price - MathAbs(price - lowestLow) * tpMultiplierShort - tpTicks * SymbolInfoDouble(_Symbol, SYMBOL_POINT) - SymbolInfoInteger(_Symbol, SYMBOL_SPREAD) * SYMBOL_SPREAD_FACTOR; } return 0.0; } // Function to calculate position size based on risk percentage, stop loss, and account information double CalculatePositionSize() { double stopLoss = CalculatestopLoss(); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); if (stopLoss != 0.0) { double dynamicRiskPercentage = riskPercentage; // Use a local variable for calculations if (FractionalPositionSizing) { // Calculate historical volatility double historicalVolatility = CalculateHistoricalVolatility(20); // Use the past 20 days for volatility calculation // Set a threshold for deciding between high/low volatility double volatilityThreshold = eaVolatilityThreshold; // Adjust dynamic risk percentages based on conditions if (historicalVolatility >= volatilityThreshold) { dynamicRiskPercentage = riskPercentageLow; } else { return dynamicRiskPercentage; } } else { return dynamicRiskPercentage; // Use default risk percentage if fractional position sizing is disabled } // Consider account leverage in the risk calculation double riskAmount = (dynamicRiskPercentage / 100.0) * AccountInfoDouble(ACCOUNT_BALANCE) / AccountInfoInteger(ACCOUNT_LEVERAGE); // Calculate stop loss in pips double stopLossPips = MathAbs(SymbolInfoDouble(_Symbol, SYMBOL_ASK) - stopLoss) / tickSize; if (stopLossPips <= 0.0) { Print("Error: Stop loss calculation resulted in 0 or negative pips. Adjust stop loss or check price data."); Print("Stop Loss: ", stopLoss); Print("Price: ", SymbolInfoDouble(_Symbol, SYMBOL_ASK)); LOG(LOG_LEVEL_ERROR, "Error: Stop loss calculation resulted in 0 or negative pips. Adjust stop loss or check price data."); return 0.0; // Return a default value or handle the error appropriately } double positionSize = riskAmount / stopLossPips / tickValue; // Log information for debugging Print("Position Size: ", positionSize); Print("Risk Amount: ", riskAmount); Print("Stop Loss Pips: ", stopLossPips); return positionSize; } else { Print("Error: Stop loss is zero. Adjust stop loss value."); LOG(LOG_LEVEL_FATAL, "Error: Stop loss is zero. Adjust stop loss value."); return 0.0; // Return a default value } } //+------------------------------------------------------------------+ //| Disable EA After Max Consecutive Losses | //+------------------------------------------------------------------+ void MaxLossEAOff() { if (EnablemaxConsecutiveLosses) { int consecutiveLosses = 0; for (int i = HistoryDealsTotal() - 1; i >= 0; i--) { ulong ticket; string symbol; long magicNumber; double profit; if (GetTradeHistory(i, ticket, symbol, magicNumber, profit)) { // Add a condition to check for a specific symbol if (symbol == _Symbol && magicNumber == MagicNumber) { if (profit < 0) { consecutiveLosses++; if (consecutiveLosses >= maxConsecutiveLosses) { Print("Consecutive losses exceed the maximum limit. Removing EA."); ExpertRemove(); // This line will remove the EA break; } } else { consecutiveLosses = 0; } } } } } } //+------------------------------------------------------------------+ //| Timeout Failsafe Close | //+------------------------------------------------------------------+ bool EATimeoutClose() { if (EnableEATimeoutClose) { if (HistoryOrdersTotal() > 0) { datetime currentTime = TimeCurrent(); if (openTimeBuy > 0 && currentTime >= openTimeBuy + tradeCloseTime * 60) { Print("Closing EA due to Buy trade exceeding failsafe timeout."); return true; } if (openTimeSell > 0 && currentTime >= openTimeSell + tradeCloseTime * 60) { Print("Closing EA due to Sell trade exceeding failsafe timeout."); return true; } } } return false; } // Function to check pip distance for a potential position int CheckPipDistance() { if (EnableMinPipDistance) { double openPrice = ConvertOrderDirectionToType(OrderDirectionChoice) == OP_BUY ? SymbolInfoDouble(_Symbol, SYMBOL_BID) : SymbolInfoDouble(_Symbol, SYMBOL_ASK); double takeProfit = CalculateTakeProfit(); ENUM_ORDER_TYPE orderType = (ENUM_ORDER_TYPE)OrderGetInteger(ORDER_TYPE); // Check the pip distance for the potential position if (openPrice == ORDER_TYPE_BUY_LIMIT || openPrice == ORDER_TYPE_BUY_STOP) pipDistance = MathAbs(openPrice - takeProfit) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); else if (openPrice == ORDER_TYPE_SELL_LIMIT || openPrice == ORDER_TYPE_SELL_STOP) pipDistance = MathAbs(takeProfit - openPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); else { // Handle other order types or return an error code Print("Invalid order type"); LOG(LOG_LEVEL_FATAL, "Error: Check pip distance - Invalid order type."); return -1; } // Check if pip distance is less than minPipDistance if (pipDistance < minPipDistance) { // Print a message if needed Print("Potential optimum position does not meet min. pip distance criteria. Cancelling order placement. Pip Distance: ", pipDistance); // Return a value indicating failure return 0; } } // Return a value indicating success return 1; } bool BullishTradeConditions() { // Calculate order direction bool Long = DetermineOrderDirection(); bool Short = DetermineOrderDirection(); // Calculate Twin Range condition int twinTRResult = EnableTwinRangeFilter ? CalculateTwinRangeFilter() : 0; bool twinRangeBuy = twinTRResult; bool twinRangeSell = twinTRResult; // Calculate RSI int rsiResult = EnableRSIFilter ? CalculateRSIFilter() : 0; bool rsiBuy = rsiResult; bool rsiSell = rsiResult; // Check for long condition (Twin Range + RSI > RSI_OVERBOUGHT) bool bullBuyCond = twinRangeBuy && rsiBuy && Long; // Check for short condition (Twin Range + RSI < RSI_OVERSOLD) bool bullSellCond = twinRangeSell && rsiSell && Short; // Check if any conditions are met return (bullBuyCond || bullSellCond); } bool OptimumTradeConditions() { // Calculate order direction bool bullBuyCond = BullishTradeConditions(); bool bullSellCond = BullishTradeConditions(); // Calculate Twin Range ATR condition int twinATRResult = EnableATRFilter ? CalculateTwinATRFilter() : 0; bool twinATRBuy = twinATRResult; bool twinATRSell = twinATRResult; // Calculate Supertrend condition int supertrendResult = EnableSupertrendFilter ? CalculateSupertrendFilter() : 0; bool supertrendBuy = supertrendResult; bool supertrendSell = !supertrendResult; // Calculate Moving Average condition int maResult = EnableMovingAverageFilter ? CalculateMAFilter() : 0; bool maBuy = maResult; bool maSell = maResult; // Check for bull condition (bullBuyCond + Twin ATR + Supertrend + MA) bool longCond = twinATRBuy && supertrendBuy && maBuy && bullBuyCond; // Check for bear condition (bullSellCond + Twin ATR + Supertrend + MA) bool shortCond = twinATRSell && supertrendSell && maSell && bullSellCond; // Final Pip distance check to eliminate orders too small if (longCond || shortCond) { if (CheckPipDistance()) { PlaceOrders(); Print("Optimum trade conditions and miniimum pip distance verified. Order sent for placement."); } else { Print("Optimum trade conditions verified but TP-Entry Pip distance too small to trade. Order not placed."); } } return false; Print("Optimum trade condition validations failed. Order not placed."); } bool PlaceOrders() { MqlTradeRequest globalRequest = {}; MqlTradeResult globalResult = {}; // Declare and initialise AccountBalance double accountBalance = AccountInfoDouble(ACCOUNT_BALANCE); // Calculate ask price double askPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK); // Calculate stop loss double stopLoss = CalculatestopLoss(); // Calculate take profit double takeProfit = CalculateTakeProfit(); // Calculate position size dynamically based on risk double positionSize = CalculatePositionSize(); // Log position size and other relevant information Print("Risk Percentage: ", riskPercentage); Print("Stop Loss: ", stopLoss); Print("Take Profit: ", takeProfit); Print("Account Balance: ", accountBalance); Print("Calculated Position Size: ", positionSize); Print("Current Ask Price: ", askPrice); // Allocate memory for request & results ZeroMemory(globalRequest); ZeroMemory(globalResult); // Request order data based on the calculated filters and strategies globalRequest.action = TRADE_ACTION_DEAL; globalRequest.symbol = _Symbol; globalRequest.volume = positionSize; globalRequest.deviation= 10; globalRequest.price = askPrice; globalRequest.sl = stopLoss; globalRequest.tp = takeProfit; globalRequest.magic = MagicNumber; globalRequest.type = ConvertOrderDirectionToType(OrderDirectionChoice); // Place the order ulong ticket = OrderSend(globalRequest, globalResult); // Check for order placement success if (ticket > 0) { Print("Voila! Order placement success! Ticket #" + IntegerToString(ticket)); return true; } else { // Handle specific errors int error = GetLastError(); switch (error) { default: Print("Order placement failed. Error code: " + IntegerToString(error)); LOG(LOG_LEVEL_FATAL, "Error when trying to open long position. Code: " + (string)trade.ResultRetcode() + ", Description: " + trade.ResultRetcodeDescription()); } return false; } } //+------------------------------------------------------------------+ //| Expert initialisation function | //+------------------------------------------------------------------+ int OnInit() { SetIndexBuffer(0, Trend, INDICATOR_DATA); SetIndexBuffer(1, TrendColor, INDICATOR_COLOR_INDEX); SetIndexBuffer(2, TrendDirection, INDICATOR_DATA); // For Supertrend iCustom reading (EAs and the like). SetIndexBuffer(3, supertrendUp, INDICATOR_CALCULATIONS); SetIndexBuffer(4, supertrendDown, INDICATOR_CALCULATIONS); SetIndexBuffer(5, trend, INDICATOR_CALCULATIONS); PlotIndexSetInteger(0, PLOT_SHIFT, Shift); PlotIndexSetInteger(1, PLOT_SHIFT, Shift); PlotIndexSetDouble(0, PLOT_EMPTY_VALUE, EMPTY_VALUE); PlotIndexSetDouble(1, PLOT_EMPTY_VALUE, EMPTY_VALUE); // Set series for arrays bool setSeriesResult = ArraySetAsSeries(Trend, true); if (!setSeriesResult) Print("Error: Failed to set series for Trend array."); setSeriesResult = ArraySetAsSeries(TrendColor, true); if (!setSeriesResult) Print("Error: Failed to set series for TrendColor array."); setSeriesResult = ArraySetAsSeries(supertrendUp, true); if (!setSeriesResult) Print("Error: Failed to set series for supertrendUp array."); setSeriesResult = ArraySetAsSeries(supertrendDown, true); if (!setSeriesResult) Print("Error: Failed to set series for supertrendDown array."); setSeriesResult = ArraySetAsSeries(TrendDirection, true); if (!setSeriesResult) Print("Error: Failed to set series for TrendDirection array."); setSeriesResult = ArraySetAsSeries(trend, true); if (!setSeriesResult) Print("Error: Failed to set series for trend array."); setSeriesResult = ArraySetAsSeries(highPrices, true); if (!setSeriesResult) Print("Error: Failed to set series for highPrices array."); setSeriesResult = ArraySetAsSeries(lowPrices, true); if (!setSeriesResult) Print("Error: Failed to set series for lowPrices array."); supertrendHandle = iATR(Symbol(), Period(), supertrendATRPeriod); // Allocate memory for arrays bool arrayResult = true; arrayResult = arrayResult && ArraySetAsSeries(highPrices, true); if (!arrayResult) Print("Error: Failed to resize highPrices array."); arrayResult = arrayResult && ArraySetAsSeries(lowPrices, true); if (!arrayResult) Print("Error: Failed to resize lowPrices array."); arrayResult = arrayResult && ArraySetAsSeries(Trend, true); if (!arrayResult) Print("Error: Failed to resize Trend array."); arrayResult = arrayResult && ArraySetAsSeries(TrendColor, true); if (!arrayResult) Print("Error: Failed to resize TrendColor array."); arrayResult = arrayResult && ArraySetAsSeries(supertrendUp, true); if (!arrayResult) Print("Error: Failed to resize supertrendUp array."); arrayResult = arrayResult && ArraySetAsSeries(supertrendDown, true); if (!arrayResult) Print("Error: Failed to resize supertrendDown array."); arrayResult = arrayResult && ArraySetAsSeries(trend, true); if (!arrayResult) Print("Error: Failed to resize trend array."); arrayResult = arrayResult && ArraySetAsSeries(TrendDirection, true); if (!arrayResult) Print("Error: Failed to resize TrendDirection array."); arrayResult = arrayResult && ArraySetAsSeries(highPrices, true); if (!arrayResult) Print("Error: Failed to resize highPrices array."); arrayResult = arrayResult && ArraySetAsSeries(lowPrices, true); if (!arrayResult) Print("Error: Failed to resize lowPrices array."); if (!arrayResult) { Print("Initialization failed due to array resizing issues."); return(INIT_FAILED); } // Configure trade settings trade.SetExpertMagicNumber(MagicNumber); trade.SetTypeFilling(ORDER_FILLING_RETURN); trade.SetTypeFillingBySymbol(Symbol()); LogDebugInfo("Initialization complete."); // Set log configs CLogger::SetLevels(LOG_LEVEL_INFO, LOG_LEVEL_FATAL); CLogger::SetLoggingMethod(LOGGING_OUTPUT_METHOD_EXTERN_FILE); CLogger::SetLogFileName(MQLInfoString(MQL_PROGRAM_NAME)); CLogger::SetLogFileLimitType(LOG_FILE_LIMIT_TYPE_ONE_DAY); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialisation function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { // ArraySetAsSeries reversal ArraySetAsSeries(highPrices, false); ArraySetAsSeries(lowPrices, false); ArraySetAsSeries(Trend, false); ArraySetAsSeries(TrendColor, false); ArraySetAsSeries(supertrendUp, false); ArraySetAsSeries(supertrendDown, false); ArraySetAsSeries(trend, false); ArraySetAsSeries(TrendDirection, false); // Log Deinit message LogDebugInfo("Deinitialization complete."); } //+------------------------------------------------------------------+ //| OnTick | //+------------------------------------------------------------------+ void OnTick() { // Check if it's a trading day and time if (ItsTradingTime() && BullishTradeConditions()) { // Execute OptimumTradeConditions if conditions apply if (OptimumTradeConditions()) { // Execute PlaceOrders PlaceOrders(); } } // Check pending order timeout threshold EATimeoutClose(); // Check for consecutive losses and disable EA if needed MaxLossEAOff(); } //+------------------------------------------------------------------+ //| Tester function | //+------------------------------------------------------------------+ double OnTester() { double param = 0.0; double balance = TesterStatistics(STAT_PROFIT); if(balance<=0) { param=0; return(param); } double sharpratio = TesterStatistics(STAT_SHARPE_RATIO); if(sharpratio<1) { param=0; return(param); } double profitfactor = TesterStatistics(STAT_PROFIT_FACTOR); if(profitfactor<1) { param=0; return(param); } double min_dd = TesterStatistics(STAT_BALANCE_DD); if(min_dd > 0.0) { min_dd = 1.0 / min_dd; } double trades_number = TesterStatistics(STAT_TRADES); if(trades_number<=1) { param=0; return(param); } param = (balance * trades_number *min_dd*sharpratio*profitfactor)/10000 ; return(param); } //+------------------------------------------------------------------+ //| Logging | //+------------------------------------------------------------------+ void LogDebugInfo(string message) { if(EnableDebugLogging) { Print("EA: ", message); } } // Function to log trade information void LogTradeInfo(string message) { // Log trade details, entry/exit reasons, P&L, etc. Print("Trade Info: ", message); } //+------------------------------------------------------------------+