Tested by BacktestingMax
9/21 EMA crossover backtest: from 5-minute to daily charts
Does the 9/21 EMA crossover make money, and on which chart?
Mostly no. It made money only on some markets, and only on 1-hour, 4-hour and daily charts. The strategy buys when the 9 EMA (a fast average of recent prices) crosses above the 21 EMA (a slower average), and sells when it crosses below. We ran 40 backtests from 2014 to 2025, after trading costs: 8 markets (EUR/USD, GBP/USD, USD/JPY, AUD/USD, gold, the S&P 500, the Nasdaq 100 and the DAX), each on 5 timeframes from 5-minute to daily charts. On 5-minute and 15-minute charts, the crossover lost money on all 8 markets. On 1-hour, 4-hour and daily charts, 10 of 24 tests made money. 5 of them made money in the older years (2014–2021) and again in the newer years (2022–2025): gold on 1-hour and 4-hour charts, USD/JPY on 1-hour charts, the DAX on 4-hour charts and EUR/USD on daily charts.
40 backtests 2014–2025 Trading costs included Tested 12 October 2026
Where it worked 40 backtests- Made money
- 10 of 40 tests, with the basic rules
- Made money in both periods
- 5 2014–2021 and again 2022–2025
- Best backtest
- +539% USD/JPY, 1-hour chart: account growth 2014–2025
- With a 200 EMA trend filter
- 6 tests made money in both periods, compared with 5 with the basic rules
Made money in both periods
- EUR/USD, daily
- USD/JPY, 1-hour
- Gold, 1-hour
- Gold, 4-hour
- DAX, 4-hour
Lost money on every market
- 5-minute charts
- 15-minute charts
The results
Results on every market
Each box shows one backtest of the 9/21 EMA crossover with the basic rules, from 2014 to 2025, after trading costs: the % result, and a small chart of the account balance. The solid line is the account balance and the dashed line is the starting balance. Green results made money; red results lost money. Under each result: whether it made money in both 2014–2021 and 2022–2025, or passed all four of our checks (see "How a test passes" further down).
| Market | 5-minute | 15-minute | 1-hour | 4-hour | daily |
|---|---|---|---|---|---|
| EUR/USD | −100% Lost money. Account down 90% by Apr 2014 | −100% Lost money. Account down 90% by Mar 2016 | −52% Lost money | −8% Lost money | +19% Passed all four checks |
| GBP/USD | −100% Lost money. Account down 90% by Mar 2014 | −99% Lost money. Account down 90% by Dec 2014 | −59% Lost money | +10% Made money overall, lost in one period | −4% Lost money |
| USD/JPY | −100% Lost money. Account down 90% by Jun 2014 | −99% Lost money. Account down 90% by Nov 2018 | +539% Made money in both periods | +23% Made money overall, lost in one period | −3% Lost money |
| AUD/USD | −100% Lost money. Account down 90% by Apr 2014 | −100% Lost money. Account down 90% by Sept 2016 | −92% Lost money. Account down 90% by Aug 2025 | −58% Lost money | −8% Lost money |
| Gold | −100% Lost money. Account down 90% by May 2014 | −99% Lost money. Account down 90% by Apr 2015 | +194% Made money in both periods | +99% Made money in both periods | +5% Made money overall, lost in one period |
| S&P 500 | −100% Lost money. Account down 90% by Feb 2014 | −100% Lost money. Account down 90% by Jul 2014 | −88% Lost money | −43% Lost money | −1% Lost money |
| Nasdaq 100 | −100% Lost money. Account down 90% by Feb 2014 | −100% Lost money. Account down 90% by Jun 2014 | −47% Lost money | +14% Made money overall, lost in one period | −0.4% Lost money |
| DAX | −100% Lost money. Account down 90% by May 2014 | −99% Lost money. Account down 90% by Oct 2015 | +39% Made money overall, lost in one period | +17% Made money in both periods | −8% Lost money |
All the numbers, test by test
| Market | Chart | Trades | Win rate | Profit factor | Result | Max drawdown | 2014–2021 | 2022–2025 | Monte Carlo drawdown | Trading costs |
|---|---|---|---|---|---|---|---|---|---|---|
| EUR/USD | 5-minute | 2,691 | 18.3% | 0.60 | −99.7% | −99.7% | −99.7% | −5.1% | −99.8% | $5,302 |
| EUR/USD | 15-minute | 6,137 | 22.9% | 0.82 | −99.6% | −99.6% | −99.6% | −4.1% | −99.8% | $6,269 |
| EUR/USD | 1-hour | 3,057 | 27.2% | 0.96 | −52.1% | −66.8% | −17.5% | −42.0% | −83.6% | $7,925 |
| EUR/USD | 4-hour | 776 | 29.5% | 0.98 | −8.3% | −34.1% | +16.2% | −21.1% | −50.3% | $1,048 |
| EUR/USD | daily | 131 | 26.7% | 1.31 | +19.1% | −15.2% | +11.6% | +6.7% | −20.4% | $71 |
| GBP/USD | 5-minute | 2,026 | 18.9% | 0.58 | −99.6% | −99.7% | −99.6% | −1.7% | −99.7% | $5,656 |
| GBP/USD | 15-minute | 5,690 | 22.2% | 0.74 | −99.3% | −99.4% | −99.2% | −6.2% | −99.6% | $5,705 |
| GBP/USD | 1-hour | 3,072 | 27.2% | 0.95 | −59.2% | −77.4% | −2.4% | −58.2% | −85.0% | $7,549 |
| GBP/USD | 4-hour | 782 | 29.5% | 1.02 | +10.0% | −27.3% | −2.5% | +12.9% | −46.2% | $1,272 |
| GBP/USD | daily | 132 | 29.5% | 0.93 | −3.7% | −18.6% | −10.5% | +7.7% | −21.9% | $64 |
| USD/JPY | 5-minute | 3,059 | 22.4% | 0.77 | −99.7% | −99.7% | −99.7% | 0.0% | −99.8% | $10,617 |
| USD/JPY | 15-minute | 7,237 | 24.3% | 0.94 | −99.1% | −99.6% | −99.5% | +59.9% | −99.6% | $21,834 |
| USD/JPY | 1-hour | 2,995 | 28.3% | 1.16 | +538.9% | −54.0% | +103.6% | +213.8% | −59.1% | $17,863 |
| USD/JPY | 4-hour | 771 | 29.3% | 1.05 | +22.8% | −24.1% | −0.2% | +23.1% | −45.7% | $1,297 |
| USD/JPY | daily | 138 | 30.4% | 0.95 | −2.6% | −19.2% | −18.1% | +19.0% | −21.0% | $67 |
| AUD/USD | 5-minute | 2,461 | 20.3% | 0.68 | −99.8% | −99.8% | −99.8% | −2.5% | −99.8% | $7,269 |
| AUD/USD | 15-minute | 4,931 | 25.5% | 0.85 | −99.5% | −99.6% | −99.5% | −5.3% | −99.7% | $9,314 |
| AUD/USD | 1-hour | 3,150 | 26% | 0.87 | −92.1% | −94.0% | −71.6% | −72.2% | −95.4% | $5,025 |
| AUD/USD | 4-hour | 832 | 25.7% | 0.80 | −57.8% | −61.4% | −32.7% | −37.3% | −68.8% | $1,045 |
| AUD/USD | daily | 128 | 29.7% | 0.86 | −8.2% | −14.7% | +1.2% | −9.3% | −23.1% | $93 |
| Gold | 5-minute | 1,992 | 21.2% | 0.65 | −99.6% | −99.6% | −99.6% | 0.0% | −99.7% | $9,589 |
| Gold | 15-minute | 4,103 | 21.9% | 0.73 | −98.9% | −98.9% | −98.9% | 0.0% | −99.4% | $7,298 |
| Gold | 1-hour | 2,955 | 26.7% | 1.08 | +193.6% | −62.1% | +50.1% | +95.6% | −67.7% | $19,076 |
| Gold | 4-hour | 745 | 29.7% | 1.23 | +99.3% | −27.9% | +16.8% | +70.7% | −36.1% | $1,752 |
| Gold | daily | 127 | 24.4% | 1.09 | +4.5% | −11.5% | −10.3% | +16.6% | −18.5% | $80 |
| S&P 500 | 5-minute | 1,492 | 18% | 0.33 | −99.9% | −99.9% | −99.9% | 0.0% | −99.9% | $4,777 |
| S&P 500 | 15-minute | 2,369 | 17.7% | 0.54 | −99.9% | −99.9% | −99.9% | 0.0% | −99.9% | $4,661 |
| S&P 500 | 1-hour | 2,901 | 25.9% | 0.81 | −88.3% | −89.2% | −87.3% | −7.9% | −94.4% | $3,634 |
| S&P 500 | 4-hour | 783 | 28.1% | 0.84 | −42.7% | −54.0% | −47.3% | +8.8% | −63.2% | $919 |
| S&P 500 | daily | 121 | 34.7% | 0.98 | −1.1% | −12.3% | −5.9% | +5.2% | −19.6% | $69 |
| Nasdaq 100 | 5-minute | 1,033 | 15.7% | 0.43 | −99.9% | −99.9% | −99.9% | 0.0% | −99.9% | $8,775 |
| Nasdaq 100 | 15-minute | 1,534 | 14% | 0.49 | −99.8% | −99.8% | −99.8% | 0.0% | −99.9% | $5,627 |
| Nasdaq 100 | 1-hour | 2,839 | 27% | 0.95 | −46.6% | −75.4% | −50.9% | +8.8% | −83.4% | $7,634 |
| Nasdaq 100 | 4-hour | 745 | 29.1% | 1.03 | +13.5% | −31.5% | −0.6% | +14.1% | −45.8% | $1,442 |
| Nasdaq 100 | daily | 127 | 26.8% | 0.99 | −0.4% | −11.6% | −0.9% | +0.5% | −18.4% | $86 |
| DAX | 5-minute | 2,994 | 19.1% | 0.64 | −99.6% | −99.6% | −99.6% | 0.0% | −99.8% | $6,574 |
| DAX | 15-minute | 3,088 | 19.9% | 0.83 | −98.7% | −98.8% | −98.6% | −9.6% | −99.5% | $5,391 |
| DAX | 1-hour | 2,890 | 27.7% | 1.02 | +38.8% | −57.8% | −5.5% | +46.9% | −73.6% | $4,717 |
| DAX | 4-hour | 811 | 27% | 1.03 | +17.2% | −32.2% | +9.9% | +6.6% | −47.9% | $678 |
| DAX | daily | 136 | 28.7% | 0.84 | −7.7% | −9.5% | −8.8% | +1.2% | −20.1% | $26 |
Profit factor is the money won divided by the money lost. Above 1 means the test made money. Max drawdown is the biggest fall from the highest balance. 2014–2021 and 2022–2025 show the result in each period on its own. Monte Carlo drawdown: we shuffled the trades into a random order 1,000 times; in 95% of those runs, the worst drawdown was this number or less. Result is the change in the account, with the risk of each trade set to 1% of the account at that time.
The account balance
The best test and the middle test
What we found
What the 40 backtests show
On 5-minute and 15-minute charts, the 9/21 crossover lost money on all 8 markets. On 5-minute charts, all 8 accounts were down 90% by June 2014, in the first 6 months of the test. Trading costs are the main reason: before costs, the 5-minute trades lost 6.5% of the money they risked in total, and after costs they lost 24.3%. To check that the losses were not only a 2014 problem, we also ran the 5-minute and 15-minute charts on 2022–2025 alone. 15 of the 16 tests still lost money; only USD/JPY on 15-minute charts made money.
Gold made money on the most charts: the crossover made money on its 1-hour, 4-hour and daily charts. USD/JPY and the DAX made money on their 1-hour and 4-hour charts. The best backtest was USD/JPY on 1-hour charts: +539% from 2014 to 2025, but at one point the account fell 54% from its highest balance.
On EUR/USD daily charts, one sell trade from July 2014 to April 2015 made 24.9R (24.9 times the money it risked). That one trade made more than the whole test made in 131 trades (21.2R), so without it the test would have lost money. With the basic rules, this is the only test that passed all four of our checks (see 'How a test passes'). Across the 40 tests, only 14% to 35% of trades won.
On the S&P 500, the crossover lost money on every chart, even though the index rose about 271% from 2014 to 2025. Much of the loss came from the sell trades: on 1-hour, 4-hour and daily charts, the S&P 500 sells lost 257.3R and the buys made 37.7R. In all 24 tests on these three charts, the trades in the same direction as the market's overall move from 2014 to 2025 (buys if it ended higher, sells if it ended lower) did better than the others.
We tried 4 changes traders often make: a 200 EMA trend filter, an ADX trend-strength filter, a pullback entry and a 2:1 take-profit. The 200 EMA filter (only buy above the 200 EMA, only sell below it) did best on 2014–2021, the years we used to pick the best change. On 2022–2025, it made only a small difference (see 'Can the 9/21 EMA crossover be improved?' below). It helped most on 4-hour charts: with the filter, 4 of 8 tests made money in both periods, compared with 2 of 8 with the basic rules. No change made the 5-minute or 15-minute charts make money from 2014 to 2025.
Trading costs
Why the 5-minute and 15-minute charts lost
Every trade pays a trading cost: the spread, which is the gap between the buy price and the sell price. Each bar shows that cost as a share of the money a trade risks (what it loses if the stop-loss is hit), for all trades on all 8 markets from 2014 to 2025.
- 5-minute17,748 trades 17.8%
- 15-minute35,089 trades 8.1%
- 1-hour23,859 trades 3.2%
- 4-hour6,245 trades 1.5%
- Daily1,040 trades 0.6%
The stop-loss is 2 times the ATR (how far price moves in an average candle), so on 5-minute and 15-minute charts it is close to the entry price, and the same trading cost is a bigger share of each trade. Most of these 5-minute and 15-minute trades are from 2014 to 2016, because most of those accounts were down 90% by then. On 2022–2025 alone, when prices moved more, the cost was 8.1% of the money risked on 5-minute charts and 4.7% on 15-minute charts. The test does not include overnight (swap) fees, which brokers charge for keeping a trade open overnight.
Buys against sells
On stock indices and gold, the buys made money and the sells lost
We added up all the trades on 1-hour, 4-hour and daily charts from 2014 to 2025, in R. 1R is the money risked on one trade: 1% of the account.
- 3 stock indices: buys
- +455.2R 5,700 trades
- 3 stock indices: sells
- −577.1R 5,653 trades
- Gold: buys
- +329.1R 1,905 trades
- Gold: sells
- −90.7R 1,922 trades
The 3 stock indices are the S&P 500, the Nasdaq 100 and the DAX. They and gold all rose a lot from 2014 to 2025. On the 4 currency pairs, the buys lost 31.8R and the sells lost 64.4R. We did not test a version that only buys.
Year by year
Without 2020, the 1-hour, 4-hour and daily tests lost money in total
We added up the 24 tests on 1-hour, 4-hour and daily charts by the year each trade opened, in R (1R is the money risked on one trade).
- 20142,561 trades +62.1R
- 20152,591 trades +43.4R
- 20162,660 trades −270.5R
- 20172,614 trades −72.6R
- 20182,589 trades −3.9R
- 20192,615 trades −95.2R
- 20202,520 trades +310.2R
- 20212,679 trades −107.8R
- 20222,578 trades +137.2R
- 20232,552 trades +26.2R
- 20242,626 trades −27.1R
- 20252,559 trades +18.2R
From 2016 to 2019, the 24 tests lost money four years in a row. 2020 alone made 310.2R, more than all 12 years together (about 20R). The 5-minute and 15-minute tests are left out, because most of those accounts were down 90% by 2016.
The strategy
What the 9/21 EMA crossover is
An EMA (exponential moving average) is an indicator that shows the average price of the last few candles, with more weight on the newest candles. The 9 EMA averages about the last 9 candles, so it follows price closely. The 21 EMA averages about the last 21 candles, so it moves more slowly. After price has been rising for a while, the 9 EMA crosses above the 21 EMA. After price has been falling for a while, the 9 EMA crosses below it. The 9/21 crossover strategy trades in the direction of each cross. Many day traders use it on 5-minute and 15-minute charts.
- EMAs
- 9 and 21, on closing prices
- Stop-loss
- 2 × ATR(14)
- Take-profit
- None. The next cross in the other direction closes the trade.
- Risk per trade
- 1% of the account
The rules we tested
- Buy when the 9 EMA crosses above the 21 EMA. We check for a cross when each candle closes, and the trade opens at the next candle's open.
- Sell (go short) when the 9 EMA crosses below the 21 EMA.
- When the EMAs cross the other way, close the trade and open a new trade in the new direction. In our test, the new trade opens one candle after the old trade closes. After a stop-loss, wait for the next cross.
- ATR is an indicator of how far price moves in an average candle, measured over the last 14 candles. Put the stop-loss 2 times the ATR away from the entry price.
- Size every trade so that, if the stop-loss is hit, the trade loses 1% of the account.
The method
How we tested it
The test
- Markets
- EUR/USD, GBP/USD, USD/JPY, AUD/USD, Gold, S&P 500, Nasdaq 100 and DAX
- Charts
- 5-minute, 15-minute, 1-hour, 4-hour and daily candles
- Dates
- January 2014 to December 2025
- Account
- $10,000, risking 1% of the account on each trade
- Trading costs
- Included. Every trade pays that market's normal trading cost: the spread, which is the gap between the buy price and the sell price.
How a test passes
We call a test passed only if it meets all four checks. A strategy can make money in one backtest and still fail one of these checks.
- At least 100 trades. With fewer trades, a good result could be luck.
- It made money from 2014 to 2021 and again from 2022 to 2025. This shows whether the strategy kept working in the newer years, not only in the older ones.
- A profit factor (the money won divided by the money lost) of at least 1.2 from 2022 to 2025, after trading costs.
- In 95% of 1,000 random shuffles of the trades, the account must not fall more than 30% from its highest point.
Improvements
Can the 9/21 EMA crossover be improved?
Traders often add one rule to the 9/21 crossover to try to improve it. We tested four common ones, each on the same 40 tests. If we picked the best change and judged it on the same years, it would almost always look good. So we picked the best change using 2014–2021 only, then checked it on 2022–2025, the years we did not use to pick it.
| Version | Made money 2014–2021years used to pick the best version | Made money 2022–2025years not used to pick it | Made money in both periods | Passed all four checks |
|---|---|---|---|---|
| The basic rules, no changes The rules under 'The rules we tested', with nothing added. | 7 of 40middle test −40.0% | 18 of 40middle test 0.0% | 5 | 1 |
| With a 200 EMA trend filterBest on 2014–2021 Only buy when the price is above the 200 EMA, a moving average of the last 200 candles. Only sell when it is below. A cross the other way still closes the open trade, but the new trade opens only if the price is on the right side of the 200 EMA. | 11 of 40middle test −27.0% | 20 of 40middle test +0.2% | 6 | 1 |
| With a trend-strength filter (ADX above 25) ADX is an indicator that measures how strong a trend is, from 0 to 100, whether price is rising or falling. Only open a trade when ADX(14) is above 25, a common sign of a strong trend, to skip crosses in a market that moves sideways. A cross the other way still closes the open trade, but the new trade opens only if ADX is above 25. | 6 of 40middle test −29.6% | 16 of 40middle test −0.1% | 4 | 1 |
| With a pullback entry to the 9 EMA Don't buy as soon as the EMAs cross. After the 9 EMA crosses above the 21 EMA, wait for the first candle that dips back to the 9 EMA and closes above it, then buy. For sells, do the opposite. The next cross closes the trade, and the new trade waits for its own pullback. | 8 of 40middle test −38.4% | 16 of 40middle test 0.0% | 4 | 2 |
| With a 2:1 take-profit Add a take-profit: close the trade when it has made 2 times the money it risked. If the EMAs cross the other way first, that cross still closes the trade. After a take-profit, wait for the next cross. | 10 of 40middle test −31.1% | 15 of 40middle test 0.0% | 5 | 0 |
On 2014–2021, the years we used to pick the best change, the 200 EMA trend filter did best: 11 of 40 tests made money with it, compared with 7 with the basic rules. On 2022–2025, the years we kept for the check, the difference was small: 20 of 40 tests made money with the filter, compared with 18 without it. In every version, most 5-minute and 15-minute accounts were already down 90% before 2022, and each trade risks 1% of what is left, so those tests made only small trades, or none, in 2022–2025. The 2022–2025 counts come mainly from the 1-hour, 4-hour and daily charts. On daily charts, the filter left only 58 to 76 trades per test, too few to judge. So the filter helped a little: with it, 6 of 40 tests made money in both periods, compared with 5 with the basic rules.
Passed all four checks with a 200 EMA trend filter
- Gold, 4-hour
Questions
Questions traders ask
Does the 9/21 EMA crossover strategy work?
Mostly no. In our backtest from 2014 to 2025, with trading costs, the 9/21 crossover with the basic rules made money in 10 of 40 tests (8 markets, each on 5 timeframes). All 10 were on 1-hour, 4-hour or daily charts; on 5-minute and 15-minute charts, it lost money on all 8 markets. 5 tests made money in both 2014–2021 and 2022–2025: gold on 1-hour and 4-hour charts, USD/JPY on 1-hour charts, the DAX on 4-hour charts and EUR/USD on daily charts.
What is the best timeframe for the 9/21 EMA crossover?
In our test, 4-hour charts did best: the crossover made money on 5 of 8 markets. It made money on 3 of 8 markets on 1-hour charts and on 2 of 8 on daily charts. On 5-minute and 15-minute charts, the timeframes many day traders use, it lost money on all 8 markets. Trading costs are the main reason: on 5-minute charts, the cost of a trade was 17.8% of the money it risked, on average. We also ran the 5-minute and 15-minute charts on 2022–2025 alone, and 15 of those 16 tests still lost money.
What is the win rate of the 9/21 EMA crossover?
Low. In our 40 backtests, only 14% to 35% of trades were winners. The crossover makes its money from a few long trends, and those trends pay for many small losses. For example, on EUR/USD daily charts, one sell trade from July 2014 to April 2015 made 24.9 times the money it risked, more than the whole test made in 131 trades.
Does a 200 EMA filter improve the 9/21 EMA crossover?
A little, in our backtest, and mostly on 4-hour charts. A 200 EMA filter means: only buy when the price is above the 200 EMA (a moving average of the last 200 candles), and only sell when it is below. From 2014 to 2025, 15 of the 40 tests made money with the filter, compared with 10 without it. On 4-hour charts, 4 of 8 tests made money in both 2014–2021 and 2022–2025 with the filter, compared with 2 of 8 without it. In 2022–2025 alone, the years we did not use to pick it, the difference was small: 20 of 40 tests made money with the filter, compared with 18 without it. On 5-minute and 15-minute charts, all 16 tests still lost money with the filter.
Keep exploring
Related strategies we tested
MACD MACD crossover strategy backtest Made money in 6 of 21 tests, 4 of them in both periods. Best: DAX, 1-hour, +294%The strategy code we ran
This is the exact code from the BacktestingMax strategy engine. Paste it into the strategy editor in the app to run it on any market.
class EmaCrossover921(Strategy):
"""The 9/21 EMA crossover as most traders learn it, plus four changes traders often make.
Standard (all switches off): buy when the 9 EMA crosses above the 21 EMA, sell short when
it crosses below. The opposite cross closes the trade and opens a new one the other way
(the exit fills at the next candle's open, the new trade one candle later). A stop-loss
2 x ATR(14) away caps each trade; size so that the stop costs 1% of the account. After a
stop-loss (or a take-profit), wait for the next cross.
The changes, one at a time:
trend_filter = 1 only buy above the 200 EMA, only sell below it
adx_filter = 1 only open a trade when ADX(14) is above 25 (a trending market)
pullback = 1 don't buy the cross itself: after the cross, buy the first candle that
dips to the 9 EMA and closes back above it (sells mirror this); the
opposite cross closes the trade but doesn't open a new one at once
exit_rr = 2 add a take-profit at 2 times the risk; the opposite cross still exits
"""
params = {"fast": Param(type=int, default=9, min=2, max=100),
"slow": Param(type=int, default=21, min=3, max=200),
"atr_mult": Param(type=float, default=2.0, min=0.5, max=5.0),
"trend_filter": Param(type=int, default=0, min=0, max=1),
"adx_filter": Param(type=int, default=0, min=0, max=1),
"adx_min": Param(type=float, default=25.0, min=0.0, max=100.0),
"pullback": Param(type=int, default=0, min=0, max=1),
"exit_rr": Param(type=float, default=0.0, min=0.0, max=10.0)}
lookback = 220
def init(self, ctx):
self.fast = ctx.indicator("builtin:ema", period=ctx.params.fast)
self.slow = ctx.indicator("builtin:ema", period=ctx.params.slow)
self.atr = ctx.atr(period=14)
self.ema200 = ctx.ema(source=ctx.close, period=200)
self.adx = ctx.indicator("builtin:adx", period=14)
self.reverse_to = None # "long" / "short": open this once the closing trade is gone
self.armed = None # pullback version: the side of the last cross, until it trades
def on_bar(self, ctx):
f0, f1 = self.fast.ema[0], self.fast.ema[1]
s0, s1 = self.slow.ema[0], self.slow.ema[1]
a = self.atr.value
if f0 is None or f1 is None or s0 is None or s1 is None or a is None:
return
trend = ctx.params.trend_filter == 1
if trend and self.ema200.value is None:
return
use_adx = ctx.params.adx_filter == 1
adx = self.adx.adx[0]
if use_adx and adx is None:
return
pullback = ctx.params.pullback == 1
rr = ctx.params.exit_rr
cross_up = f1 <= s1 and f0 > s0
cross_down = f1 >= s1 and f0 < s0
if pullback:
if cross_up:
self.armed = "long"
elif cross_down:
self.armed = "short"
pos = ctx.position
if pos is not None:
if pos.is_long and cross_down:
ctx.close_position()
self.reverse_to = None if pullback else "short"
elif pos.is_short and cross_up:
ctx.close_position()
self.reverse_to = None if pullback else "long"
return
entry = ctx.close[0]
if pullback:
# The first candle after the cross that dips to the 9 EMA and closes back beyond it.
# The cross candle itself doesn't count, and the EMAs must still be in cross order.
go_long = (self.armed == "long" and not cross_up and f0 > s0
and ctx.low[0] <= f0 and entry > f0)
go_short = (self.armed == "short" and not cross_down and f0 < s0
and ctx.high[0] >= f0 and entry < f0)
else:
# A reversal waits one candle for the close to fill. Drop it if the EMAs crossed back.
pending, self.reverse_to = self.reverse_to, None
go_long = cross_up or (pending == "long" and f0 > s0)
go_short = cross_down or (pending == "short" and f0 < s0)
long_ok = not trend or entry > self.ema200.value
short_ok = not trend or entry < self.ema200.value
strong = not use_adx or adx > ctx.params.adx_min
if ctx.gate("ema9_crosses_up", go_long) and ctx.gate("long_trend_filter", long_ok) \
and ctx.gate("adx_strong", strong):
stop = entry - ctx.params.atr_mult * a
tp = entry + rr * (entry - stop) if rr > 0 else None
if ctx.buy(size=ctx.size_by_risk_pct(0.01, stop=stop, entry=entry), sl=stop, tp=tp,
tag="ema_long") is not None:
self.armed = None
elif ctx.gate("ema9_crosses_down", go_short) and ctx.gate("short_trend_filter", short_ok) \
and ctx.gate("adx_strong", strong):
stop = entry + ctx.params.atr_mult * a
tp = entry - rr * (stop - entry) if rr > 0 else None
if ctx.sell(size=ctx.size_by_risk_pct(0.01, stop=stop, entry=entry), sl=stop, tp=tp,
tag="ema_short") is not None:
self.armed = None
Your turn
Test the 9/21 EMA crossover yourself
Change the settings, the market or the stop-loss and see the result in seconds. Ask the BacktestingMax AI: Backtest a 9/21 EMA crossover with a 200 EMA filter on gold, 4-hour chart, from 2014 to 2025
.
Free account. No credit card needed.
See every strategy we tested Browse strategies shared by tradersBacktest on historical data with trading costs, run by BacktestingMax. Past results do not guarantee future results, and nothing here is financial advice. Updated 12 October 2026.