5-Minute Range Filter Trading Strategy
Master a powerful intraday strategy that filters market noise and identifies high-probability trading opportunities using range-based price action.
5-Minute Timeframe
Fast-paced scalping opportunities
Noise Filtration
Eliminate false breakouts
High Win Rate
Proven price action signals
Strategy Components
Understanding the 5-Minute Range Filter Strategy
The 5-Minute Range Filter Trading Strategy is a powerful scalping approach designed for intraday traders who want to capitalize on short-term price movements while avoiding the noise and false signals common in lower timeframes. This strategy combines a dynamic range filter with price action confirmation to identify high-probability trades during active market sessions.
What Is a Range Filter?
A range filter is a technical indicator that smooths price data by calculating a moving average that only updates when price moves beyond a specified range threshold. Unlike traditional moving averages that respond to every price tick, the range filter ignores minor fluctuations, making it ideal for filtering out market noise in choppy conditions.
Range Filter Calculation:
1. Calculate the Range Size:
2. Update the Filter Line:
- If price moves above (Filter + Range), update Filter upward
- If price moves below (Filter - Range), update Filter downward
- Otherwise, maintain the current Filter value
Strategy Components
1. Timeframe: 5-Minute Charts
The 5-minute timeframe strikes an optimal balance between trade frequency and signal reliability. It's fast enough for intraday scalping but slow enough to filter out the excessive noise found in 1-minute charts. This timeframe is ideal during high-liquidity sessions (London and New York overlaps).
2. Range Filter Indicator
The core indicator uses ATR (Average True Range) with a 14-period setting and a 2.0 multiplier. When price closes above the filter line, it signals upward momentum; when price closes below, it signals downward momentum. The filter line acts as dynamic support and resistance.
Settings: ATR Period = 14, Range Multiplier = 2.0 (can be adjusted between 1.5-3.0 based on volatility preference)
3. Price Action Confirmation
Beyond the range filter signal, confirm entries with candlestick patterns. Look for bullish engulfing patterns or hammer candles for long entries, and bearish engulfing or shooting star patterns for short entries. This adds an extra layer of confirmation to filter trades.
4. Volume Analysis (Optional)
Incorporate volume indicators to validate breakouts. Higher-than-average volume on filter crossovers suggests genuine momentum, while low-volume signals may indicate false breakouts. Use a volume moving average to identify significant volume spikes.
Trading Rules
Long (Buy) Entry Criteria
- Price closes above the range filter line
- Filter line changes from red to green (uptrend confirmation)
- Bullish candlestick pattern forms (engulfing, hammer, or piercing)
- Optional: Volume exceeds 20-period moving average
- Enter at the open of the next candle
Stop Loss: Place below the recent swing low or 1.5× ATR below entry
Take Profit: Target 2:1 or 3:1 risk-reward ratio, or exit when price closes back below the filter line
Short (Sell) Entry Criteria
- Price closes below the range filter line
- Filter line changes from green to red (downtrend confirmation)
- Bearish candlestick pattern forms (engulfing, shooting star, or dark cloud)
- Optional: Volume exceeds 20-period moving average
- Enter at the open of the next candle
Stop Loss: Place above the recent swing high or 1.5× ATR above entry
Take Profit: Target 2:1 or 3:1 risk-reward ratio, or exit when price closes back above the filter line
MQL5 Implementation
Here's a complete MQL5 Expert Advisor implementing the 5-Minute Range Filter Strategy:
//+------------------------------------------------------------------+
//| RangeFilterScalper.mq5 |
//| 5-Minute Range Filter Trading Strategy |
//+------------------------------------------------------------------+
#property copyright "SmartFinanceData"
#property version "1.00"
#property strict
// Input Parameters
input int ATR_Period = 14; // ATR Period for Range Filter
input double Range_Multiplier = 2.0; // Range Multiplier
input double RiskPercent = 1.0; // Risk per trade (%)
input double RR_Ratio = 2.0; // Risk-Reward Ratio
input int Volume_MA_Period = 20; // Volume MA Period
input bool Use_Volume_Filter = true; // Use Volume Confirmation
// Global Variables
int atrHandle;
int volumeHandle;
double filterLine[];
double rangeSize[];
bool isUpTrend = false;
datetime lastBarTime = 0;
//+------------------------------------------------------------------+
//| Expert initialization function |
//+------------------------------------------------------------------+
int OnInit()
{
// Initialize ATR indicator
atrHandle = iATR(_Symbol, PERIOD_M5, ATR_Period);
if(atrHandle == INVALID_HANDLE)
{
Print("Failed to create ATR indicator");
return(INIT_FAILED);
}
// Initialize arrays
ArraySetAsSeries(filterLine, true);
ArraySetAsSeries(rangeSize, true);
Print("Range Filter Scalper EA initialized successfully");
return(INIT_SUCCEEDED);
}
//+------------------------------------------------------------------+
//| Expert deinitialization function |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
IndicatorRelease(atrHandle);
}
//+------------------------------------------------------------------+
//| Expert tick function |
//+------------------------------------------------------------------+
void OnTick()
{
// Check for new bar
if(lastBarTime == iTime(_Symbol, PERIOD_M5, 0))
return;
lastBarTime = iTime(_Symbol, PERIOD_M5, 0);
// Calculate Range Filter
CalculateRangeFilter();
// Check for existing positions
if(PositionsTotal() > 0)
{
ManageOpenPositions();
return;
}
// Look for entry signals
CheckForEntrySignals();
}
//+------------------------------------------------------------------+
//| Calculate Range Filter |
//+------------------------------------------------------------------+
void CalculateRangeFilter()
{
double atr[];
ArraySetAsSeries(atr, true);
if(CopyBuffer(atrHandle, 0, 0, 3, atr) <= 0)
return;
double range = atr[1] * Range_Multiplier;
double close1 = iClose(_Symbol, PERIOD_M5, 1);
double close2 = iClose(_Symbol, PERIOD_M5, 2);
// Initialize filter on first run
static double prevFilter = 0;
if(prevFilter == 0)
prevFilter = close2;
double newFilter = prevFilter;
// Update filter based on price movement
if(close1 > prevFilter + range)
{
newFilter = prevFilter + range;
isUpTrend = true;
}
else if(close1 < prevFilter - range)
{
newFilter = prevFilter - range;
isUpTrend = false;
}
ArrayResize(filterLine, ArraySize(filterLine) + 1);
ArrayResize(rangeSize, ArraySize(rangeSize) + 1);
filterLine[0] = newFilter;
rangeSize[0] = range;
prevFilter = newFilter;
}
//+------------------------------------------------------------------+
//| Check for entry signals |
//+------------------------------------------------------------------+
void CheckForEntrySignals()
{
if(ArraySize(filterLine) < 3)
return;
double close1 = iClose(_Symbol, PERIOD_M5, 1);
double close2 = iClose(_Symbol, PERIOD_M5, 2);
double open1 = iOpen(_Symbol, PERIOD_M5, 1);
double high1 = iHigh(_Symbol, PERIOD_M5, 1);
double low1 = iLow(_Symbol, PERIOD_M5, 1);
// Volume filter (optional)
bool volumeConfirm = true;
if(Use_Volume_Filter)
{
long volume1 = iVolume(_Symbol, PERIOD_M5, 1);
double avgVolume = 0;
for(int i = 1; i <= Volume_MA_Period; i++)
avgVolume += iVolume(_Symbol, PERIOD_M5, i);
avgVolume /= Volume_MA_Period;
volumeConfirm = (volume1 > avgVolume);
}
// Long Entry Signal
if(close1 > filterLine[1] && close2 <= filterLine[2] && volumeConfirm)
{
// Bullish candlestick patterns
bool bullishEngulfing = (close1 > open1 && open1 < close2 && close1 > open1);
bool hammer = ((high1 - close1) < (close1 - open1) * 0.3 &&
(close1 - low1) > (high1 - low1) * 0.6);
if(bullishEngulfing || hammer)
{
double sl = low1;
double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_ASK);
double tp = entryPrice + (entryPrice - sl) * RR_Ratio;
OpenPosition(ORDER_TYPE_BUY, entryPrice, sl, tp);
}
}
// Short Entry Signal
if(close1 < filterLine[1] && close2 >= filterLine[2] && volumeConfirm)
{
// Bearish candlestick patterns
bool bearishEngulfing = (close1 < open1 && open1 > close2 && close1 < open1);
bool shootingStar = ((close1 - low1) < (open1 - close1) * 0.3 &&
(high1 - close1) > (high1 - low1) * 0.6);
if(bearishEngulfing || shootingStar)
{
double sl = high1;
double entryPrice = SymbolInfoDouble(_Symbol, SYMBOL_BID);
double tp = entryPrice - (sl - entryPrice) * RR_Ratio;
OpenPosition(ORDER_TYPE_SELL, entryPrice, sl, tp);
}
}
}
//+------------------------------------------------------------------+
//| Open a position |
//+------------------------------------------------------------------+
void OpenPosition(ENUM_ORDER_TYPE orderType, double entry, double sl, double tp)
{
double lotSize = CalculateLotSize(entry, sl);
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.symbol = _Symbol;
request.volume = lotSize;
request.type = orderType;
request.price = entry;
request.sl = sl;
request.tp = tp;
request.deviation = 10;
request.magic = 123456;
request.comment = "Range Filter";
if(!OrderSend(request, result))
Print("OrderSend failed: ", GetLastError());
else
Print("Position opened: ", (orderType == ORDER_TYPE_BUY ? "BUY" : "SELL"),
" at ", entry, " SL: ", sl, " TP: ", tp);
}
//+------------------------------------------------------------------+
//| Calculate lot size based on risk |
//+------------------------------------------------------------------+
double CalculateLotSize(double entry, double sl)
{
double risk = AccountInfoDouble(ACCOUNT_BALANCE) * RiskPercent / 100.0;
double tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
double tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
double slDistance = MathAbs(entry - sl);
double lots = risk / (slDistance / tickSize * tickValue);
// Normalize lot size
double minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
double maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
double lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
lots = MathFloor(lots / lotStep) * lotStep;
lots = MathMax(minLot, MathMin(maxLot, lots));
return lots;
}
//+------------------------------------------------------------------+
//| Manage open positions |
//+------------------------------------------------------------------+
void ManageOpenPositions()
{
if(ArraySize(filterLine) < 1)
return;
for(int i = PositionsTotal() - 1; i >= 0; i--)
{
ulong ticket = PositionGetTicket(i);
if(PositionSelectByTicket(ticket))
{
if(PositionGetString(POSITION_SYMBOL) != _Symbol)
continue;
double close1 = iClose(_Symbol, PERIOD_M5, 1);
ENUM_POSITION_TYPE posType = (ENUM_POSITION_TYPE)PositionGetInteger(POSITION_TYPE);
// Exit if price crosses back through filter line
if(posType == POSITION_TYPE_BUY && close1 < filterLine[0])
ClosePosition(ticket);
else if(posType == POSITION_TYPE_SELL && close1 > filterLine[0])
ClosePosition(ticket);
}
}
}
//+------------------------------------------------------------------+
//| Close a position |
//+------------------------------------------------------------------+
void ClosePosition(ulong ticket)
{
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.position = ticket;
request.symbol = _Symbol;
request.volume = PositionGetDouble(POSITION_VOLUME);
request.type = (PositionGetInteger(POSITION_TYPE) == POSITION_TYPE_BUY) ?
ORDER_TYPE_SELL : ORDER_TYPE_BUY;
request.price = (request.type == ORDER_TYPE_SELL) ?
SymbolInfoDouble(_Symbol, SYMBOL_BID) :
SymbolInfoDouble(_Symbol, SYMBOL_ASK);
request.deviation = 10;
if(!OrderSend(request, result))
Print("Close position failed: ", GetLastError());
else
Print("Position closed: Ticket ", ticket);
}
Best Practices and Risk Management
1. Optimal Trading Sessions
Trade during high-liquidity sessions for best results. The London-New York overlap (8:00 AM - 12:00 PM EST) provides the most reliable signals. Avoid trading during low-volume Asian sessions or major news releases.
2. Risk Management Rules
Never risk more than 1-2% of account balance per trade. Use a minimum 2:1 risk-reward ratio. Limit daily drawdown to 3-5% maximum. If you hit three consecutive losses, stop trading for the day and review your executions.
3. Currency Pair Selection
This strategy works best on major pairs with tight spreads: EUR/USD, GBP/USD, USD/JPY, and AUD/USD. Avoid exotic pairs with wide spreads that can erode profits on short-term scalps.
Tip: EUR/USD typically offers the best balance of volatility and liquidity for this strategy, with average spreads under 1 pip during peak hours.
4. Parameter Optimization
Test different range multipliers (1.5-3.0) to match current market volatility. In trending markets, use lower multipliers (1.5-2.0) for more signals. In choppy markets, increase to 2.5-3.0 to reduce false signals.
5. Backtesting Requirements
Backtest on at least 6-12 months of historical data before live trading. Use 99% modeling quality tick data for accurate results. Forward test on a demo account for 2-4 weeks to validate performance in current market conditions.
Common Mistakes to Avoid
| Mistake | Why It Fails | Solution |
|---|---|---|
| Trading against the trend | Leads to repeated stop-outs | Only trade in direction of filter line |
| Ignoring volume confirmation | Catches false breakouts | Require above-average volume on signals |
| Over-leveraging positions | Small losses wipe out accounts | Stick to 1-2% risk per trade maximum |
| Trading during news events | Causes unpredictable volatility spikes | Avoid trading 15 min before/after major news |
| Moving stop-loss to breakeven too early | Gets stopped out by normal noise | Wait for 1:1 R:R before adjusting stops |
Performance Expectations
When properly implemented, the 5-Minute Range Filter Strategy typically achieves:
Win Rate
55-65%
With proper filtering and confirmation
Risk:Reward
1:2 to 1:3
Minimum 2:1 ratio recommended
Trades Per Day
3-8 signals
During active trading sessions
Average Trade Duration
15-45 min
Typical scalp holding period
Key Takeaways
-
The range filter eliminates noise by only updating when price moves beyond ATR-based thresholds
-
Combine filter signals with candlestick patterns and volume for higher-probability entries
-
Trade only during high-liquidity sessions (London-NY overlap) for best results
-
Maintain strict risk management: 1-2% per trade, minimum 2:1 R:R ratio
-
Adjust range multiplier based on market conditions (1.5-3.0) to optimize signal quality
"Success in scalping comes from filtering noise and executing only the highest-probability setups with disciplined risk management."