20-Day High Breakout + RSI Strategy for MetaTrader 5:
Working MQL5 Code + Backtest

This strategy enters long when NAS100 breaks above its 20-day high with RSI(14) above 50 as a momentum confirmation. The 20-day high breakout catches the start of new trending moves, while the RSI filter eliminates breakouts that lack underlying momentum. Exit occurs when price closes below the 10-day low (a faster exit channel) or RSI drops below 40. The asymmetric entry/exit channels (20-day entry, 10-day exit) allow trends to develop while providing relatively quick exits when momentum fades.

20-Day High Breakout + RSIMQL5NAS100

What is Donchian Channel Breakout with RSI Momentum Filter?

The 20-day high breakout, derived from the Donchian Channel concept, identifies new highs in price as a signal of emerging momentum. A stock or index making new 20-day highs is demonstrating strength, particularly when confirmed by RSI above 50 (indicating positive momentum). This combination of price breakout and momentum filtering was popularized by the Turtle Traders and remains one of the most robust trend-following signals. The RSI filter reduces false breakouts that occur during low-momentum, range-bound conditions.

ParameterValue
breakout HighHighest high of last 20 bars
rsi Filter50
breakout LowLowest low of last 20 bars

Nasdaq 20-Day Breakout Momentum: The Setup

Entry Rules

  • Price closes above the highest high of the last 20 bars (Donchian breakout)
  • RSI(14) is above 50 (momentum confirmation)
  • Enter long on the next bar open after both conditions are met
  • Position size: 1 lot per trade

Exit Rules

  • Exit when price closes below the lowest low of the last 10 bars
  • Exit when RSI(14) drops below 40
  • Whichever condition triggers first closes the position

Working MQL5 Code

The MQL5 Expert Advisor runs on each new daily bar (detected by comparing `iTime()` timestamps). The RSI is created as an indicator handle in `OnInit()` and values are read via `CopyBuffer()`. The breakout channel is calculated by looping through the last 20 completed bars to find the highest high, and the exit channel loops through the last 10 bars for the lowest low. Entry triggers when the previous close exceeds the 20-day high and RSI is above 50. Exit triggers when the previous close falls below the 10-day low or RSI drops below 40. The `CTrade` class handles order execution with a magic number for identification.

cpp
#property copyright "Spenddock Strategy"
#property link      "https://spenddock.com"
#property version   "1.00"
#property strict

#include <Trade\Trade.mqh>

// === INPUTS ===
input int    BreakoutPeriod = 20;       // Breakout lookback period
input int    ExitPeriod     = 10;       // Exit channel lookback period
input int    RSIPeriod      = 14;       // RSI period
input double RSIEntryLevel  = 50.0;    // RSI minimum for entry
input double RSIExitLevel   = 40.0;    // RSI level for exit
input double LotSize        = 1.0;     // Trade lot size
input int    MagicNumber    = 123456;  // Expert Advisor magic number

// === GLOBALS ===
CTrade trade;
int rsiHandle;

int OnInit()
{
    trade.SetExpertMagicNumber(MagicNumber);
    rsiHandle = iRSI(_Symbol, PERIOD_D1, RSIPeriod, PRICE_CLOSE);
    if(rsiHandle == INVALID_HANDLE)
    {
        Print("Failed to create RSI indicator handle");
        return(INIT_FAILED);
    }
    return(INIT_SUCCEEDED);
}

void OnDeinit(const int reason)
{
    if(rsiHandle != INVALID_HANDLE)
        IndicatorRelease(rsiHandle);
}

