The NQ opening range breakout sized from $500 a trade, on MNQ or NQ
One script for either contract: it sizes each trade from a dollar risk, so the same plan reads $2 a point on MNQ and $20 on NQ. When one contract would risk more than the plan, it skips the day, or takes one contract anyway if you tell it to. It keeps a 50K challenge in its own books and counts the days with a setup, the days skipped, the average dollars at risk and every challenge passed or failed. The strategy is our NQ study's opening range breakout, whose stop is the 15-minute range.
- Trades
- 471
- Won
- 44.4%
- Profit factor
- 1.02
- Net
- +2.8%
- Worst drawdown
- −18.6%
- Worst trade
- −$531
MNQ1! · 5-minute · 23 Oct 2024 → 22 Oct 2026 · 2 years · 517 sessions · $0.75 a contract each side on MNQ, one tick of slippage, $50,000 account, $500 risk a trade, one trade a day, flat by 15:55 New York
On MNQ the plan held: 508 days had a setup, 471 were traded at 1.8 contracts on average with $382 at risk, and 37 were skipped because the range was so wide that even one micro risked more than $500. The 50K example challenges came to 22: 6 passed, 15 failed on the drawdown and one was still running. On NQ with the same inputs, one contract fitted $500 on a single day in two years.
- The size: the risk a trade in dollars, the most contracts, and what to do when one contract risks more than that.
- perContract() is what one contract loses at the stop with two ticks of slippage: the stop in points times syminfo.pointvalue, $2 on MNQ and $20 on NQ, so the same script sizes either one.
- The challenge is example numbers in the script's own books: a $3,000 target and a $2,000 drawdown trailing the day's close, stopping at the start, checked at each candle's worst point, and a new challenge the session after one ends.
- The strategy: the first close outside the 15-minute opening range, the stop at the range's other side, the target at 2R, one trade a day, flat by 15:55 New York.
- The tally in the Data Window and a table on the chart: days with a setup, days skipped, the average dollars at risk, challenges, passed and failed.
//@version=6
// Suggested timeframe: 5-minute. Run it on MNQ1! and on NQ1!: it sizes either one from the same dollar risk.
strategy("MNQ or NQ: one ORB, sized from the same risk", overlay=true, initial_capital=50000, default_qty_type=strategy.fixed, default_qty_value=1,
commission_type=strategy.commission.cash_per_contract, commission_value=0.75, slippage=1, margin_long=0, margin_short=0)
// commission_value is a side per contract: 0.75 is about a micro's all-in rate; on NQ we ran 2.5, put your own
// ── The size: contracts from dollars ─────────────────────────────────────────────────────────────
riskUsd = input.float(500, "Risk per trade ($)", minval=10, group="Size")
maxQty = input.int(10, "Most contracts", minval=1, group="Size")
tooBig = input.string("Skip the trade", "When one contract risks more than that", options=["Skip the trade", "Take one contract"], group="Size")
// what one contract loses at a stop this many points away, two ticks of slippage counted
perContract(stopPts) =>
(stopPts + 2 * syminfo.mintick) * syminfo.pointvalue
// the contracts the risk pays for: on MNQ a point is $2 a contract, on NQ $20
contractsFor(stopPts) =>
n = math.min(maxQty, math.floor(riskUsd / perContract(stopPts)))
tooBig == "Take one contract" ? math.max(n, 1) : n
// ── The challenge: example rules, not any firm's ─────────────────────────────────────────────────
target = input.float(3000, "Profit target ($)", group="Challenge")
trailDD = input.float(2000, "Trailing drawdown ($), end of day, stops at the start", group="Challenge")
var float base = strategy.initial_capital // the strategy's equity when this challenge began
var float peak = 0.0 // the closing balance the floor trails
var bool live = true
var int started = 1
var int passed = 0
var int failed = 0
if session.isfirstbar
if live
peak := math.max(peak, strategy.equity - base)
else
base := strategy.equity
peak := 0.0
live := true
started += 1
pnl = strategy.equity - base
pos = strategy.position_size
worst = pnl - (pos > 0 ? (close - low) * pos : pos < 0 ? (high - close) * -pos : 0.0) * syminfo.pointvalue
if live
if worst <= math.min(peak - trailDD, 0.0)
strategy.close_all(comment="Drawdown hit", immediately=true)
live := false
failed += 1
else if pnl >= target
strategy.close_all(comment="Target reached", immediately=true)
live := false
passed += 1
// ── The strategy: the opening range breakout from our NQ study, one trade a day ──────────────────
orSess = input.session("0930-0945", "Opening range (New York)", group="Strategy")
tradeSess = input.session("0945-1200", "Entry window (New York)", group="Strategy")
rMult = input.float(2.0, "Target (R)", minval=0.5, step=0.5, group="Strategy")
inOR = not na(time(timeframe.period, orSess, "America/New_York"))
inTrade = not na(time(timeframe.period, tradeSess, "America/New_York"))
cutoff = hour(time, "America/New_York") == 15 and minute(time, "America/New_York") >= 55
var float orHigh = na
var float orLow = na
var bool done = false // today's trade is taken, or today's setup did not fit
var float stopLvl = na
var float tgtLvl = na
var int setups = 0 // days with a setup
var int skipped = 0 // days whose setup one contract was too big for
var float riskSum = 0.0 // dollars at risk, over every trade taken
var int qtySum = 0
if session.isfirstbar
orHigh := na
orLow := na
done := false
if inOR
orHigh := na(orHigh) ? high : math.max(orHigh, high)
orLow := na(orLow) ? low : math.min(orLow, low)
rangeSet = not inOR and not na(orHigh)
longSetup = rangeSet and close > orHigh and close[1] <= orHigh and orLow < close
shortSetup = rangeSet and close < orLow and close[1] >= orLow and orHigh > close
if live and inTrade and not done and strategy.position_size == 0 and (longSetup or shortSetup)
stopPts = longSetup ? close - orLow : orHigh - close
qty = contractsFor(stopPts)
setups += 1
done := true
if qty < 1
skipped += 1
else
stopLvl := longSetup ? orLow : orHigh
tgtLvl := longSetup ? close + stopPts * rMult : close - stopPts * rMult
riskSum += qty * perContract(stopPts)
qtySum += qty
strategy.entry(longSetup ? "Long" : "Short", longSetup ? strategy.long : strategy.short, qty=qty)
// the stop and target go in with the entry, so the bar it fills on is covered too
strategy.exit(longSetup ? "Long exit" : "Short exit", from_entry=longSetup ? "Long" : "Short", stop=stopLvl, limit=tgtLvl)
if strategy.position_size > 0
strategy.exit("Long exit", from_entry="Long", stop=stopLvl, limit=tgtLvl)
if strategy.position_size < 0
strategy.exit("Short exit", from_entry="Short", stop=stopLvl, limit=tgtLvl)
if (cutoff or session.islastbar) and strategy.position_size != 0
strategy.close_all(comment="Cut-off", immediately=true)
plot(rangeSet ? orHigh : na, "Range high", color=color.new(#16A56A, 0), style=plot.style_linebr)
plot(rangeSet ? orLow : na, "Range low", color=color.new(#E5484D, 0), style=plot.style_linebr)
// the tally, in the Data Window and a table on the last bar
taken = setups - skipped
plot(setups, "Days with a setup", display=display.data_window)
plot(skipped, "Skipped: one contract risked more", display=display.data_window)
plot(taken > 0 ? riskSum / taken : na, "Average risk a trade ($)", display=display.data_window)
plot(taken > 0 ? float(qtySum) / taken : na, "Average contracts", display=display.data_window)
plot(started, "Challenges started", display=display.data_window)
plot(passed, "Passed", display=display.data_window)
plot(failed, "Failed on the drawdown", display=display.data_window)
var table book = table.new(position.top_right, 2, 5, bgcolor=color.new(color.black, 15), border_width=1)
if barstate.islast
table.cell(book, 0, 0, "Days with a setup", text_color=color.white)
table.cell(book, 1, 0, str.tostring(setups), text_color=color.white)
table.cell(book, 0, 1, "Skipped: one contract risked more", text_color=color.white)
table.cell(book, 1, 1, str.tostring(skipped), text_color=color.white)
table.cell(book, 0, 2, "Average risk a trade", text_color=color.white)
table.cell(book, 1, 2, taken > 0 ? "$" + str.tostring(math.round(riskSum / taken)) : "—", text_color=color.white)
table.cell(book, 0, 3, "Challenges", text_color=color.white)
table.cell(book, 1, 3, str.tostring(started), text_color=color.white)
table.cell(book, 0, 4, "Passed / failed", text_color=color.white)
table.cell(book, 1, 4, str.tostring(passed) + " / " + str.tostring(failed), text_color=color.white)

