//+------------------------------------------------------------------+ //| Bot10001.mq5 | //| Copyright 2024, MetaQuotes Ltd. | //| https://www.abbeyroadtech.com | //+------------------------------------------------------------------+ #property copyright "Copyright 2024, MetaQuotes Ltd." #property link "https://www.abbeyroadtech.com" #property version "1.00" #property strict #include CTrade trade; //--- Trading Parameters // Removed fixed lot size input in favor of risk-based volume calculation // input double LotSize = 0.1; // (Removed) //--- Risk management: risk per trade as a percentage of account equity // Note: A value of 0.01 means 0.01% of the equity. input double RiskPerTrade = 0.5; input int StopLossPips = 150; // Stop loss in pips input int TakeProfitPips = 300; // Take profit in pips input int Slippage = 10; // Allowed slippage in points //--- Input parameters for Moving Averages input int FastMAPeriod = 10; // Fast Moving Average period input int SlowMAPeriod = 20; // Slow Moving Average period //--- Input parameters for Trailing Stop input int TrailingStart = 50; // Profit in points to start trailing input int TrailingDistance = 30; // Distance to maintain from current price input int TrailingStep = 10; // Step in points to move the stop loss // (RiskPerTrade already declared above) input long MagicNumber = 98765; // Magic number for this EA /***** Day-of-Week Logic *****/ input bool TradeMonday = true; input bool TradeTuesday = true; input bool TradeWednesday = true; input bool TradeThursday = true; input bool TradeFriday = true; input bool TradeSaturday = false; input bool TradeSunday = false; /************** RSI Tuning ****************/ input int RSIOversold = 30; //RSI over bought input int RSIOverbought = 70; //RSI over sold //--- Pin Bar Filter input bool UsePinBarFilter = true; //--- Control for Previous Day Trade Restriction input bool RequirePreviousDayTrade = true; //--- ATR-Based Stop Loss input bool UseATRStopLoss = true; // Enable ATR-Based Stop-Loss input bool UseATRTakeProfit = true; input int ATRMultiplier = 2; // ATR Multiplier for Stop-Loss input int ATRPeriod = 14; // ATR Period for Calculation //--- Global variables (Pivot Points) static datetime TradeDate = 0; // Tracks the current day static bool OrdersPlaced = false; /***** For multiple-trade trailing: track best price per position ticket *****/ /** We'll keep two parallel static arrays: - posTicketArray[] holds position tickets - bestPriceArray[] holds the best price so far for that ticket (highest for BUY, lowest for SELL). We only store up to some arbitrary max positions (e.g. 100). Each time we see a new position, we add if not found. Each time a position is closed, we remove it from the arrays. */ #define MAX_POSITIONS 100 static ulong posTicketArray[MAX_POSITIONS]; static double bestPriceArray[MAX_POSITIONS]; static int posCount = 0; // How many valid entries we have bool IsTradingDay(); //+------------------------------------------------------------------+ //| Expert initialization function | //+------------------------------------------------------------------+ int OnInit() { Print("Expert Advisor initialized (no Fibonacci)."); return(INIT_SUCCEEDED); } //+------------------------------------------------------------------+ //| Expert deinitialization function | //+------------------------------------------------------------------+ void OnDeinit(const int reason) { Print("Expert Advisor deinitialized."); } //+------------------------------------------------------------------+ //| Expert tick function | //+------------------------------------------------------------------+ void OnTick() { //--- Calculate Moving Averages (MQL5 style) double FastMA = iMA(_Symbol, PERIOD_CURRENT, FastMAPeriod, 0, MODE_SMA, PRICE_CLOSE); double SlowMA = iMA(_Symbol, PERIOD_CURRENT, SlowMAPeriod, 0, MODE_SMA, PRICE_CLOSE); //--- Get current market prices double currentClose = iClose(_Symbol, PERIOD_CURRENT, 0); MqlTick lastTick; SymbolInfoTick(_Symbol, lastTick); //--- Execute trading logic ExecuteTradingLogic(FastMA, SlowMA, currentClose, lastTick ); ApplyTrailingStop(); } //+------------------------------------------------------------------+ //| Calculate RSI | //+------------------------------------------------------------------+ bool IsRSIOversold() { int RSIPeriod = 14; double rsi[]; if(CopyBuffer(iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE), 0, 0, 1, rsi) <= 0) { Print("Failed to retrieve RSI values"); return false; } return rsi[0] < RSIOversold; } bool IsRSIOverbought() { int RSIPeriod = 14; double rsi[]; if(CopyBuffer(iRSI(_Symbol, PERIOD_CURRENT, RSIPeriod, PRICE_CLOSE), 0, 0, 1, rsi) <= 0) { Print("Failed to retrieve RSI values"); return false; } return rsi[0] > RSIOverbought; } //+------------------------------------------------------------------+ //| Calculate ATR-Based Stop Loss | //+------------------------------------------------------------------+ double CalculateATRStopLoss() { if (!UseATRStopLoss) return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Use Fixed SL if ATR is disabled int atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod); if (atrHandle == INVALID_HANDLE) { Print("Failed to create ATR handle"); return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Fallback to Fixed SL } double atrBuffer[1]; if (CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0) { Print("Failed to retrieve ATR values"); return StopLossPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Fallback to Fixed SL } IndicatorRelease(atrHandle); return atrBuffer[0] * ATRMultiplier; } //--- ATR-Based Take Profit Calculation //+------------------------------------------------------------------+ double CalculateATRTakeProfit() { if (!UseATRTakeProfit) return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); int atrHandle = iATR(_Symbol, PERIOD_CURRENT, ATRPeriod); if (atrHandle == INVALID_HANDLE) return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); double atrBuffer[1]; if (CopyBuffer(atrHandle, 0, 0, 1, atrBuffer) <= 0) return TakeProfitPips * SymbolInfoDouble(_Symbol, SYMBOL_POINT); IndicatorRelease(atrHandle); return atrBuffer[0] * ATRMultiplier; } //+------------------------------------------------------------------+ //| Ensure Stop Loss and Take Profit meet broker constraints | //+------------------------------------------------------------------+ void ValidateStopLevels(double &stopLoss, double &takeProfit, double entryPrice, ENUM_ORDER_TYPE orderType) { double minStopLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL) * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if (minStopLevel == 0) minStopLevel = SymbolInfoDouble(_Symbol, SYMBOL_POINT) * 10; // Default fallback if (orderType == ORDER_TYPE_BUY) { if (stopLoss > 0 && (entryPrice - stopLoss) < minStopLevel) stopLoss = entryPrice - minStopLevel; if (takeProfit > 0 && (takeProfit - entryPrice) < minStopLevel) takeProfit = entryPrice + minStopLevel; } else if (orderType == ORDER_TYPE_SELL) { if (stopLoss > 0 && (stopLoss - entryPrice) < minStopLevel) stopLoss = entryPrice + minStopLevel; if (takeProfit > 0 && (entryPrice - takeProfit) < minStopLevel) takeProfit = entryPrice - minStopLevel; } // Ensure stopLoss and takeProfit are not equal to entryPrice if (stopLoss == entryPrice) stopLoss += minStopLevel; if (takeProfit == entryPrice) takeProfit -= minStopLevel; } //+------------------------------------------------------------------+ //| Enhanced Trading Logic with ATR Stop-Loss, RSI Filter, and Stop Validation | //+------------------------------------------------------------------+ void ExecuteTradingLogic(double fastMA, double slowMA, double closePrice, MqlTick &tick ) { static datetime lastBar = 0; datetime currentBar = iTime(_Symbol, PERIOD_CURRENT, 0); // Only check on new bar formation if(lastBar == currentBar) return; lastBar = currentBar; //bool rsiBuyCondition = IsRSIOversold(); //bool rsiSellCondition = IsRSIOverbought(); double stopLoss = CalculateATRStopLoss(); // Get dynamic stop-loss; double takeProfit = CalculateATRTakeProfit(); // Get candle data double openPrice = iOpen(_Symbol, PERIOD_CURRENT, 1); double pclosePrice = iClose(_Symbol, PERIOD_CURRENT, 1); double highPrice = iHigh(_Symbol, PERIOD_CURRENT, 1); double lowPrice = iLow(_Symbol, PERIOD_CURRENT, 1); bool isPinedBar = IsPinBar(openPrice, pclosePrice,highPrice, lowPrice); bool isBearishPinBar = IsBearishPinBar(openPrice, pclosePrice,highPrice, lowPrice); bool isBullishPinBar = IsBullishPinBar(openPrice, pclosePrice,highPrice, lowPrice); bool isDoji = IsDoji(openPrice, pclosePrice,highPrice, lowPrice); bool isStrongBullishCandle = IsStrongBullishCandle(openPrice, pclosePrice,highPrice, lowPrice); bool isStrongBearishCandle = IsStrongBearishCandle(openPrice, pclosePrice,highPrice, lowPrice); bool maBuyCondition = (fastMA > slowMA) && (PositionSelect(_Symbol) == false); if( maBuyCondition && ( isStrongBullishCandle) && !IsRSIOverbought() && !IsTradeOpenForSymbol() ) { double tpPrice = closePrice + takeProfit; double slPrice = closePrice - takeProfit; double lot = CalculateLotSizeFromRisk(closePrice, closePrice - slPrice); //ValidateStopLevels(slPrice, tpPrice, closePrice, ORDER_TYPE_BUY); ExecuteBuyOrder(lot, closePrice, slPrice, tpPrice, Slippage); } bool maSellCondition = (fastMA < slowMA) && (PositionSelect(_Symbol) == false); if( maSellCondition && ( isStrongBearishCandle) && !IsRSIOversold() && !IsTradeOpenForSymbol() ) { double tpPrice = closePrice - takeProfit; double slPrice = closePrice + takeProfit; double lot = CalculateLotSizeFromRisk(closePrice, closePrice + slPrice); //ValidateStopLevels(slPrice, tpPrice, closePrice, ORDER_TYPE_SELL); ExecuteSellOrder(lot, closePrice, slPrice, tpPrice, Slippage); } } //+------------------------------------------------------------------+ //| Check if an open trade is from the previous day | //+------------------------------------------------------------------+ bool IsPreviousDayTradeOpen() { datetime currentTime = TimeCurrent(); MqlDateTime nowStruct; TimeToStruct(currentTime, nowStruct); for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { datetime openTime = PositionGetInteger(POSITION_TIME); MqlDateTime openStruct; TimeToStruct(openTime, openStruct); if(openStruct.day != nowStruct.day) return true; } } return false; } //+------------------------------------------------------------------+ //| Check if there are any open trades for the current symbol | //+------------------------------------------------------------------+ bool IsTradeOpenForSymbol() { for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) return true; } return false; } //+------------------------------------------------------------------+ //| Candle Pattern Detection | //+------------------------------------------------------------------+ bool IsBullishPinBar(double open, double close, double high, double low) { double body = MathAbs(open - close); double lowerWick = MathMin(open, close) - low; return (lowerWick > body * 2) && body < (high - low) * 0.3; } bool IsBearishPinBar(double open, double close, double high, double low) { double body = MathAbs(open - close); double upperWick = high - MathMax(open, close); return (upperWick > body * 2) && body < (high - low) * 0.3; } bool IsDoji(double open, double close, double high, double low) { double body = MathAbs(open - close); return (body < (high - low) * 0.1); } bool IsStrongBullishCandle(double open, double close, double high, double low) { double body = MathAbs(open - close); return (close > open && body > (high - low) * 0.6); } bool IsStrongBearishCandle(double open, double close, double high, double low) { double body = MathAbs(open - close); return (open > close && body > (high - low) * 0.6); } bool IsPinBar(double open, double close, double high, double low) { double body = MathAbs(open - close); double upperWick = high - MathMax(open, close); double lowerWick = MathMin(open, close) - low; return (upperWick > body * 2 || lowerWick > body * 2) && body < (high - low) * 0.3; } double GetVolumeProfileLevel(ENUM_TIMEFRAMES timeframe, int lookbackPeriod) { // Calculate the start time for the lookback period datetime start_time = TimeCurrent() - lookbackPeriod * PeriodSeconds(timeframe); double total_volume = 0; // Total volume over the lookback period double price_level = 0; // Weighted sum of price levels for(int i = 0; i < lookbackPeriod; i++) { // Get the volume and closing price for the current bar double bar_volume = iVolume(_Symbol, timeframe, i); double bar_price = iClose(_Symbol, timeframe, i); // Add to the total volume and weighted price level total_volume += bar_volume; price_level += bar_volume * bar_price; } // Avoid division by zero if(total_volume == 0) { Print("Error: Total volume is zero. Cannot calculate Point of Control (POC)."); return 0; // Return 0 or handle the error as needed } // Calculate and return the Point of Control (POC) return price_level / total_volume; } //--- Input parameters for ADX input int ADXPeriod = 14; // Period for ADX calculation // Function to calculate ADX double GetADX(ENUM_TIMEFRAMES timeframe) { int adx_handle = iADX(_Symbol, timeframe, ADXPeriod); if(adx_handle == INVALID_HANDLE) { Print("Error creating ADX handle: ", GetLastError()); return 0; } double adx_value[1]; // Array to store the ADX value if(CopyBuffer(adx_handle, 0, 0, 1, adx_value) <= 0) // Copy the main ADX line { Print("Error copying ADX buffer: ", GetLastError()); return 0; } return adx_value[0]; // Return the latest ADX value } double CalculateDynamicTakeProfit(double atrValue, double multiplier) { return atrValue * multiplier * SymbolInfoDouble(_Symbol, SYMBOL_POINT); } //+------------------------------------------------------------------+ //| Calculate lot size based on risk from entry to stop-loss | //+------------------------------------------------------------------+ double CalculateLotSizeFromRisk(double entryPrice, double stopLoss) { // Calculate the risk money (a percentage of account equity) double riskMoney = AccountInfoDouble(ACCOUNT_EQUITY) * (RiskPerTrade / 100.0); // Calculate the risk in points (using the symbol’s point size) double riskPoints = MathAbs(entryPrice - stopLoss) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Obtain tick value and tick size to determine pip value per lot. double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE); double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE); // Here we assume one pip equals one Symbol POINT in value terms. double pipValue = (tickValue / tickSize) * SymbolInfoDouble(_Symbol, SYMBOL_POINT); // Calculate volume so that: riskMoney = lot * (riskPoints * pipValue) double lot = riskMoney / (riskPoints * pipValue); return NormalizeDouble(lot, 2); } //+------------------------------------------------------------------+ //| Order Execution Helpers | //+------------------------------------------------------------------+ void ExecuteBuyOrder(double volume, double entryPrice, double stopLoss, double takeProfit, double slippage) { MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); // Set up the trade request request.action = TRADE_ACTION_DEAL; // Immediate order execution request.symbol = _Symbol; // Current symbol request.volume = volume; // Calculated volume based on risk request.type = ORDER_TYPE_BUY; // Buy order request.price = entryPrice; // Entry price request.sl = stopLoss; // Stop loss request.tp = takeProfit; // Take profit request.deviation = (uint)slippage; // Allowed slippage request.magic = MagicNumber; // Use the magic number // Send the order if(!OrderSend(request, result)) Print("Buy Order Failed: ", GetLastError()); else Print("Buy Order Executed Successfully"); } void ExecuteSellOrder(double volume, double entryPrice, double stopLoss, double takeProfit, double slippage) { MqlTradeRequest request; MqlTradeResult result; ZeroMemory(request); ZeroMemory(result); // Set up the trade request request.action = TRADE_ACTION_DEAL; // Immediate order execution request.symbol = _Symbol; // Current symbol request.volume = volume; // Calculated volume based on risk request.type = ORDER_TYPE_SELL; // Sell order request.price = entryPrice; // Entry price request.sl = stopLoss; // Stop loss request.tp = takeProfit; // Take profit request.deviation = (uint)slippage; // Allowed slippage request.magic = MagicNumber; // Magic number // Send the order if(!OrderSend(request, result)) Print("Sell Order Failed: ", GetLastError()); else Print("Sell Order Executed Successfully"); } //+------------------------------------------------------------------+ //| Apply trailing stop to open positions | //+------------------------------------------------------------------+ void ApplyTrailingStop() { double bidPrice, askPrice; MqlTick lastTick; if(SymbolInfoTick(_Symbol, lastTick)) { bidPrice = lastTick.bid; askPrice = lastTick.ask; } for(int i = PositionsTotal() - 1; i >= 0; i--) { if(PositionGetSymbol(i) == _Symbol) { double openPrice = PositionGetDouble(POSITION_PRICE_OPEN); double stopLoss = PositionGetDouble(POSITION_SL); double takeProfit = PositionGetDouble(POSITION_TP); double currentPrice= (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ? bidPrice : askPrice; double profitPoints= (currentPrice - openPrice) / SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) profitPoints = -profitPoints; if(profitPoints >= TrailingStart) { double newStopLoss = currentPrice - TrailingDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if(PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL) newStopLoss = currentPrice + TrailingDistance * SymbolInfoDouble(_Symbol, SYMBOL_POINT); if((PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY && newStopLoss > stopLoss + TrailingStep * SymbolInfoDouble(_Symbol, SYMBOL_POINT)) || (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_SELL && newStopLoss < stopLoss - TrailingStep * SymbolInfoDouble(_Symbol, SYMBOL_POINT))) { trade.PositionModify(PositionGetInteger(POSITION_TICKET), newStopLoss, takeProfit); } } } } } //+------------------------------------------------------------------+ /*****===================================================================== * Cleanup any closed positions from the best price array *======================================================================*****/ void CleanUpClosedPositions() { // Loop through our array of known tickets and remove closed ones for(int i = posCount - 1; i >= 0; i--) { ulong storedTicket = posTicketArray[i]; bool found = false; uint totalPos = PositionsTotal(); for(uint p = 0; p < totalPos; p++) { if(PositionGetTicket(p) == storedTicket) { found = true; break; } } if(!found) { for(int j = i; j < posCount - 1; j++) { posTicketArray[j] = posTicketArray[j+1]; bestPriceArray[j] = bestPriceArray[j+1]; } posCount--; } } } /*****===================================================================== * IsTradingDay() *======================================================================*****/ bool IsTradingDay() { MqlDateTime mt; TimeToStruct(TimeCurrent(), mt); int dow = mt.day_of_week; // 0=Sun,1=Mon,...,6=Sat switch(dow) { case 0: return TradeSunday; case 1: return TradeMonday; case 2: return TradeTuesday; case 3: return TradeWednesday; case 4: return TradeThursday; case 5: return TradeFriday; case 6: return TradeSaturday; } return false; } //+------------------------------------------------------------------+