Strategies we backtested VWAP + 9 EMA crossover

Tested by BacktestingMax

VWAP + 9 EMA strategy backtest on NQ and ES futures

Does the VWAP + 9 EMA strategy work on NQ and ES futures?

Not in 2024 and 2025: all 6 of our tests lost money in those two years. From 2020 to 2023, 5 of the 6 made money. The strategy buys when the 9 EMA, a fast moving average, crosses above VWAP, the average price of the day weighted by volume. It sells short (sells first, to profit if price falls) when the 9 EMA crosses below VWAP. We tested it on MNQ and MES, the micro contracts of NQ and ES futures, on 1-minute, 5-minute and 15-minute charts, with trading costs. Each trade risked 1% of the account, which started at $50,000. We call these rules the basic rules. The best test was MNQ on the 15-minute chart: +90.8% from 2020 to 2025, but −1.0% in 2024–2025 (the change in the account from the start of 2024). We also tested VWAP alone, with a trade on every candle that closes on the other side of VWAP. In 5 of the 6 tests, the 9 EMA cut the number of trades about in half. From 2020 to 2025, 5 of the 6 tests made money with the 9 EMA, and 2 of 6 with VWAP alone.

6 backtests 2020–2025 Trading costs included Tested 19 October 2026

Where it worked 6 backtests
Made money
5
of 6 tests, with the basic rules
Made money in both periods
0
2020–2023 and again 2024–2025
Best backtest
+91%
MNQ (Micro Nasdaq 100), 15-minute chart: account growth 2020–2025
Wait for a pullback to the 9 EMA
0
tests made money in both periods, compared with 0 with the basic rules

The results

Results on every market

Each box shows one backtest of the VWAP + 9 EMA crossover with the basic rules, from 2020 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 2020–2023 and 2024–2025, or passed all four of our checks (see "How a test passes" further down).

Market 1-minute 5-minute 15-minute
MNQ (Micro Nasdaq 100) +29% Made money overall, lost in one period +79% Made money overall, lost in one period +91% Made money overall, lost in one period
MES (Micro S&P 500) −98% Lost money. Account down 90% by Sept 2021 +12% Made money overall, lost in one period +18% Made money overall, lost in one period
All the numbers, test by test
Market Chart Trades Win rate Profit factor Result Max drawdown 2020–2023 2024–2025 Monte Carlo drawdown Trading costs
MNQ (Micro Nasdaq 100) 1-minute 8,754 23.6% 1.00 +28.6% −88.8% +142.6% −47.0% −95.8% $430,401
MNQ (Micro Nasdaq 100) 5-minute 3,686 32.8% 1.02 +79.0% −49.5% +144.2% −26.7% −67.3% $76,367
MNQ (Micro Nasdaq 100) 15-minute 1,882 43.6% 1.07 +90.8% −22.2% +92.7% −1.0% −37.9% $21,578
MES (Micro S&P 500) 1-minute 6,537 21.1% 0.94 −97.7% −98.7% −90.4% −76.0% −99.5% $136,337
MES (Micro S&P 500) 5-minute 3,614 31.4% 1.00 +11.7% −52.6% +19.2% −6.3% −75.4% $125,409
MES (Micro S&P 500) 15-minute 1,949 41.2% 1.02 +17.5% −29.3% +38.3% −15.1% −51.4% $42,574

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. 2020–2023 and 2024–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

Best test MNQ (Micro Nasdaq 100), 15-minute chart +90.8% from 2020 to 2025
Middle test (median): half of the 6 did better, half did worse MNQ (Micro Nasdaq 100), 1-minute chart +28.6% from 2020 to 2025

What we found