void OnTick()
{
    // Only run on new bar
    static datetime lastBar = 0;
    datetime currentBar = iTime(_Symbol, PERIOD_D1, 0);
    if(currentBar == lastBar) return;
    lastBar = currentBar;

    // Get RSI value
    double rsiBuffer[];
    ArraySetAsSeries(rsiBuffer, true);
    if(CopyBuffer(rsiHandle, 0, 1, 1, rsiBuffer) <= 0) return;
    double rsiValue = rsiBuffer[0];

    // Calculate breakout channel (using previous completed bars)
    double highestHigh = 0;
    double lowestLow = DBL_MAX;
    double exitLow = DBL_MAX;

    for(int i = 1; i <= BreakoutPeriod; i++)
    {
        double h = iHigh(_Symbol, PERIOD_D1, i);
        if(h > highestHigh) highestHigh = h;
    }

    for(int i = 1; i <= ExitPeriod; i++)
    {
        double l = iLow(_Symbol, PERIOD_D1, i);
        if(l < exitLow) exitLow = l;
    }

    double prevClose = iClose(_Symbol, PERIOD_D1, 1);
    bool hasPosition = PositionSelect(_Symbol);

    // === ENTRY LOGIC ===
    if(!hasPosition)
    {
        if(prevClose > highestHigh && rsiValue > RSIEntryLevel)
        {
            trade.Buy(LotSize, _Symbol, 0, 0, 0, "Breakout Entry");
        }
    }
    // === EXIT LOGIC ===
    else
    {
        if(prevClose < exitLow || rsiValue < RSIExitLevel)
        {
            trade.PositionClose(_Symbol);
        }
    }
}

Backtest Results: NAS100 (1D, 2020-2025)

Total Return

+35.1%

Annualized

+6.2%

Win Rate

44%

Max Drawdown

-22.3%

Sharpe Ratio

0.68

Total Trades

56

Profit Factor

1.28

Past performance does not guarantee future results. Backtest results may not reflect actual trading conditions including slippage, commissions, and liquidity constraints.

Optimization Tips

1

Add an ATR-based position sizing filter. When ATR(14) is high (above its 50-period average), reduce position size to 0.5 lots. This normalizes risk across different volatility regimes and reduces the impact of volatile false breakouts.

2

Test different exit channel lengths. The 10-day exit is relatively quick. For stronger trends on NAS100, try a 15-day exit channel to allow more room for the trend to develop, at the cost of giving back more on reversals.

3

Implement a breakout confirmation delay. Instead of entering immediately on the breakout bar, wait for a second consecutive close above the 20-day high. This filters out single-bar spikes that quickly reverse.

4

Add a VIX filter for Nasdaq breakouts. When VIX is above 30, breakouts are more likely to fail due to high uncertainty. Require VIX < 25 as an additional entry condition using `request.security("CBOE:VIX", ...)` on TradingView or an equivalent data feed in MT5.

5

Consider a time-of-week filter. Nasdaq breakouts on Mondays and Fridays tend to have lower follow-through than mid-week breakouts. Add day-of-week filtering to improve win rate.

Common Mistakes with 20-Day High Breakout + RSI on MetaTrader 5

Expecting a high win rate from a breakout strategy. The 44% win rate is normal for trend-following systems — profitability comes from winning trades being much larger than losing trades (reflected in the 1.28 profit factor). Do not abandon the strategy after a string of small losses.

Not accounting for spread and slippage on NAS100. Nasdaq index CFDs often have wider spreads during US market open. Breakout entries at market open can suffer significant slippage. Consider using limit orders slightly above the breakout level.

Using the same breakout period for all market regimes. In low-volatility consolidation phases, 20-day breakouts are reliable. In high-volatility environments, consider extending to 30-day breakouts to avoid noise.

Forgetting to handle weekend gaps in MT5. NAS100 CFDs can gap significantly on Monday open. A Friday close above the 20-day high does not mean Monday's open will be at the same level. Consider adding gap filters or waiting for the gap to fill before entering.

Build This Strategy in 60 Seconds with SpendDock

Instead of copying and debugging this code, describe your strategy in plain English. SpendDock generates the MQL5 code, backtests it against real market data, and lets you iterate until it is right.

Try SpendDock Free