Why Your Scalping EA Keeps Getting Stopped Out
You've seen it. Your scalping EA enters a trade on the M1 chart because the stochastic crossed and price broke a minor level. Five minutes later, price reverses hard and hits your stop. You check the H1 chart and what do you see? A clear downtrend. Your EA was fighting the larger timeframe trend. That's not scalping—that's donating money to the market.
Most scalping EAs focus entirely on the execution timeframe. They watch M1 or M5 like a hawk, reacting to every wiggle. The problem is that price on a low timeframe is mostly noise—random movements that look like signals but aren't. What you need is a filter that tells you, "Hey, the bigger picture says we're trending down, so don't take long entries."
The Hull Moving Average (HMA) is perfect for this. It's smoother than a standard moving average and has almost no lag. When you use it as a multi-timeframe filter, you get a clear directional bias from a higher timeframe without the HMA lagging behind price. In this post, I'll walk you through exactly how to code this filter in MQL4, with real code examples, parameter tuning advice, and the honest limitations you'll face.
What Makes the Hull Moving Average Different?
Developed by Alan Hull, the HMA uses a weighted calculation that reduces lag dramatically compared to a simple or exponential moving average. The formula is:
HMA = WMA(2 * WMA(price, period/2) - WMA(price, period), sqrt(period))That looks intimidating, but the effect is simple: the HMA follows price changes faster than an EMA of the same period, while remaining smoother. For a scalping EA, this means you can use a higher timeframe HMA (say H1) as a trend filter, and it will react quickly enough to catch trend shifts without lagging so badly that you miss the move.
I prefer the HMA over the EMA for multi-timeframe filtering because EMAs on higher timeframes tend to cross price too late. With the HMA, you get a cleaner signal that reduces the number of times your scalping EA enters against the trend. The HMA's reduced lag also means you're less likely to get whipsawed during fast market turns—something that plagues EMA-based filters on volatile pairs like GBPJPY.
Coding the Multi-Timeframe Hull MA Filter in MQL4
Let's get into the code. You'll need to write a function that fetches the HMA value from a higher timeframe. In MQL4, you can't directly call iHMA() because there's no built-in Hull MA indicator. You have to calculate it yourself or use a custom indicator. I prefer to calculate it inline in the EA to avoid external dependencies. This keeps your EA portable—no extra indicator files to manage when you move it between brokers or VPS servers.
The Core Function
Here's a function that returns the current HMA value for any timeframe and period. Note the shift parameter: you'll want shift=0 for the current bar, but you might use shift=1 for confirmation in some strategies.
double GetHMA(int tf, int period, int shift)
{
int bars = period + shift + 5;
double wma1[], wma2[], hma[];
ArrayResize(wma1, bars);
ArrayResize(wma2, bars);
ArrayResize(hma, bars);
// Calculate WMA for half period
for(int i = 0; i < bars; i++)
{
double sum1 = 0, sum2 = 0;
double weightSum1 = 0, weightSum2 = 0;
int halfPeriod = period / 2;
// First WMA: period/2
for(int j = 0; j < halfPeriod; j++)
{
double price = iClose(tf, i + j);
sum1 += price * (halfPeriod - j);
weightSum1 += (halfPeriod - j);
}
wma1[i] = sum1 / weightSum1;
// Second WMA: full period
for(int j = 0; j < period; j++)
{
double price = iClose(tf, i + j);
sum2 += price * (period - j);
weightSum2 += (period - j);
}
wma2[i] = sum2 / weightSum2;
}
// Calculate raw HMA value (2*WMA1 - WMA2)
for(int i = 0; i < bars; i++)
{
double raw = 2 * wma1[i] - wma2[i];
wma1[i] = raw; // Reuse array
}
// Final WMA on raw with sqrt(period)
int sqrtPeriod = (int)MathSqrt(period);
for(int i = 0; i < bars - sqrtPeriod; i++)
{
double sum = 0, weightSum = 0;
for(int j = 0; j < sqrtPeriod; j++)
{
sum += wma1[i + j] * (sqrtPeriod - j);
weightSum += (sqrtPeriod - j);
}
hma[i] = sum / weightSum;
}
return hma[shift];
}This function is a bit heavy on CPU because it recalculates the full HMA each time. For a scalping EA that runs on every tick, you'll want to cache the value. Here's a more efficient version that stores the last calculated HMA and only recalculates when a new bar forms on the filter timeframe:
datetime lastHMATime = 0;
double cachedHMA = 0;
double GetHMAFilter(int filterTF, int hmaPeriod)
{
if(Time[0] == lastHMATime)
return cachedHMA;
lastHMATime = Time[0];
cachedHMA = GetHMA(filterTF, hmaPeriod, 0);
return cachedHMA;
}The caching trick is critical. Without it, on a 1-minute chart with H1 filter, your EA would recalculate the HMA 60+ times per hour unnecessarily. I've seen EAs that don't cache this and they cause terminal lag during high-volatility news events—exactly when you need your EA to be responsive.
Integrating the Filter Into Your Scalping Logic
Now you use this filter in your trade entry conditions. The idea is simple: only take long signals when price is above the HMA on the higher timeframe, and only take short signals when price is below it. Here's a practical example:
// Inputs
input int FilterTF = PERIOD_H1; // Higher timeframe for filter
input int HMA_Period = 20; // HMA period on filter TF
input double FilterDeviation = 0.0001; // Buffer to avoid whipsaws
// In your tick handler
double filterValue = GetHMAFilter(FilterTF, HMA_Period);
double currentPrice = iClose(FilterTF, 0);
bool trendUp = (currentPrice - filterValue) > FilterDeviation;
bool trendDown = (filterValue - currentPrice) > FilterDeviation;
// Your scalping entry logic
if(buySignal && trendUp)
OpenBuy();
if(sellSignal && trendDown)
OpenSell();The FilterDeviation parameter is crucial. Without it, price can be within a pip of the HMA and your EA will flip-flop between allowing buys and shorts. I usually set this to 0.5 * the average true range on the filter timeframe, expressed in price. For EURUSD on H1 with an ATR of 0.0010 (10 pips), I'd use 0.0005 (5 pips) as the deviation. For GBPJPY, where the H1 ATR might be 0.0025 (25 pips), I'd use 0.0012 (12 pips).
One edge case: when the filter timeframe has no new bar yet (e.g., during the first minute of a new H1 bar), your EA might use stale HMA data. The caching function handles this by returning the previous bar's value until the new H1 bar forms. That's actually desirable—you don't want the filter flipping mid-bar.
Parameter Optimization: Finding the Sweet Spot
You can't just slap an HMA filter on your EA and expect magic. The two key parameters—filter timeframe and HMA period—need tuning for your specific scalping strategy and instrument.
| Parameter | Type | Typical Range | Effect |
|---|---|---|---|
| FilterTF | ENUM_TIMEFRAMES | H1, H4, D1 | Higher TF = fewer but more reliable signals. H1 works best for M1/M5 scalping. H4 for M15 scalping. |
| HMA_Period | int | 10–40 | Shorter period reacts faster but risks noise. Longer period smooths more but lags. 20 is a good starting point. |
| FilterDeviation | double | 0.0001–0.0010 | Higher deviation reduces false flips but may delay entries. Tune per pair's volatility. Use ATR as a guide. |
I've found that for a typical M5 scalping EA on EURUSD, an H1 HMA with period 20 and a deviation of 0.0003 (3 pips) works well. On GBPJPY, which moves more, I bump the deviation to 0.0008 and use H4 as the filter timeframe. Your mileage will vary—always backtest with out-of-sample data. I also recommend running a three-month forward test on a demo account before going live. The filter might look perfect in 2023 data but fail in 2024's different volatility regime.
How to Optimize Without Curve-Fitting
The temptation is to run a full optimization across all parameters and pick the best result. Don't. That's how you end up with a filter that works only on the training data. Here's my approach:
- Run a walk-forward optimization: optimize on six months of data, then test on the next three months. Repeat over a two-year period.
- Fix the HMA period to a round number (20 or 30) and only optimize the deviation and filter timeframe. Fewer parameters mean less risk of overfitting.
- Check that the filter doesn't eliminate too many trades. If your trade count drops by more than 60%, the filter is too aggressive. You need enough trades for statistical validity.
Backtesting Your Hull MA Filter EA
When you backtest a multi-timeframe EA, the Strategy Tester in MetaTrader 4 has a quirk: it only loads data for the chart timeframe unless you explicitly request it. If your EA uses H1 data while running on M1, the tester will show gaps unless you have H1 history loaded. Here's what I do:
- Open the H1 chart for your symbol and let it download all history (you can check the "Periods" tab in the Data Window). Make sure you have at least two years of H1 data.
- Switch to M1 and open the Strategy Tester (Ctrl+R).
- In the EA properties, under "Testing" tab, set "Optimization" to "Slow" or "Fast" depending on your needs. The key is to ensure "Use date" is checked and you have enough history. I usually test from 2022-01-01 to 2024-01-01.
- Run a quick single-test pass first to verify the HMA filter is working. Add a comment in the code to print the filter value to the Experts tab:
Print("HMA: ", filterValue, " Price: ", currentPrice); - Check the "Expert" tab during the test. If you see "zero divide" errors, your HMA period might be too small for the filter timeframe (e.g., period 5 on H1 with insufficient bars).
One trap I've hit: if your EA uses iClose() with a higher timeframe inside a loop, the tester can slow to a crawl. The caching trick I showed earlier solves this. Without caching, a 10-year backtest on M1 might take hours instead of minutes. I once ran a backtest overnight without caching—woke up to find it had only processed six months.
Common Backtest Pitfalls
- Missing history: The tester doesn't warn you if H1 data is incomplete. Check the "Journal" tab for "not enough data" errors.
- Bar shift issues: When using shift=0 in the HMA calculation, the tester might use the current forming bar on the filter timeframe, which can change mid-test. Use shift=1 for the last completed bar to avoid look-ahead bias.
- Spread and slippage: The filter doesn't account for these, but your entry conditions do. Always run backtests with realistic spread settings (e.g., 1-2 pips for EURUSD, 3-5 pips for GBPJPY).
Pros, Cons, and Honest Risks
Let's be straight with each other. The Hull MA multi-timeframe filter is not a holy grail. Here's what it does well and where it falls short.
What Works
- Reduces false signals significantly. In sideways markets on the higher timeframe, the filter keeps you out of chop. I've seen win rates jump from 40% to 65% on some EAs after adding this filter. That's the difference between a






