Strategies we backtested overnight gap fill strategy

Tested by BacktestingMax

Overnight gap fill backtest on NQ and ES futures

Does the overnight gap fill strategy work on NQ and ES futures?

Only a little on NQ, almost not at all on ES, and neither made money in 2024–2025. The overnight gap is the difference between the price at 09:30 New York time, when the US stock market opens, and the previous trading day's close at 16:00. The gap fill strategy trades against gaps of 0.25% to 1%. After a gap up, it sells short (sells first, to profit if price falls back). After a gap down, it buys. It takes profit at the previous close, where the gap fills. The stop-loss is one gap size beyond the 09:30 price, away from the previous close, and a trade still open at 16:00 closes then. We tested three futures from 2020 to 2025: MNQ and MES, the micro contracts of NQ (Nasdaq 100) and ES (S&P 500), and RTY (Russell 2000). Each trade risked 1% of a $50,000 account. In the backtest, after trading costs, the account grew 22.3% on MNQ and 4.6% on MES, and fell 11.8% on RTY. About half of the trades won, and a winning trade made about as much as a losing trade lost. The gap filled more often than the trades won. On MNQ, it filled by 16:00 on 58.0% of the days traded, but only 47.5% of the trades reached the take-profit first.

3 backtests 2020–2025 Trading costs included Tested 26 October 2026

Where it worked 3 backtests
Made money
2
of 3 tests, with the basic rules
Made money in both periods
0
2020–2023 and again 2024–2025
Best backtest
+22%
MNQ (Micro Nasdaq 100), 5-minute chart: account growth 2020–2025
Take profit at half the gap
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 overnight gap fill strategy 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 5-minute
MNQ (Micro Nasdaq 100) +22% Made money overall, lost in one period
MES (Micro S&P 500) +5% Made money overall, lost in one period
RTY (Russell 2000 futures) −12% Lost money
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) 5-minute 731 51.8% 1.07 +22.3% −15.3% +24.4% −1.7% −25.1% $4,504
MES (Micro S&P 500) 5-minute 730 50.7% 1.01 +4.6% −14.5% +9.1% −4.1% −29.0% $8,190
RTY (Russell 2000 futures) 5-minute 330 49.7% 0.91 −11.8% −25.4% −7.4% −4.8% −25.0% $3,110

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), 5-minute chart +22.3% from 2020 to 2025
Middle test (median): half of the 3 did better, half did worse MES (Micro S&P 500), 5-minute chart +4.6% from 2020 to 2025

What we found

