MN HOLD-SIDE — the midnight-open hold day
On ~33% of MNQ sessions, price never crosses the NY midnight open after 09:30. A four-step investigation — filter, target, entry, confluence — that produced a +55 pt/trade tier-1 setup and a bonus/penalty layer that lifted 16 other models in the suite.
updated 2026-04-24The question
Does the NY midnight open (00:00 ET 1-minute bar open) act as support/resistance during RTH, and if so, how do we trade it?
The obvious answer — “it’s a magnet, fade touches” — was the first experiment. The obvious answer was wrong. The tradable edge sits in the sessions that never reach the midnight open, and isolating those sessions in advance turned out to be a four-step investigation.
Part 1 — three modes on the midnight open
backtest_midnight_open.py ran three theses against 1,370 RTH sessions of MNQ 1-minute data:
| Mode | Thesis | Result |
|---|---|---|
| First-touch fade | Price touches MN after 09:30 → fade back | 47.8% close-favorable (coin flip) |
| Reclaim momentum | Price reclaims MN → continuation | Negative expectancy at every target (stop too wide; Q50 risk 37.8) |
| Hold-side | Price opens on one side and never crosses | 33.4% of sessions, Q50 excursion 172.5 pts vs 63.8 on cross days — the edge |
Hold days pay 2.7× on average excursion. The question became: can we filter for them before they play out?
Part 2 — the T3 classifier
mn_hold_classifier.py mined features (gap state, open side, distance bucket, 4H trend, DOW, opening-range ratio, gap/ATR ratio) against the hold/cross label. Three features produced usable lift:
| Feature | Hold rate | Base 33.5% |
|---|---|---|
|dist| ≥ 60 | 50.6% | +17 |
gap/ATR = XL | 65.8% | +32 |
on_range/ATR = XL | 68.3% | +35 |
The cleanest two-feature combinations:
- side BELOW × 4H BEAR × dist ≥ 60: 62.2% hold rate (n=188)
- side ABOVE × 4H BULL × dist ≥ 60: 60.6% hold rate (n=221)
That’s the T3 filter: |RTH_open − MN| ≥ 60 AND 4H SMC trend aligns with open side. It picks 30% of sessions with a ~60% hold rate (vs 33% base). 409 qualifying sessions in 4 years of history.
Part 3 — target sweep and distance cap
backtest_mn_hold_trades.py first confirmed T3 was the best of four candidate filters (T1 raw |dist|≥60, T2 dist+gap, T3 dist+trend, T4 dist+trend+range). T3 returned +28.14 pts/trade at target 300 on 409 trades — the winner.
But risk was unbounded: worst single loss was 424.5 pts (one wide-gap outlier on 2024-08-05). The next question: does capping the distance improve the risk profile?
backtest_mn_hold_entry_refine.py (Experiment A) swept 6 distance tiers:
| Tier | n | Risk MAX | Best target | Exp/tr | Total |
|---|---|---|---|---|---|
| T3 (baseline) | 409 | 471.8 | 300 | +28.14 | +11,508 |
| T3a [60–100] | 166 | 100.0 | 300 | +45.59 | +7,568 |
| T3b [60–120] | 244 | 120.0 | 300 | +35.81 | +8,738 |
| T3c [60–150] | 295 | 150.0 | 300 | +34.33 | +10,127 |
| T3d [60–200] | 341 | 199.2 | 300 | +39.09 | +13,330 |
| T3e [60–250] | 374 | 248.5 | 300 | +35.92 | +13,433 |
Every cap beats baseline on expectancy. T3a tightens risk to 100 pts and pushes exp/tr to +45.59, but throws away half the trade count. T3d caps at 200 pts and captures the highest total points. T3d won the deployment slot.
Part 4 — should we wait for a pullback?
backtest_mn_hold_entry_refine.py (Experiment B) measured max pullback toward MN within the first 15 / 30 / 60 min after 09:30, then simulated a “wait for ≥X pts pullback, enter at limit” rule:
| Pullback required | Trades taken | Win% | Exp/tr | Total |
|---|---|---|---|---|
| 0 (enter at 09:30 open) | 409 | 39.6% | +20.25 | +8,283 |
| ≥ 10 pts | 362 | 34.8% | +12.35 | +4,472 |
| ≥ 20 pts | 319 | 33.2% | +8.32 | +2,654 |
| ≥ 30 pts | 284 | 31.0% | +4.28 | +1,216 |
| ≥ 40 pts | 251 | 27.5% | −0.02 | −4 |
| ≥ 50 pts | 221 | 24.0% | −4.11 | −909 |
Every 10-pt of waited-for pullback cost ~8 pts of expectancy. On hold days, price doesn’t retrace — that’s the definition of a hold day. Waiting for a better fill is a trap.
Part 5 — what about filtering out chased trades?
The intuition: if price has already extended, say, 80 pts away from MN in the first 30 min, the “easy move” is gone, right? Skip it?
backtest_mn_hold_entry_refine.py (Experiment C) tested extension-based skip filters at 40/60/80/100 pts. Every filter destroyed edge. The conditional table showed why:
| First-30-min extension | n | Win% | Exp/tr |
|---|---|---|---|
| 0–20 pts | 96 | 10.4% | −65.51 |
| 20–40 pts | 66 | 22.7% | −10.43 |
| 40–60 pts | 62 | 17.7% | −9.33 |
| 60–80 pts | 42 | 40.5% | +28.55 |
| 80–100 pts | 39 | 66.7% | +79.65 |
| 100–150 pts | 70 | 70.0% | +91.90 |
| 150+ | 34 | 100% | +150.00 |
Early extension is the signal, not a warning. Sessions that trundle near MN for 30 min are where the losses live — price is still coiling and has room to reclaim. Once the market is 60+ pts away by 10:00, target hit becomes near-certain.
This flips the deployment logic: instead of skipping extended trades, we confirm them. Enter at 10:00 only if ≥60 pts of early extension is on the tape.
Part 6 — the stacked configuration
backtest_mn_hold_entry_D.py stacked distance cap × ext filter × target sweep. Best cells (exp/trade):
| Tier | No ext filter | ext ≥ 40 | ext ≥ 60 | ext ≥ 80 |
|---|---|---|---|---|
| T3 (all, 409) | +21.23 | +31.58 | +41.79 | +44.28 |
| T3a [60–100] | +31.84 | +40.54 | +54.16 | +52.50 |
| T3d [60–200] | +28.25 | +39.51 | +55.13 | +61.49 |
Two deployable modes emerge:
- Mode A — ImmediateOpen930: T3d, enter 09:30 open, stop MN, target 250. 341 trades, +28/tr, +12,694 pts total. Simple, higher frequency.
- Mode B — ConfirmAt1000: T3d + 10:00 entry after ≥60 pts ext, stop MN, target 250. 148 trades, +55/tr, +8,160 pts. Lower frequency, deeper stops, concentrated edge.
Part 7 — DOW breakdown
| DOW | Mode A n | Mode A exp/tr | Mode B n | Mode B exp/tr |
|---|---|---|---|---|
| Mon | 61 | +40.06 | 29 | +26.33 |
| Tue | 71 | +50.77 | 35 | +86.11 |
| Wed | 65 | +46.84 | 25 | +55.82 |
| Thu | 81 | +8.92 | 29 | +17.23 |
| Fri | 63 | +45.69 | 30 | +82.91 |
All five weekdays net positive in both modes. Thursday drags Mode A (can be filtered to lift Mode A to +46/tr × 260), Tuesday and Friday shine especially in Mode B.
Part 8 — the confluence discovery
While building the target & entry logic, one more question was worth asking: does the MN HOLD signal tell us anything about the other models that fire on the same day?
backtest_mn_hold_integration.py joined 341 MN HOLD sessions against ~34k historical PredictionModel predictions and classified each line as ALIGNED, OPPOSED, or NO-HOLD. The results were striking:
Top aligned boosts
| Model | Base hit% | Aligned% | Δ | Opposed% | Δ |
|---|---|---|---|---|---|
| PDH TOUCH | 47.8 | 77.2 | +29.4 | 21.6 | −26.2 |
| PDL TOUCH | 43.5 | 74.5 | +31.0 | 22.8 | −20.7 |
| PO XTND BEAR | 44.2 | 63.0 | +18.7 | 41.3 | −2.9 |
| ASIA [Med] LOW | 74.3 | 89.5 | +15.2 | 65.1 | −9.2 |
| 1H CRT | 81.7 | 94.5 | +12.8 | 64.6 | −17.0 |
| 0809 MID | 85.6 | 97.1 | +11.6 | 73.4 | −12.2 |
PDH/PDL TOUCH swings 60 percentage points between aligned and opposed. That’s one of the cleanest directional signals we’ve ever seen in a confluence layer.
PRIME tier spikes
| Model | Base | Aligned + PRIME | Lift |
|---|---|---|---|
| LON SWEEP L | 80.5 | 98.4 | +17.8 |
| PO RNG MID | 80.4 | 100.0 | +19.6 |
| LON SWEEP H | 84.6 | 98.5 | +13.9 |
| 0809 SEQ HIGH | 87.7 | 98.2 | +10.5 |
Heavy penalties on mean-reversion lines
| Model | Base | Opposed | Δ |
|---|---|---|---|
| INSIDE GAP FILL | 66.2 | 39.1 | −27.1 |
| MN OPEN FILL | 66.5 | 40.1 | −26.4 |
| GAP FILL → PDH | 65.7 | 51.6 | −14.0 |
| FILL 07-08 L | 80.8 | 66.2 | −14.6 |
The entire “fill / magnet” family — MN OPEN FILL, INSIDE GAP FILL, GAP FILL → PDH/PDL, FILL XX-XX, VWAP MAGNET — degrades dramatically on opposed hold days. These are mean-reversion models; hold days are trend days by definition. MN HOLD turned out to be the single best “mean-reversion OFF” filter we have.
Part 9 — meta-validation
backtest_mn_hold_meta.py applied the full bonus/penalty table above to the 34k historical prediction population and measured calibration drift:
| Bucket | Baseline hit% (n) | Adjusted hit% (n) |
|---|---|---|
| <45 | 45.4% (1,166) | 44.9% (780) |
| 45–54 | 48.5% (2,988) | 49.2% (2,947) |
| 55–64 | 66.9% (2,179) | 67.7% (2,248) |
| 65–74 | 70.7% (5,472) | 70.8% (6,083) |
| 75–84 | 79.5% (16,034) | 79.8% (14,548) |
| 85–94 | 89.0% (6,118) | 89.8% (5,994) |
| 95+ | 93.3% (119) | 97.5% (681) |
Every bucket stays calibrated or slightly improves. The adjusted 95+ bucket grew 5.7× and still hit at 97.5%. Overall adjusted hit rate 75.98% vs baseline 75.16% (+0.82 pp), with 795 bad lines correctly killed — 60–70% of those kills were real losers.
The ship list
All three production layers live:
smc_analysis.py(HUD producer) — emitsmn_hold_*keys on its schedule.PredictionModel.cs+PredictionModelWeb.cs— drawsMN HOLD BULL/BEAR(upgrades toMN HOLD PRIME) and applies the confluence bonus/penalty table to all other active lines after the AMD block.MnHoldSide.cs— NT8 strategy withModeparameter switching between ImmediateOpen930 and ConfirmAt1000.
Model page: MN HOLD-SIDE.
Scripts
C:\SMC\scripts\
backtest_midnight_open.py Part 1 — three-mode exploration
mn_hold_classifier.py Part 2 — T3 filter discovery
backtest_mn_hold_trades.py Part 3 — tier comparison + target sweep
mn_hold_show_examples.py Concrete wins/losses/scratches for inspection
backtest_mn_hold_entry_refine.py Parts 3-5 — distance cap, pullback, extension
backtest_mn_hold_entry_D.py Part 6 — stacked grid
backtest_mn_hold_dow.py Part 7 — DOW breakdown
backtest_mn_hold_integration.py Part 8 — confluence discovery
backtest_mn_hold_meta.py Part 9 — end-to-end validation
Result files live in C:\SMC\backtest_results\.