Summary: EuroStable EA is a low-risk MQL4 expert advisor for EURUSD. It uses Bollinger Bands mean reversion with RSI filter and strict money management. Suitable for M15 timeframe.




EuroStable EA is designed specifically for EURUSD with a low-risk, stable operational approach. It uses Bollinger Bands (20,2) to identify mean reversion opportunities when price touches the outer band and RSI (14) confirms non-extreme conditions. Each trade has a fixed stop loss and take profit, with a daily equity protection and maximum spread filter. The EA trades only one position at a time and avoids news periods using a simple time filter.

Recommended Timeframe: M15
Trading Logic:
  • Bollinger Bands (20,2): Price touches upper band -> potential sell; touches lower band -> potential buy.

  • RSI(14) confirmation: For buy, RSI < 70; for sell, RSI > 30 (avoid overbought/oversold extremes).

  • Entry: Wait for price to close beyond the band, then enter on next bar open.

  • Risk Control: Fixed SL 25 pips, TP 40 pips. Max 1 trade at a time, daily loss limit 4%, max spread 20 points.


  • ``mql4
    //+------------------------------------------------------------------+
    //| EuroStableEA.mq4 |
    //| |
    //| |
    //+------------------------------------------------------------------+
    #property copyright ""
    #property link ""
    #property version "1.00"
    #property strict

    //--- input parameters with comments
    input double LotSize = 0.01; // Fixed lot size (0.01 for EURUSD ~$1 per pip)
    input int StopLossPips = 25; // Stop loss in pips (25 pips)
    input int TakeProfitPips = 40; // Take profit in pips (risk-reward 1:1.6)
    input int BandsPeriod = 20; // Bollinger Bands period
    input double BandsDeviation = 2.0; // Bollinger Bands deviation multiplier
    input int RSIPeriod = 14; // RSI period
    input int RSIUpperThreshold= 70; // RSI upper threshold (sell signal below this when at upper band)
    input int RSILowerThreshold= 30; // RSI lower threshold (buy signal above this when at lower band)
    input int MagicNumber = 202412; // Unique EA identifier
    input int MaxSpread = 20; // Maximum allowed spread (in points)
    input double DailyLossLimit = 4.0; // Daily loss limit in percentage of balance (4%)
    input bool UseTimeFilter = true; // Filter out high impact news times (13:30-15:30 GMT)
    input int StartHourBlock = 13; // Block start hour (GMT)
    input int EndHourBlock = 16; // Block end hour (GMT)

    //--- global variables
    double dailyStartBalance = 0;
    datetime lastBarTime = 0;

    //+------------------------------------------------------------------+
    //| Expert initialization function |
    //+------------------------------------------------------------------+
    int OnInit()
    {
    dailyStartBalance = AccountBalance();
    lastBarTime = 0;
    return(INIT_SUCCEEDED);
    }

    //+------------------------------------------------------------------+
    //| Expert deinitialization function |
    //+------------------------------------------------------------------+
    void OnDeinit(const int reason)
    {
    Comment("");
    }

    //+------------------------------------------------------------------+
    //| Expert tick function |
    //+------------------------------------------------------------------+
    void OnTick()
    {
    // Daily equity protection
    double currentEquity = AccountEquity();
    double lossPercent = (dailyStartBalance - currentEquity) / dailyStartBalance 100;
    if(lossPercent >= DailyLossLimit)
    {
    Comment("Daily loss limit reached. No new trades.");
    return;
    }

    // Spread filter
    if(MarketInfo(Symbol(), MODE_SPREAD) > MaxSpread)
    {
    Comment("Spread too high: ", MarketInfo(Symbol(), MODE_SPREAD));
    return;
    }

    // Time filter (avoid news)
    if(UseTimeFilter)
    {
    datetime currentTime = TimeCurrent();
    int currentHour = TimeHour(currentTime);
    if(currentHour >= StartHourBlock && currentHour < EndHourBlock)
    {
    Comment("News time block. No new trades.");
    return;
    }
    }

    // New bar logic (M15)
    if(Time[0] == lastBarTime)
    return;
    lastBarTime = Time[0];

    // Check for existing position
    if(CountPositions() > 0)
    return;

    // Get Bollinger Bands values on completed bar (index 1)
    double upperBand = iBands(Symbol(), PERIOD_M15, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_UPPER, 1);
    double lowerBand = iBands(Symbol(), PERIOD_M15, BandsPeriod, BandsDeviation, 0, PRICE_CLOSE, MODE_LOWER, 1);
    double close1 = iClose(Symbol(), PERIOD_M15, 1);

    // Get RSI value on completed bar
    double rsi = iRSI(Symbol(), PERIOD_M15, RSIPeriod, PRICE_CLOSE, 1);

    // Entry conditions
    bool buySignal = false;
    bool sellSignal = false;

    // Price touches or closes below lower band -> potential buy
    if(close1 <= lowerBand && rsi > RSILowerThreshold)
    {
    buySignal = true;
    }
    // Price touches or closes above upper band -> potential sell
    else if(close1 >= upperBand && rsi < RSIUpperThreshold)
    {
    sellSignal = true;
    }

    double sl = 0, tp = 0;
    int cmd = -1;

    if(buySignal)
    {
    cmd = OP_BUY;
    sl = SymbolInfoDouble(Symbol(), SYMBOL_BID) - StopLossPips
    Point 10; // 10 points per pip
    tp = SymbolInfoDouble(Symbol(), SYMBOL_BID) + TakeProfitPips
    Point 10;
    }
    else if(sellSignal)
    {
    cmd = OP_SELL;
    sl = SymbolInfoDouble(Symbol(), SYMBOL_ASK) + StopLossPips
    Point 10;
    tp = SymbolInfoDouble(Symbol(), SYMBOL_ASK) - TakeProfitPips
    Point * 10;
    }

    if(cmd != -1)
    {
    int ticket = OrderSend(Symbol(), cmd, LotSize, (cmd==OP_BUY?Ask:Bid), 3, sl, tp, "EuroStable EA", MagicNumber, 0, clrNONE);
    if(ticket < 0)
    Print("OrderSend failed: ", GetLastError());
    }
    }

    //+------------------------------------------------------------------+
    //| Count open positions with this MagicNumber |
    //+------------------------------------------------------------------+
    int CountPositions()
    {
    int count = 0;
    for(int i = OrdersTotal()-1; i >= 0; i--)
    {
    if(OrderSelect(i, SELECT_BY_POS, MODE_TRADES))
    {
    if(OrderSymbol() == Symbol() && OrderMagicNumber() == MagicNumber)
    count++;
    }
    }
    return count;
    }
    //+------------------------------------------------------------------+
    ``
    Reference: Original MQL4 code for educational purposes.
    Disclaimer: Trading Forex involves substantial risk of loss. This EA is provided as-is without any guarantee. Test thoroughly on a demo account before live trading. Past performance does not guarantee future results.