What the 3 backtests show

  1. From 2020 to 2025, after trading costs, the basic rules made +22.3% on MNQ and +4.6% on MES, and lost 11.8% on RTY. We also split the test in two: 2020–2023, and the newer years, 2024–2025. A period's result is the change in the account in those years only. With the basic rules, no market made money in both periods. MNQ made +24.4% in 2020–2023 and then lost 1.7% in 2024–2025. MES made +9.1% and then lost 4.1%, and RTY lost money in both. The profit factor (the money won divided by the money lost) was 1.07 on MNQ, 1.01 on MES and 0.91 on RTY, so all three were close to break-even. On MES, trading costs took $8,190 of the $10,505 that the trades made before costs. Costs were high on MES because a typical MES trade needed 5 contracts to risk 1%, compared with 3 on MNQ, and every contract pays the spread and the commission.

  2. The gap filled more often than the trade won. On MNQ, price came back to the previous close by 16:00 on 424 of the 731 days the strategy traded (58.0%). But only 347 trades (47.5%) reached the take-profit before the stop-loss. On 77 days (10.5%), price first moved one more gap size away from the previous close, which hit the stop-loss, and filled the gap only after that. On MES, the gap filled on 51.6% of the days, and 42.9% of the trades reached the take-profit first. So a high gap fill rate does not mean as many winning trades, because on some days price hits the stop-loss before it fills the gap. The table below shows how often, for each gap size.

  3. Many traders say that gaps inside the previous day's range fill more often. The range is the area between the high and the low of the previous day's stock market session, from 09:30 to 16:00. In our test, these gaps did fill more often: on MNQ, 60.8% of the inside gaps filled by 16:00, compared with 54.4% of the gaps outside the range. But as trades, the inside gaps lost money on all 3 markets (see the chart below). On MNQ, inside-gap trades hit the stop-loss first about as often as they reached the take-profit first (46.2% and 45.8%), and they lost money. Outside-gap trades reached the take-profit first much more often than they hit the stop-loss (49.7% and 34.9%). The outside gaps made money on all 3 markets, but we noticed that only after we saw the results, so treat it as an idea to test.

  4. Smaller gaps filled more often, but they were not always the better trades. On MNQ, 67.1% of the gaps of 0.25% to 0.5% filled by 16:00, compared with 46.6% of the gaps of 0.75% to 1%. To compare the trades, we use R: 1R is 1% of the account at the time of the trade, the amount each trade was sized to risk. On MNQ, the 343 trades on small gaps (0.25% to 0.5%) made +5.6R, and the 161 trades on big gaps (0.75% to 1%) made +13.0R. On MES, the small gaps did better: +18.8R in 420 trades, while the big gaps lost 8.7R in 107 trades. So the gap size gave no rule that held on both markets. On a big gap, the take-profit and the stop-loss are both far away. So many trades reached neither and closed at 16:00: 27.3% on MNQ and 41.1% on MES, compared with 2.3% and 9.5% on small gaps.

  5. Many trades ended at the take-profit or the stop-loss soon after 09:30, especially on MNQ. On MNQ, half of the trades that reached the take-profit got there within 28 minutes of 09:30, and 87% got there by 12:00. Half of the stop-losses were hit within 26 minutes. On MES, it took longer: 49 minutes for half of the take-profits, and 63 minutes for half of the stop-losses. The direction of the first 5 minutes made a big difference. On some days, the first 5-minute candle closed toward the previous close: lower than it opened after a gap up, higher after a gap down. On those days, the MNQ trades made +134.5R in total. On the other days, they lost 111.8R. But these trades opened at 09:30, so those results include the first 5 minutes, which you miss if you wait for that candle to close. The version that waits until 09:35 made only +9.2% on MNQ from 2020 to 2025 (+9.4R in total).

Gap fill or winning trade

The gap filled more often than the trade won

Each row is one market and one gap size, or all gap sizes together ('All'), with the basic rules, from 2020 to 2025. 'Gap filled by 16:00' is the share of days when price came back to the previous close at any time between 09:30 and 16:00 New York time. The next three columns show how the trades ended: at the take-profit (the gap filled before the stop-loss was hit), at the stop-loss, or at 16:00. 'Total in R' adds up the results of the trades, after trading costs: 1R is 1% of the account at the time of the trade.

Market Gap size Days traded Gap filled by 16:00 Take-profit first Stop-loss first Still open at 16:00 Total in R
MNQ 0.25% to 0.5% 343 67.1% 50.7% 46.9% 2.3% +5.6R
MNQ 0.5% to 0.75% 227 52.4% 45.8% 41.0% 13.2% +4.0R
MNQ 0.75% to 1% 161 46.6% 42.9% 29.8% 27.3% +13.0R
MNQ All 731 58.0% 47.5% 41.3% 11.2% +22.7R
MES 0.25% to 0.5% 420 61.9% 49.5% 41.0% 9.5% +18.8R
MES 0.5% to 0.75% 203 40.9% 36.9% 37.4% 25.6% −3.1R
MES 0.75% to 1% 107 31.8% 28.0% 30.8% 41.1% −8.7R
MES All 730 51.6% 42.9% 38.5% 18.6% +7.1R
RTY All 330 71.8% 49.7% 50.3% 0.0% −11.5R