What the 6 backtests show

  1. From 2020 to 2023, the strategy made money in 5 of the 6 tests. On MNQ, the account grew by 92.7% to 144.2%, depending on the chart. In 2024–2025, all 6 tests lost money, so no test made money in both periods, and no test passed all four of our checks (see 'How a test passes' below). This matches our VWAP cross backtest, where the Nasdaq 100 also made money in 2020–2023 and lost money in 2024–2025 on all three charts. On the 5-minute and 15-minute charts here, most of the loss came in 2025: for example, the MNQ 15-minute account grew 19.3% in 2024 and lost 17.0% in 2025. The trades made much less before costs in the newer years, while the costs stayed about the same. On MNQ's 15-minute chart, the average trade made 7.2% of its risk before costs in 2020–2023, and only 1.6% in 2024–2025. Its trading costs were 1.4% of its risk in 2020–2023, and 1.2% in 2024–2025.

  2. To see what the 9 EMA changes, we ran the same 6 tests with VWAP alone: a trade on every candle that closes on the other side of VWAP, with the same stop-loss, the same 16:00 close and the same 1% risk. VWAP alone uses the same rules as our VWAP cross backtest, which tested other markets. In 5 of the 6 tests, the 9 EMA cut the number of trades about in half: on the 5-minute charts, about 2.5 trades a day instead of about 5. The win rate (the share of trades that made money) was higher with the 9 EMA in all 6 tests. From 2020 to 2025, 5 of 6 tests made money with the 9 EMA, and 2 of 6 with VWAP alone. In 2024–2025, both lost money, and the 9 EMA lost less in all 5 tests where both traded. VWAP alone made no trades on MES's 1-minute chart after March 2023, because its account was too small (see the table below).

  3. With the 9 EMA, each trade also paid less in trading costs, compared with the money it risked, in all 6 tests. The 9 EMA crosses VWAP later than price does, when price is already further from VWAP. So the stop-loss (1 ATR beyond VWAP, where ATR is the average candle size over the last 14 candles) was further from the entry price, and for the same account balance, a 1% risk needed fewer contracts. Every contract pays the spread (the gap between the buy and the sell price) and a commission (the broker's fee). For example, on MNQ's 5-minute chart, half of the trades had a stop-loss more than 47.9 points from the entry price with the 9 EMA, and more than 34.8 points with VWAP alone.

  4. Trading costs took more on MES than on MNQ, on every chart (see the chart below). On the 1-minute charts, the costs of an average trade were 11.8% of the money it risked on MES and 4.3% on MNQ, and the MES account lost 97.7%. ES moves fewer points than NQ, so the stop-loss was closer: on the 1-minute charts, half of the MES stop-losses were less than 4.4 points from the entry price, and half of the MNQ stop-losses less than 23.8 points. The spread is 1 tick (0.25 points) on both, so it took a bigger part of the MES stop-loss.

  5. The 9 EMA mostly removed short trades. With VWAP alone, price often closed back across VWAP within a few candles, and that closed the trade: on the 5-minute charts, half of the VWAP-alone trades lasted 20 minutes or less. The 9 EMA moves more smoothly, so it often stayed on its side of VWAP while price crossed back for a candle or two, and the trade stayed open. On the 5-minute charts, half of the trades with the 9 EMA lasted 70 minutes or more. In all 6 tests, the 9 EMA had more trades that lasted 4 hours or more: 3,318 in total, compared with 2,317 with VWAP alone (see the chart below).

What the 9 EMA changes

In 5 of 6 tests, the 9 EMA cut the trades about in half and made the max drawdown smaller

Each row is one market and chart, tested two ways, after trading costs. In each cell, the first number is VWAP alone and the second is with the 9 EMA. VWAP alone opens a trade on every candle that closes on the other side of VWAP. With the 9 EMA, a trade opens when the 9 EMA crosses VWAP: the strategy on this page. Both close a trade when their line crosses back, and both use the same stop-loss, the same 16:00 close and the same 1% risk. Max drawdown is the biggest fall from the highest account balance. The 2024–2025 result is the change in the account from the start of 2024.

Market Chart Trades Result 2020–2025 Result 2024–2025 Max drawdown
MNQ 1-minute 16,345 → 8,754 −96.9% → +28.6% −85.3% → −47.0% −99.4% → −88.8%
MNQ 5-minute 8,402 → 3,686 +174.8% → +79.0% −47.6% → −26.7% −79.2% → −49.5%
MNQ 15-minute 4,377 → 1,882 +109.4% → +90.8% −29.2% → −1.0% −49.8% → −22.2%
MES 1-minute 3,595 → 6,537 −98.7% → −97.7% No trades → −76.0% −98.7% → −98.7%
MES 5-minute 6,801 → 3,614 −95.4% → +11.7% −64.6% → −6.3% −96.6% → −52.6%
MES 15-minute 4,303 → 1,949 −59.3% → +17.5% −21.9% → −15.1% −67.5% → −29.3%

On MNQ's 5-minute and 15-minute charts, VWAP alone made more from 2020 to 2025, but its max drawdown was much bigger. On MES's 1-minute chart, the VWAP-alone account was down 90% by August 2020. After March 2023, it was too small to risk 1% on even one MES contract, so it made no more trades. That is why it made fewer trades than the 9 EMA version.

Trading costs

How much of the profit before costs went to trading costs

Each bar shows one test with the basic rules, from 2020 to 2025. It shows the trading costs (the spread and the commission) as a % of what the trades made before costs. Under 100%, the test made money. Over 100%, the test lost money.

  • MNQ, 1-minute8,754 trades 97%
  • MNQ, 5-minute3,686 trades 66%
  • MNQ, 15-minute1,882 trades 32%
  • MES, 1-minute6,537 trades 156%
  • MES, 5-minute3,614 trades 96%
  • MES, 15-minute1,949 trades 83%

Each trade risked 1% of the account at that time, so the dollar amounts grew and shrank with the account. For example, on MNQ's 1-minute chart, the trades made $444,708 before costs and paid $430,401 in costs.

How long trades lasted

The 9 EMA cut the short trades by more than half, and kept more trades open for hours

Each bar is a number of trades from all 6 tests, 2020–2025, grouped by how long the trade stayed open. Light grey bars are VWAP alone. Amber bars are with the 9 EMA (the basic rules).

  • Under 1 hour, VWAP alone75.8% of its trades 33,206
  • Under 1 hour, with the 9 EMA58.6% of its trades 15,487
  • 1 to 4 hours, VWAP alone18.9% of its trades 8,300
  • 1 to 4 hours, with the 9 EMA28.8% of its trades 7,617
  • 4 hours or more, VWAP alone5.3% of its trades 2,317
  • 4 hours or more, with the 9 EMA12.6% of its trades 3,318

With the 9 EMA, the trades that lasted less than an hour lost 0.71R on average, and the trades that lasted 4 hours or more made 2.33R on average. R is the amount a trade risked: 1% of the account. VWAP alone made no trades on MES's 1-minute chart after March 2023, so its counts there are lower.

The strategy

What the VWAP + 9 EMA crossover is

VWAP stands for volume-weighted average price. In this test, VWAP is the average price since 09:30 New York time, when the US stock market opens. Candles with more volume (more buying and selling) count more in this average, and VWAP starts again every day. The 9 EMA (exponential moving average) is a moving average of closing prices, set to 9 candles. It gives the most weight to the newest candles, so it stays close to price but moves more smoothly. Many day traders treat price above VWAP as a sign that buyers are stronger that day, and price below VWAP as a sign that sellers are stronger. A common way to trade the two lines together: buy when the 9 EMA crosses above VWAP, and sell short when it crosses below. Traders use the 9 EMA instead of price itself because one candle can close on the other side of VWAP, and the next candle can close back on the first side. The 9 EMA moves more smoothly, so it crosses VWAP less often. We tested MNQ (Micro Nasdaq 100) and MES (Micro S&P 500) futures. They follow the same prices as NQ and ES, at one tenth of the size. We did not test the full-size NQ and ES contracts. With a stop-loss 1 ATR beyond VWAP, one full-size contract often risked more than 1% of a $50,000 account, so the backtest would have skipped many signals: on the 5-minute chart, 92% of the NQ signals and 54% of the ES signals. For the same reason, we left out YM (Dow) and RTY (Russell 2000) futures: our data has only their full-size contracts.

VWAP
From 09:30 to 16:00 New York time. Each candle's typical price, (high + low + close) ÷ 3, weighted by the candle's volume.
9 EMA
An exponential moving average set to 9 candles, on closing prices
Volume
The volume in our price data. It is not the volume of the exchange, so our VWAP can differ from the VWAP on an exchange chart.
Stop-loss
1 ATR(14) below VWAP for a buy, and 1 ATR(14) above VWAP for a sell, set when the trade opens. ATR(14) is the average candle size over the last 14 candles.
Take-profit
None. The 9 EMA crossing back over VWAP, or 16:00 New York time, closes the trade.
Risk per trade
1% of the account balance at that time, rounded down to whole contracts
Fills
Signals are checked when a candle closes, and a trade opens or closes at the next candle's open. The stop-loss fills at its price, or at a candle's open if price opens past it. The backtest adds no slippage (a worse fill than the planned price).
Price data
BacktestingMax's futures data. It is built from CFD prices (a broker's price that follows the same market) and adjusted to match the real contracts: their price, trading hours, tick (the smallest price step) and roll (the switch from one contract month to the next). It is not exchange data. BacktestingMax is not affiliated with or endorsed by CME Group, Nasdaq or S&P Dow Jones Indices.
Trading costs
The spread (the gap between the buy and the sell price) is 1 tick, 0.25 points: half is paid when a trade opens, and half when it closes. Each MNQ or MES contract also pays a commission (the broker's fee) of $0.62 when the trade opens, and $0.62 when it closes.

The rules we tested

  1. Trade only from 09:30 to 16:00 New York time, the hours of the US stock market. VWAP starts at 09:30 and counts the candles until 16:00. The 9 EMA is calculated on the closing prices of the same chart, including the candles before 09:30.
  2. Buy when the 9 EMA crosses above VWAP and the candle closes above both lines. We check when each candle closes, and the trade opens at the next candle's open. If that candle does not close above both lines, buy after the first later candle that does, as long as the 9 EMA is still above VWAP. The first candle of the day can't be a cross, because VWAP starts on it.
  3. Sell (go short) when the 9 EMA crosses below VWAP and the candle closes below both lines. If that candle does not close below both lines, sell after the first later candle that does, as long as the 9 EMA is still below VWAP.
  4. Set the stop-loss when the trade opens: 1 ATR below VWAP for a buy, and 1 ATR above VWAP for a sell. ATR is an indicator of how far price moves in an average candle, measured over the last 14 candles of the same chart. VWAP and ATR are taken at the close of the candle that gave the signal. The stop-loss does not move after that.
  5. When the 9 EMA crosses to the other side of VWAP, close the trade at the next candle's open. That cross is also a signal in the new direction, under rules 2 and 3. The new trade can't open on the candle where the old trade closes, so the earliest it can open is one candle later. After a stop-loss, wait for the next cross.
  6. Close any open trade at 16:00 New York time. A signal on the last candle before 16:00 doesn't open a trade: the candle that opens at 15:59 on the 1-minute chart, at 15:55 on the 5-minute chart and at 15:45 on the 15-minute chart. Don't trade on days when the New York Stock Exchange is closed or closes early (at 13:00).
  7. Size every trade so that a loss at the stop-loss costs 1% of the account balance at that time. Futures trade in whole contracts, so round down to a whole number of contracts. If even one contract would risk more than 1% of the account, skip the signal.

The method

How we tested it

The test

Markets
MNQ (Micro Nasdaq 100) and MES (Micro S&P 500)
Charts
1-minute, 5-minute and 15-minute candles
Dates
January 2020 to December 2025
Account
$50,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 2020 to 2023 and again from 2024 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 2024 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 VWAP + 9 EMA crossover be improved?

We tried three changes that traders often make to this strategy: 'Wait for a pullback to the 9 EMA', 'Exit when a candle closes on the other side of the 9 EMA' and 'Only the first 2 hours after the open'. The table below explains each one. We ran each change on the same 6 tests. We picked the best change using 2020–2023 only: the change that made money in the most tests, and in a tie, the one with the better middle test. Then we checked it on 2024–2025, the years we did not use to pick it. If you pick a change and judge it on the same years, it almost always looks better than it really is.

Version Made money 2020–2023years used to pick the best version Made money 2024–2025years not used to pick it Made money in both periods Passed all four checks
Basic rules The rules under 'The rules we tested', with none of the changes below. 5 of 6middle test +65.5% 0 of 6middle test −20.9% 0 0
Wait for a pullback to the 9 EMABest on 2020–2023 After the 9 EMA crosses VWAP, don't buy at once. Wait for the first candle that dips to the 9 EMA and closes back above it (and above VWAP), then buy at the next candle's open. The candle where the 9 EMA crosses doesn't count. For sells, do the opposite. One trade per cross, with the same stop-loss and the same exits. 2 of 6middle test −29.4% 1 of 6middle test −22.8% 0 0
Exit when a candle closes on the other side of the 9 EMA Also close a buy when a candle closes below the 9 EMA, and a sell when a candle closes above it. Don't wait for the 9 EMA to cross back over VWAP. After this exit, wait for the next cross. 1 of 6middle test −40.6% 1 of 6middle test −7.8% 1 0
Only the first 2 hours after the open Open new trades only from 09:30 to 11:30 New York time: the candle that gives the signal must close by 11:30. A trade opened before then closes on the normal rules. 1 of 6middle test −3.8% 3 of 6middle test +5.2% 1 0

No change made money in most tests in 2024–2025, and none beat the basic rules on 2020–2023, when the basic rules made money in 5 of 6 tests. 'Wait for a pullback to the 9 EMA' was the best change on 2020–2023, with 2 of 6 tests (MNQ 5-minute and 15-minute). In 2024–2025, it made money in 1 of 6 (MES 5-minute, +17.2%). 'Only the first 2 hours after the open' was the best change in 2024–2025, with 3 of 6 tests (MNQ 1-minute and 5-minute, MES 5-minute). 'Exit when a candle closes on the other side of the 9 EMA' lost money from 2020 to 2025 on all four 1-minute and 5-minute tests. It closed the long trades early. With the basic rules, 2,300 trades on those four tests lasted 4 hours or more and made 3.03R on average. With this exit, none lasted that long. Two single tests made money in both periods. One was 'Only the first 2 hours after the open' on MNQ's 1-minute chart: +30.5% in 2020–2023 and +30.4% in 2024–2025. The other was 'Exit when a candle closes on the other side of the 9 EMA' on MNQ's 15-minute chart: +18.2% and +16.5%. One test each could be luck.

Questions

Questions traders ask

Does the 9 EMA and VWAP strategy work?

Not in 2024 and 2025. From 2020 to 2023, it made money in 5 of our 6 tests, but all 6 lost money in 2024–2025. We tested MNQ and MES futures (the micro contracts of NQ and ES) on 1-minute, 5-minute and 15-minute charts, after trading costs. The best test, MNQ on the 15-minute chart, grew a $50,000 account by 90.8% from 2020 to 2025, risking 1% per trade. Its max drawdown (the biggest fall from its highest balance) was 22.2%, and it lost 1.0% in 2024–2025.

Is the 9 EMA crossing VWAP better than a plain VWAP cross?

Partly. In our backtest, adding the 9 EMA made the max drawdown smaller in 5 of 6 tests, and it lost less in 2024–2025 in all 5 tests where both versions traded. But it did not always make more money. VWAP alone (a plain VWAP cross) opens a trade on every candle that closes on the other side of VWAP. With the 9 EMA, the backtest made about half as many trades in 5 of 6 tests, more of the trades won, and each trade paid less in trading costs. From 2020 to 2025, 5 of 6 tests made money with the 9 EMA, compared with 2 of 6 with VWAP alone. On MNQ's 5-minute and 15-minute charts, VWAP alone made more over the six years (+174.8% and +109.4%, compared with +79.0% and +90.8%). But its max drawdown was bigger, and it lost more in 2024–2025.

What is the best timeframe for the VWAP 9 EMA strategy on NQ and ES?

In our backtest, the 15-minute chart did best and the 1-minute chart did worst. On MNQ, the 15-minute chart made +90.8% from 2020 to 2025, with the smallest max drawdown of all 6 tests (22.2%). On the 1-minute chart, MNQ made +28.6%, but its max drawdown was 88.8%, and MES lost 97.7%. On MNQ, the 1-minute chart gave about 6 trades a day, and every trade pays the spread and the commission. On MNQ's 1-minute chart, the costs took 97% of what the trades made before costs. Even on the 15-minute chart, both MNQ and MES lost money in 2024–2025.

Is VWAP + 9 EMA a good MNQ strategy?

Not in 2024 and 2025. In our backtest, it did better on MNQ than on MES, but it lost money on all three MNQ charts in 2024–2025. From 2020 to 2023, it made money on all three: +142.6% on the 1-minute chart, +144.2% on the 5-minute chart and +92.7% on the 15-minute chart. Our VWAP cross backtest found the same pattern on the Nasdaq 100. MNQ trades paid less in trading costs than MES trades, compared with the money each trade risked. NQ moves more points than ES, so the stop-loss was more points away, and the 1-tick spread was a smaller part of it.

Keep exploring

Related strategies we tested

VWAP VWAP cross strategy backtest Made money in 3 of 21 tests, 2 of them in both periods. Best: DAX, 15-minute, +215% Moving averages 9/21 EMA crossover backtest: from 5-minute to daily charts Made money in 10 of 40 tests, 5 of them in both periods. Best: USD/JPY, 1-hour, +539% 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 Vwap9Ema(Strategy):
    """VWAP + 9 EMA on US stock index futures, as day traders learn it: trade when the
    9 EMA crosses the session VWAP. Plus the changes traders argue about, and a control.
    VWAP (volume-weighted average price) is the average price of the session so far,
    weighted by volume: the sum of (high + low + close) / 3 x volume over the session's
    candles, divided by the sum of their volume. The session is the New York stock market's
    hours, 09:30-16:00 New York time (daylight saving included), and VWAP starts again at
    every session open. The 9 EMA is the exponential moving average of the last 9 closes on
    the same chart, overnight candles included.
    Standard (all switches off): when the 9 EMA crosses above VWAP, buy as soon as a candle
    closes above both lines (almost always the cross candle itself), as long as the 9 EMA is
    still above VWAP. Sell short on the opposite cross, once a candle closes below both.
    The trade opens at the next candle's open. The stop-loss sits 1 x ATR(14) beyond VWAP
    (below it for a buy, above it for a sell), set when the trade opens. The opposite cross
    closes the trade, and the next candle opens the trade in the new direction if it closes
    beyond both lines. After a stop-loss, wait for the next cross. Size so that the stop
    costs 1% of the account, in whole contracts; if one contract would risk more, skip the
    signal. No new trade on the session's last candle; a trade still open then closes at
    16:00. No trading on days the New York Stock Exchange is closed or closes early (13:00).
    The changes, one at a time:
      pullback  = 1   after the cross, don't buy at once: wait for the first candle that dips
                      to the 9 EMA and closes back above it (and above VWAP); the cross candle
                      itself doesn't count. Sells mirror this.
      ema_exit  = 1   also close the trade when a candle closes back through the 9 EMA (below
                      it for a buy); then wait for the next cross
      morning   = 1   only open trades in the first 2 hours: signal candles that close by 11:30
    The control (the same rules without the 9 EMA, to see what the 9 EMA changes):
      vwap_only = 1   the close crossing VWAP is the signal, as in a plain VWAP cross
    """
    params = {"ema_period": Param(type=int, default=9, min=2, max=50),
              "atr_mult": Param(type=float, default=1.0, min=0.25, max=5.0),
              "pullback": Param(type=int, default=0, min=0, max=1),
              "ema_exit": Param(type=int, default=0, min=0, max=1),
              "morning": Param(type=int, default=0, min=0, max=1),
              "vwap_only": Param(type=int, default=0, min=0, max=1)}
    lookback = 2
    session_open = 570    # 09:30 New York, in minutes after midnight
    session_close = 960   # 16:00 New York
    # Days with no trading, 2020-2025: the New York Stock Exchange is closed, or it
    # closes early at 13:00.
    closed_days = (
        "2020-01-01 2020-01-20 2020-02-17 2020-04-10 2020-05-25 2020-07-03 2020-09-07 2020-11-26 "
        "2020-11-27 2020-12-24 2020-12-25 "
        "2021-01-01 2021-01-18 2021-02-15 2021-04-02 2021-05-31 2021-07-05 2021-09-06 2021-11-25 "
        "2021-11-26 2021-12-24 "
        "2022-01-17 2022-02-21 2022-04-15 2022-05-30 2022-06-20 2022-07-04 2022-09-05 2022-11-24 "
        "2022-11-25 2022-12-26 "
        "2023-01-02 2023-01-16 2023-02-20 2023-04-07 2023-05-29 2023-06-19 2023-07-03 2023-07-04 "
        "2023-09-04 2023-11-23 2023-11-24 2023-12-25 "
        "2024-01-01 2024-01-15 2024-02-19 2024-03-29 2024-05-27 2024-06-19 2024-07-03 2024-07-04 "
        "2024-09-02 2024-11-28 2024-11-29 2024-12-24 2024-12-25 "
        "2025-01-01 2025-01-09 2025-01-20 2025-02-17 2025-04-18 2025-05-26 2025-06-19 2025-07-03 "
        "2025-07-04 2025-09-01 2025-11-27 2025-11-28 2025-12-24 2025-12-25")
    def init(self, ctx):
        self.ny = ctx.clock("America/New_York")
        self.no_trading = set(self.closed_days.split())
        self.ema = ctx.ema(source=ctx.close, period=ctx.params.ema_period)
        self.atr = ctx.atr(period=14)
        self.day = None      # the date of the session being traded
        self.pv = 0.0        # this session: sum of typical price x volume
        self.vol = 0.0       # this session: sum of volume
        self.side = 0        # +1 / -1: the signal line (the 9 EMA, or the close) is above / below VWAP
        self.armed = 0       # +1 / -1: a cross in that direction is waiting for its trade (0: none)
    def on_bar(self, ctx):
        start = self.ny.minute_of_day              # this candle's open, New York time
        end = start + ctx.timeframe_minutes        # this candle's close
        # VWAP counts the session's candles only, and starts again at each session open.
        in_session = (self.session_open <= start < self.session_close
                      and self.ny.date not in self.no_trading)
        if in_session and self.ny.date != self.day:
            self.day = self.ny.date
            self.pv, self.vol, self.side, self.armed = 0.0, 0.0, 0, 0
        if in_session:
            self.pv += ctx.hlc3[0] * ctx.volume[0]
            self.vol += ctx.volume[0]
        vwap = self.pv / self.vol if in_session and self.vol > 0 else None
        ema = self.ema.value
        close = ctx.close[0]
        line = close if ctx.params.vwap_only == 1 else ema
        side = 0 if vwap is None or line is None else (1 if line > vwap else -1)
        prev_side, self.side = self.side, side
        crossed = prev_side != 0 and side != 0 and side != prev_side
        if crossed:
            self.armed = side  # a cross: a new trade may open in its direction
        if not in_session:
            if ctx.position is not None:
                ctx.close_position()  # only if the session's last candle was missing from the data
            return
        if vwap is None or ema is None:
            return
        last_candle = end >= self.session_close
        pos = ctx.position
        if pos is not None:
            against = (pos.is_long and side < 0) or (pos.is_short and side > 0)
            ema_out = ctx.params.ema_exit == 1 and ((pos.is_long and close < ema)
                                                    or (pos.is_short and close > ema))
            if last_candle or against or ema_out:
                ctx.close_position()  # fills at the next candle's open
            return
        if last_candle:
            return
        a = self.atr.value
        if a is None:
            return
        if not ctx.gate("fresh_cross", self.armed != 0):
            return
        d = self.armed
        if ctx.params.vwap_only == 1:
            beyond = True  # the cross itself is a close beyond VWAP
        else:
            beyond = (close > vwap and close > ema) if d > 0 else (close < vwap and close < ema)
        if not ctx.gate("close_beyond_both_lines", beyond):
            return
        if ctx.params.pullback == 1:
            touched = ctx.low[0] <= ema if d > 0 else ctx.high[0] >= ema
            if not ctx.gate("pullback_to_9_ema", touched and not crossed):
                return
        minutes_open = end - self.session_open    # from 09:30 to this candle's close
        if not ctx.gate("first_2_hours", ctx.params.morning == 0 or minutes_open <= 120):
            return
        stop = vwap - ctx.params.atr_mult * a if d > 0 else vwap + ctx.params.atr_mult * a
        size = ctx.size_by_risk_pct(0.01, stop=stop, entry=close)
        self.armed = 0                             # one trade per cross: this signal is used
        if not ctx.gate("one_contract_fits_1pct_risk", size > 0):
            return                                 # one contract would risk more than 1%
        if d > 0:
            ctx.buy(size=size, sl=stop, tag="vwap_9ema_long")
        else:
            ctx.sell(size=size, sl=stop, tag="vwap_9ema_short")

Your turn

Test the VWAP + 9 EMA crossover yourself

Change the settings, the market or the stop-loss and see the result in seconds. Ask the BacktestingMax AI: Backtest the 9 EMA crossing VWAP on MNQ, 15-minute chart: VWAP from 09:30 New York time, stop-loss 1 ATR beyond VWAP, close when the 9 EMA crosses back or at 16:00, risk 1% per trade.

Try it free in BacktestingMax

Free account. No credit card needed.

See every strategy we tested Browse strategies shared by traders

Backtest on historical data with trading costs, run by BacktestingMax. Past results do not guarantee future results, and nothing here is financial advice. Updated 19 October 2026.