The difference between 'Gap filled by 16:00' and 'Take-profit first' is the days when price hit the stop-loss first and filled the gap later. That happened in 77 MNQ trades, 64 MES trades and 73 RTY trades. Futures trade in whole contracts, so most trades risked a little less than 1% of the account: on MNQ, a trade that hit the stop-loss lost 0.88R on average. RTY traded only on days when one contract fit the 1% risk, so 294 of its 330 trades were on gaps of 0.25% to 0.5%. All of its trades ended at the take-profit or the stop-loss before 16:00. A total in R is a sum of trades, so it is not the same as the % change in the account.

Inside or outside the previous day's range

Gaps outside the previous day's range made money; gaps inside it lost

Each bar adds up the results of the trades with the basic rules, from 2020 to 2025, after trading costs. An inside gap opens at 09:30 between the high and the low of the previous day's 09:30 to 16:00 session. An outside gap opens above that high or below that low. The results are in R: 1R is 1% of the account at the time of the trade. Green bars made money in total; red bars lost money.

  • MNQ, outside gaps318 trades +34.5R
  • MNQ, inside gaps413 trades −11.9R
  • MES, outside gaps368 trades +12.2R
  • MES, inside gaps362 trades −5.2R
  • RTY, outside gaps102 trades +11.4R
  • RTY, inside gaps228 trades −22.9R

We tested inside gaps because traders say they fill more often. The inside gaps lost money in both 2020–2023 and 2024–2025, on all 3 markets. The outside gaps made money in both periods on MNQ (+27.6R, then +7.0R) and on RTY (+6.9R, then +4.5R). On MES, they made +14.1R and then lost 1.8R. We noticed that the outside gaps made money only after we saw the results, so treat it as an idea to test, not as a rule. That is why 'only outside gaps' is not one of our versions.

Every version

Each version on each market

Each row is one version of the strategy on one market. The results are the change in the account, after trading costs, risking 1% of the account per trade. 'Basic rules' are the rules under 'The rules we tested' further down. Each other version adds one change to them, explained under 'Can the overnight gap fill strategy be improved?' below.

Market Version Trades 2020–2025 2020–2023 2024–2025
MNQ Basic rules 731 +22.3% +24.4% −1.7%
MNQ Take profit at half the gap 731 +0.2% +8.0% −7.2%
MNQ Only gaps inside the previous day's range 413 −13.0% −4.1% −9.3%
MNQ Wait for the first 5 minutes 338 +9.2% +5.3% +3.7%
MNQ Wait for the first 15 minutes 257 +9.3% +9.5% −0.2%
MES Basic rules 730 +4.6% +9.1% −4.1%
MES Take profit at half the gap 730 +2.4% +5.0% −2.4%
MES Only gaps inside the previous day's range 362 −7.3% −4.6% −2.9%
MES Wait for the first 5 minutes 364 +5.3% +0.8% +4.4%
MES Wait for the first 15 minutes 331 −9.0% −3.2% −6.0%
RTY Basic rules 330 −11.8% −7.4% −4.8%
RTY Take profit at half the gap 321 −8.2% −8.0% −0.2%
RTY Only gaps inside the previous day's range 216 −16.9% −10.5% −7.1%
RTY Wait for the first 5 minutes 40 −8.2% −5.0% −3.4%
RTY Wait for the first 15 minutes 11 −3.3% −2.9% −0.4%

The 2024–2025 result starts from the balance at the end of 2023, so the two periods don't add up to the 2020–2025 result. On MNQ, the biggest fall from the highest balance was 15.3% with the basic rules. It was 7.9% when waiting for the first 5 minutes, and 4.3% when waiting for the first 15 minutes. On RTY, the two waiting versions made too few trades to judge. RTY skipped a day when one contract risked more than 1% of the account, and 1% of a smaller balance is less money. Each version had its own balance, so on RTY the versions skipped different days. That is why taking profit at half the gap made 321 RTY trades, not 330. The inside-gap version made 216, not the 228 inside-gap trades of the basic rules.

The strategy

What the overnight gap fill strategy is

An overnight gap is the difference between two prices. The first is the price at 09:30 New York time, when the US stock market opens. The second is the close at 16:00 New York time on the previous trading day. Stock index futures such as NQ (Nasdaq 100) and ES (S&P 500) trade almost 24 hours a day. So for futures, the gap is how far price moved while the stock market was closed. We measure it as a percentage of the previous close. The gap fills when price comes back to that close. The gap fill strategy expects this to happen on the same day. So it trades against the gap, which traders call fading the gap. After a gap up, it sells short (sells first, to profit if price falls). After a gap down, it buys. Gap and go is the opposite trade on the same gaps: it trades in the direction of the gap. We traded gaps of 0.25% to 1%. Gaps this size happened on about half of the days: on MNQ, on 731 out of the 1,495 full trading days in the test. We tested 3 markets. MNQ (Micro Nasdaq 100) and MES (Micro S&P 500) follow the same prices as NQ and ES, but one contract is one tenth of the size. RTY (the E-mini Russell 2000) is ten times the size of its micro version, which is not in our data. RTY traded on 330 of its 771 gap days. A bigger gap puts the stop-loss further away. On 440 of the other days, one RTY contract would have risked more than 1% of the account, so the backtest skipped them. So most RTY trades were on small gaps. We left out YM (the E-mini Dow): for the same reason, it could trade on only 96 of its 687 gap days.

Gap
The price at 09:30 New York time compared with the previous trading day's close at 16:00, as a % of that close. Traded: gaps of 0.25% to 1%, up or down.
Entry
At 09:30 New York time, against the gap: sell short after a gap up, buy after a gap down
Take-profit
The previous trading day's close, where the gap fills
Stop-loss
1 gap size beyond the 09:30 price, as far from it as the take-profit
Exit
At 16:00 New York time, if neither the take-profit nor the stop-loss was hit
Trades per day
1 at most
Risk per trade
1% of the account, rounded down to whole contracts
Chart
5-minute candles. On a 15-minute chart, the basic rules gave the same trades: the entry time, the take-profit, the stop-loss and the 16:00 exit don't depend on the candles. The chart matters only for the versions that wait for the first 5 or 15 minutes.
Order prices
The stop-loss closes the trade at the stop-loss price, or at a candle's open if price opens beyond it. The take-profit closes the trade at exactly its price. When price reaches both in the same candle, the backtest looks at the 1-minute candles inside it to see which came first. If both are in the same 1-minute candle, it counts the stop-loss.
Price data
BacktestingMax's futures data. It is built from CFD prices (a broker's price that follows the same index). We adjust them 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.
Trading costs
The spread is the difference between the buy price and the sell price. We used 1 tick: 0.25 points on MNQ and MES, and 0.1 points on RTY. Half of the spread is paid when a trade opens, and half when the trade closes. The take-profit is a limit order, so it pays no spread: the backtest closes the trade at exactly the previous close as soon as price touches it. In live trading, a limit order is not always filled when price only touches it. In this test, price went at least 1 tick past the take-profit in 818 of the 824 trades that reached it. There is also a commission when a trade opens and again when it closes: $0.62 per MNQ or MES contract, and $2.50 per RTY contract.

The rules we tested

  1. Each trading day, compare the price at 09:30 New York time with the previous trading day's close at 16:00 New York time. The gap is the difference, as a % of that close. On a 5-minute chart, the 16:00 close is the close of the candle from 15:55 to 16:00. The 09:30 price is the close of the candle from 09:25 to 09:30.
  2. Trade only when the gap is between 0.25% and 1% of the previous close.
  3. Trade against the gap: sell short after a gap up, and buy after a gap down. The trade opens at 09:30, at the open of the 5-minute candle from 09:30 to 09:35.
  4. Put the take-profit at the previous day's close. The trade wins if price comes back to that close (the gap fills) before it hits the stop-loss.
  5. Put the stop-loss one gap size beyond the 09:30 price: above it after a gap up, and below it after a gap down. So the stop-loss and the take-profit are the same distance from the 09:30 price.
  6. If the trade is still open at 16:00 New York time, close it then.
  7. Take one trade a day at most.
  8. 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 day.
  9. Don't trade on days the New York Stock Exchange is closed, or closes early at 13:00. After a holiday, the gap counts from the last close before it. After an early close, it counts from the 13:00 close.

The method

How we tested it

The test

Markets
MNQ (Micro Nasdaq 100), MES (Micro S&P 500) and RTY (Russell 2000 futures)
Charts
5-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 overnight gap fill strategy be improved?

Besides the basic rules, we tested four versions, each with one change that traders often make. One takes profit at half the gap. One trades only gaps inside the previous day's range. Two wait for the first 5 or the first 15 minutes to move toward the previous close before entering. Each version ran on the same 3 markets: MNQ, MES and RTY. We picked the best of the four changes using 2020–2023 only: the one that made money on the most markets. In a tie, we picked the one with the better middle result (the median of the 3 markets). Then we checked it on 2024–2025, the years we did not use to pick it. If you pick a version 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': enter at 09:30 and take profit at the previous day's close. The stop-loss is one gap size beyond the 09:30 price, and a trade still open at 16:00 closes then. 2 of 3middle test +9.1% 0 of 3middle test −4.1% 0 0
Take profit at half the gapBest on 2020–2023 The take-profit is halfway between the 09:30 price and the previous close, so the trade wins when price has filled half the gap. The stop-loss stays one gap size away, so a winning trade makes about half of what a losing trade costs. Many gap traders take profit at half the gap, because price gets there more often. 2 of 3middle test +5.0% 0 of 3middle test −2.4% 0 0
Only gaps inside the previous day's range Trade only when the 09:30 price is between the high and the low of the previous day's stock market session (09:30 to 16:00 New York time). Many traders say that these gaps fill more often than gaps that open above the previous high or below the previous low. 0 of 3middle test −4.6% 0 of 3middle test −7.1% 0 0
Wait for the first 5 minutes Don't enter at 09:30. Wait until 09:35, and enter only if the first 5-minute candle closed toward the previous close: lower than it opened after a gap up, higher after a gap down. The trade opens at the next candle's open, with the same take-profit and stop-loss prices as the basic rules. If the first 5 minutes moved away from the previous close, or already reached the take-profit or the stop-loss, there is no trade that day. 2 of 3middle test +0.8% 2 of 3middle test +3.7% 2 0
Wait for the first 15 minutes The same as waiting for the first 5 minutes, but it waits for the first 15 minutes, from 09:30 to 09:45 (the first candle on a 15-minute chart). The trade opens at 09:45. 1 of 3middle test −2.9% 0 of 3middle test −0.4% 0 0

By our pick rule, no change beat the basic rules on 2020–2023. The basic rules made money on 2 of the 3 markets, and on MNQ (+24.4%) and MES (+9.1%) they made more than any change. Of the four changes, taking profit at half the gap came first by that rule. It made money on 2 markets, +8.0% on MNQ and +5.0% on MES, and had a better middle result than waiting for the first 5 minutes. On 2024–2025, the years we kept for the check, it lost money on all 3 markets: −7.2% on MNQ, −2.4% on MES and −0.2% on RTY. With it, 2 trades in 3 won, but on MNQ a trade that reached the take-profit made about 0.4R, and a trade that hit the stop-loss lost about 0.85R. Waiting for the first 5 minutes was the only change that made money in both periods. It did so on MNQ (+5.3% in 2020–2023 and +3.7% in 2024–2025) and on MES (+0.8% and +4.4%). It traded on about half of the days the basic rules traded: 338 of 731 on MNQ. But it made only a little, and it did not pass all four of our checks. Our pick on 2020–2023 chose a different version. Choosing this one now, because of its 2024–2025 result, would mean picking and judging on the same years. From 2020 to 2025, waiting for the first 15 minutes made +9.3% on MNQ, but it lost 9.0% on MES. On RTY, the two waiting versions made only 40 and 11 trades. Waiting moves the entry away from the stop-loss, so one RTY contract more often risked more than 1% of the account. Trading only gaps inside the previous day's range lost money on all 3 markets, in both periods. So no change made the gap fill clearly better. Waiting for the first 5 minutes held up best in 2024–2025, but it made only a little.

Questions

Questions traders ask

Does the NQ gap fill strategy work?

A little, in our backtest, but not in 2024–2025. We tested MNQ, the micro contract that follows the same price as NQ (Nasdaq 100 futures). Our rules trade against gaps of 0.25% to 1% at 09:30 New York time: they sell after a gap up and buy after a gap down. The take-profit is the previous 16:00 close. The stop-loss is one gap size beyond the 09:30 price, on the other side. From 2020 to 2025, after trading costs, the account grew 22.3%, risking 1% of a $50,000 account per trade. It made +24.4% in 2020–2023 and lost 1.7% in 2024–2025. At one point, the account was 15.3% below its highest balance. Waiting until 09:35 for the first 5 minutes to move toward the previous close made less (+9.2%), but it made money in both periods. Its biggest fall from the highest balance was 7.9%.

Does the ES gap fill strategy work?

Barely, in our backtest. We tested MES, the micro contract that follows the same price as ES (S&P 500 futures). Our rules trade against gaps of 0.25% to 1% at 09:30 New York time and take profit at the previous 16:00 close. The stop-loss is one gap size beyond the 09:30 price. From 2020 to 2025, after trading costs, MES made +4.6%: +9.1% in 2020–2023 and −4.1% in 2024–2025. Trading costs took $8,190 of the $10,505 that the trades made before costs. A typical MES trade needed 5 contracts for a 1% risk, and every contract pays the costs. The small gaps, of 0.25% to 0.5%, did best: +18.8R in 420 trades, where 1R is 1% of the account. The gaps of 0.75% to 1% lost 8.7R in 107 trades. Waiting for the first 5 minutes to move toward the previous close made +5.3%, and it made money in both periods.

How often do overnight gaps fill on NQ and ES?

A gap fills when price comes back to the previous day's 16:00 close. From 2020 to 2025, 58.0% of the MNQ (NQ) gaps of 0.25% to 1% filled by 16:00 New York time, and 51.6% of the MES (ES) gaps. Smaller gaps filled more often: on MNQ, 67.1% of the gaps of 0.25% to 0.5% filled, compared with 46.6% of the gaps of 0.75% to 1%. On MES, it was 61.9% compared with 31.8%. Fills that came before the stop-loss came early: on MNQ, half of the trades that reached the take-profit got there within 28 minutes of 09:30. But a fill is not a winning trade. On some days, price first moved one more gap size away, which hit the stop-loss, and filled the gap later. With the stop-loss of our basic rules, 47.5% of the MNQ trades and 42.9% of the MES trades reached the previous close first.

What is the best chart timeframe for the gap fill strategy?

With the basic rules, the chart makes no difference. The trade opens at 09:30, the take-profit and the stop-loss are fixed prices, and the trade closes at 16:00. In our backtest, the 5-minute and the 15-minute charts gave the same trades. The chart matters only if you wait for the first candle to move toward the previous close before you enter. Waiting for the first 5-minute candle made money on MNQ and MES in both 2020–2023 and 2024–2025, but only a little: +9.2% and +5.3% from 2020 to 2025. Waiting for the first 15-minute candle made +9.3% on MNQ, but it lost 9.0% on MES.

Keep exploring

Related strategies we tested

Gaps Gap and go backtest on NQ and ES futures Made money in 5 of 8 tests, 2 of them in both periods. Best: MES (Micro S&P 500), 5-minute, +115%
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 OvernightGapFill(Strategy):
    """Overnight gap fill on US stock index futures: fade the gap at the New York open,
    and take profit where the stock market closed the day before.
    The gap is the difference between the price at 09:30 New York time, when the US stock
    market opens, and the close at 16:00 New York time on the previous trading day (13:00
    on a half day), as a % of that close. Index futures trade almost around the clock, so
    the gap is how far the futures moved while the stock market was closed. The gap
    "fills" when price comes back to the previous 16:00 close.
    Standard (all switches off): when the gap is 0.25% to 1% of the previous close, fade
    it at 09:30. Sell short after a gap up; buy after a gap down. The gap is measured on
    the close of the candle that ends at 09:30, and the trade opens at the open of the
    09:30 candle. The take-profit is the previous close, so the trade wins when the gap
    fills. The stop-loss is 1 gap size beyond the 09:30 price, so the stop-loss and the
    take-profit are the same distance away. A trade still open at 16:00 New York closes
    then. One trade a day at most. Size every trade so that the stop-loss costs 1% of the
    account, in whole contracts; if one contract would risk more, skip the day. No trading
    on days the New York Stock Exchange is closed or closes early (13:00).
    The changes, one at a time:
      half_target     = 1   take profit halfway between the 09:30 price and the previous
                            close (half the gap); the stop-loss stays 1 gap size away
      inside_only     = 1   only trade when the 09:30 price is inside the previous day's
                            09:30-16:00 range (not above its high, not below its low)
      confirm_minutes = 5   don't enter at 09:30: wait until 09:35, and enter only if the
                            first 5 minutes moved toward the fill (closed below their open
                            after a gap up, above it after a gap down) without reaching the
                            take-profit or the stop-loss. The trade opens at the next
                            candle's open, with the same take-profit and stop-loss prices.
      confirm_minutes = 15  the same with the first 15 minutes (09:30 to 09:45)
    """
    params = {"min_gap_pct": Param(type=float, default=0.25, min=0.0, max=5.0),
              "max_gap_pct": Param(type=float, default=1.0, min=0.0, max=10.0),
              "stop_gaps": Param(type=float, default=1.0, min=0.1, max=5.0),
              "half_target": Param(type=int, default=0, min=0, max=1),
              "inside_only": Param(type=int, default=0, min=0, max=1),
              "confirm_minutes": Param(type=int, default=0, min=0, max=60)}
    lookback = 2
    session_open = 570    # 09:30 New York, in minutes after midnight
    session_close = 960   # 16:00 New York
    early_close = 780     # 13:00 New York, the close on a half day
    # Days the New York Stock Exchange is closed, 2020-2025: no stock market open, no trade,
    # and the next day's gap counts from the last close before them.
    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-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-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-12-26 "
        "2023-01-02 2023-01-16 2023-02-20 2023-04-07 2023-05-29 2023-06-19 2023-07-04 2023-09-04 "
        "2023-11-23 2023-12-25 "
        "2024-01-01 2024-01-15 2024-02-19 2024-03-29 2024-05-27 2024-06-19 2024-07-04 2024-09-02 "
        "2024-11-28 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-04 "
        "2025-09-01 2025-11-27 2025-12-25")
    # Half days: the stock market closes at 13:00. No trade; the 13:00 close is the close
    # the next day's gap counts from.
    half_days = (
        "2020-11-27 2020-12-24 2021-11-26 2022-11-25 2023-07-03 2023-11-24 "
        "2024-07-03 2024-11-29 2024-12-24 2025-07-03 2025-11-28 2025-12-24")
    def init(self, ctx):
        self.ny = ctx.clock("America/New_York")
        self.no_trading = set(self.closed_days.split())
        self.half = set(self.half_days.split())
        self.day = None
        self.trading_day = False   # True when today is a New York Stock Exchange trading day
        self.prev_close = None     # the previous trading day's close (16:00, or 13:00 on a half day)
        self.prev_high = None      # the previous trading day's 09:30-16:00 high
        self.prev_low = None       # ... and low
        self.close_today = None    # today's close, once the closing candle is in
        self.high_today = None     # today's high since 09:30
        self.low_today = None      # today's low since 09:30
        self.first_open = None     # the open of the 09:30 candle
        self.setup = None          # today's gap: (gap up?, take-profit, stop-loss)
        self.done = True           # True once today's trade is placed, or the day is skipped
    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
        if self.ny.date != self.day:               # a new day
            if self.trading_day:                   # the day that just ended becomes "the previous day"
                self.prev_close = self.close_today  # None if its closing candle was missing
                self.prev_high, self.prev_low = self.high_today, self.low_today
            self.day = self.ny.date
            self.trading_day = self.ny.weekday < 5 and self.day not in self.no_trading
            self.close_today = self.high_today = self.low_today = self.first_open = None
            self.setup = None
            self.done = not self.trading_day or self.day in self.half
        close_at = self.early_close if self.day in self.half else self.session_close
        # Today's stock market session (09:30 to 16:00): its high, its low and its close.
        if self.trading_day and self.session_open <= start < close_at:
            if start == self.session_open:
                self.first_open = ctx.open[0]
            self.high_today = ctx.high[0] if self.high_today is None else max(self.high_today, ctx.high[0])
            self.low_today = ctx.low[0] if self.low_today is None else min(self.low_today, ctx.low[0])
            if end == close_at:
                self.close_today = ctx.close[0]
        if ctx.position is not None:
            if end >= self.session_close or start < self.session_open:
                ctx.close_position()               # still open at 16:00: closes at the 16:00 candle's open
            return
        if self.done:
            return
        wait = ctx.params.confirm_minutes
        if self.setup is None:
            # The gap, measured at 09:30: the close of the candle that ends at 09:30.
            if end != self.session_open:
                return
            if not ctx.gate("previous_close_known", self.prev_close is not None and self.prev_high is not None):
                self.done = True
                return
            price = ctx.close[0]
            gap = price - self.prev_close          # above zero: a gap up; below zero: a gap down
            gap_pct = abs(gap) / self.prev_close * 100
            if not ctx.gate("gap_0_25_to_1_pct", ctx.params.min_gap_pct <= gap_pct <= ctx.params.max_gap_pct):
                self.done = True
                return
            if ctx.params.inside_only == 1 and not ctx.gate(
                    "opens_inside_previous_day_range", self.prev_low <= price <= self.prev_high):
                self.done = True
                return
            up = gap > 0
            stop = price + ctx.params.stop_gaps * gap   # beyond the 09:30 price, away from the fill
            target = price - gap / 2 if ctx.params.half_target == 1 else self.prev_close
            self.setup = (up, target, stop)
            if wait == 0:
                self._enter(ctx, up, price, target, stop)
            return
        # Waiting for the first minutes after 09:30 to move toward the fill.
        if end < self.session_open + wait:
            return
        self.done = True                           # decided at 09:30 + the wait, trade or not
        up, target, stop = self.setup
        if self.first_open is None or self.high_today is None:
            return
        toward = ctx.close[0] < self.first_open if up else ctx.close[0] > self.first_open
        if not ctx.gate("first_minutes_move_toward_fill", toward):
            return
        reached = (self.low_today <= target or self.high_today >= stop) if up else \
                  (self.high_today >= target or self.low_today <= stop)
        if not ctx.gate("target_and_stop_not_reached_yet", not reached):
            return
        self._enter(ctx, up, ctx.close[0], target, stop)
    def _enter(self, ctx, up, entry, target, stop):
        self.done = True                           # one trade a day
        if abs(stop - entry) <= 0:
            return
        size = ctx.size_by_risk_pct(0.01, stop=stop, entry=entry)
        if not ctx.gate("one_contract_fits_1pct_risk", size > 0):
            return                                 # one contract would risk more than 1%
        if up:
            ctx.sell(size=size, sl=stop, tp=target, tag="gap_up_fade")
        else:
            ctx.buy(size=size, sl=stop, tp=target, tag="gap_down_fade")

Your turn

Test the overnight gap fill strategy yourself

Change the settings, the market or the stop-loss and see the result in seconds. Ask the BacktestingMax AI: Backtest an overnight gap fill on MNQ, 5-minute chart: at 09:30 New York, fade gaps of 0.25% to 1% from the previous 16:00 close, take profit at that close, stop-loss 1 gap size beyond the 09:30 price, close at 16:00.

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 26 October 2026.