Skip to content

Instantly share code, notes, and snippets.

@deltatrend
Last active April 1, 2026 18:41
Show Gist options
  • Select an option

  • Save deltatrend/ec550d13ea6246f25e41023e77426247 to your computer and use it in GitHub Desktop.

Select an option

Save deltatrend/ec550d13ea6246f25e41023e77426247 to your computer and use it in GitHub Desktop.
RP Profits' 8AM ORB strategy, implemented in PineScript
//@version=6
// © QuantPad LLC [made with https://quantpad.ai/]
strategy("'RP Profits' 8AM ORB",
overlay = true,
dynamic_requests = true,
initial_capital = 50000,
default_qty_type = strategy.fixed,
default_qty_value = 2,
commission_type = strategy.commission.cash_per_contract,
commission_value = 1.40,
margin_long = 0,
margin_short = 0,
use_bar_magnifier = true)
// ─────────────────────────────────────────────────────────────────────────────
// INPUTS
// ─────────────────────────────────────────────────────────────────────────────
group_vis = "Visuals"
show_asia = input.bool(true, "Show Asia Session", group=group_vis, tooltip="Show/hide Asia session high and low levels.")
show_london = input.bool(true, "Show London Session", group=group_vis, tooltip="Show/hide London session high and low levels.")
show_bg = input.bool(true, "Show Background Color", group=group_vis, tooltip="Tint the background green (bullish) or red (bearish) when a trade direction has been determined.")
show_bos_lines = input.bool(true, "Show BOS Lines", group=group_vis, tooltip="When enabled, draws the structural level broken by any 1-minute BOS that triggered a trade entry. Only trade-triggering BOS events are shown.")
group_entry = "Entry Mode"
entry_mode = input.string("Limit Order", "Entry Mode", group=group_entry,
options=["Limit Order", "BOS Confirmation"],
tooltip="Limit Order: place a limit at the 8AM range midpoint immediately after direction is established (original behavior).\n\nBOS Confirmation: wait for price to tap the midpoint in the direction of the trade (wick touch is sufficient), then enter on the close of the first confirmed 1-minute Break of Structure in that direction.\n\nIf price advances fully through the opposing zone boundary (past zone high on a bearish day, past zone low on a bullish day), the tap setup is permanently invalidated for the rest of the day.\n\nOnly one trade per day is permitted in either mode; the day resets at 18:00.")
group_sessions = "Session Times"
asia_time = input.session("1800-0300", "Asia Session", group=group_sessions, inline="asia")
london_time = input.session("0330-1130", "London Session", group=group_sessions, inline="london")
group_filter = "Trade Window Filter"
use_trade_window = input.bool(true, "Restrict Trade Window", group=group_filter, inline="trade_window", tooltip="When enabled, trade direction is only active inside the specified time window. The limit order is cancelled if the window closes before entry.")
trade_window_time = input.session("0900-1100", "", group=group_filter, inline="trade_window")
group_bias = "Daily Bias Filter"
require_clean_bias = input.bool(false, "Require Clean Daily Bias", group=group_bias,
tooltip="When enabled, a daily bias is only allowed if price has stayed on ONE side of the 8:00–8:15 range between 8:15 and the moment bias would be set.\n\nBullish bias requires price did NOT trade below the 8:00–8:15 low.\nBearish bias requires price did NOT trade above the 8:00–8:15 high.\n\nIf violated, bias is rejected for the entire day and no trades are taken. Bias checks stop after a trade is entered.")
invalidate_large_range = input.bool(true, "Invalidate Large 8:00 Range (> 20pts)", group=group_bias,
tooltip="When enabled, days where the 8:00–8:15 range width exceeds 20 points are trade-disabled for the day.\nThe range box and bias visuals still plot, but no trades will be taken, and the range box will display 'Range Too Large'.")
group_risk = "Risk Parameters"
sl_scan_low = input.float(5.0, "SL Scan Range Low", group=group_risk, minval=0.0, inline="sl_scan", tooltip="Minimum distance (pts) from the entry price a session level must be to qualify as a stop-loss anchor. In Limit Order mode the entry price is the zone midpoint; in BOS Confirmation mode it is the BOS bar close.")
sl_scan_high = input.float(10.0, "— High (pts)", group=group_risk, minval=0.0, inline="sl_scan", tooltip="Maximum distance (pts) from the entry price a session level must be to qualify as a stop-loss anchor.")
sl_default = input.float(8.0, "Default SL (pts)", group=group_risk, minval=0.1, tooltip="Fallback stop-loss distance in points when no qualifying session level is found within the scan range.")
rr_multiple = input.float(4.0, "RR Multiple", group=group_risk, minval=0.1, tooltip="Risk-reward multiple applied to the stop-loss distance to set the take-profit target (e.g. 4 = 4× the SL distance).")
sl_manage_mode = input.string("Don't Move", "SL Management", group=group_risk,
options=["Don't Move", "Move to BE", "Trail"],
tooltip="Don't Move: stoploss stays fixed at its initial level.\nMove to BE: stoploss moves to entry price once price has moved sufficiently into profit (entry 1m bar ignored).\nTrail: stoploss trails price by the same distance as the initial stop, updating every 1m bar (entry 1m bar is ignored). Uses strategy.exit trailing parameters.")
// ─────────────────────────────────────────────────────────────────────────────
// TYPES
// ─────────────────────────────────────────────────────────────────────────────
type Session
string name
string time_window
color line_color
float high = na
float low = na
int high_time = na
int low_time = na
int session_start_time = na
bool high_valid = true
bool low_valid = true
int high_invalid_time = na
int low_invalid_time = na
line high_line = na
line low_line = na
label high_label = na
label low_label = na
type DailyState
float zone_high = na
float zone_low = na
float zone_mid = na
int zone_start_time = na
box zone_box = na
line zone_mid_line = na
string trade_dir = na
int dir_determined_time = na
// Large range invalidation
bool range_too_large = false
bool trade_banned = false
// Clean-bias tracking (8:15 → bias set)
bool bias_locked_out = false
bool touched_below_since_815 = false
bool touched_above_since_815 = false
// trade_taken: set true the moment ANY trade is entered regardless of mode.
// Prevents a second entry within the same day. Resets at 18:00.
bool trade_taken = false
// Trade state
bool limit_pending = false
float limit_price = na
bool trade_active = false
bool pos_opened = false
int entry_time = na
int entry_ib_time = na
float entry_price = na
float sl_price = na
float tp_price = na
int exit_time = na
box sl_box = na
box tp_box = na
string sl_label_text = na
label sl_label = na
string tp_label_text = na
label tp_label = na
// Freeze direction at fill so management/visuals don't depend on trade_dir persistence
string entry_dir = na
// Store initial risk geometry (so BE logic is stable even after SL moves)
float init_sl_price = na
float init_tp_price = na
float init_sl_dist = na
float init_tp_dist = na
// SL management state
string sl_mode = na
bool be_triggered = false
int be_time = na
label be_label = na
// Trail
float trail_dist = na
int trail_offset_ticks = na
float trail_best = na
float trail_sl_price = na
label trail_label = na
// ── BOS Confirmation entry mode ───────────────────────────────────────────
// tap_state: 0 = unarmed, +1 = bullish tap armed, -1 = bearish tap armed
// tap_invalidated: permanently true for the rest of the day once price advances
// fully through the opposing zone boundary (past zone_high on a
// bearish day; past zone_low on a bullish day). Blocks any
// further tap arming even after a reset. Cleared at 18:00.
int tap_state = 0
int tap_time = na
bool awaiting_bos = false
bool tap_invalidated = false
// ─────────────────────────────────────────────────────────────────────────────
// 1-MINUTE BREAK OF STRUCTURE DETECTOR
// Ported verbatim from TJR BOS module. Do NOT modify BOS_core() logic.
// Called via request.security on the "1" timeframe; fires on confirmed 1m bars.
// ─────────────────────────────────────────────────────────────────────────────
BOS_core() =>
var int[] BOS_bosTimesBull = array.new_int()
var int[] BOS_bosTimesBear = array.new_int()
bool BOS_isBosBull = false
bool BOS_isBosBear = false
int BOS_left_ts = na
float BOS_level = na
int BOS_dir = 0
if close > open and barstate.isconfirmed
BOS_isBosBull := true
int i = 1
while BOS_isBosBull and i <= bar_index and i <= 1000 and close[i] >= open[i]
if high[i] > close or array.includes(BOS_bosTimesBull, time[i])
BOS_isBosBull := false
i += 1
float BOS_high_to_beat = na
int BOS_high_time = na
while BOS_isBosBull and i <= bar_index and i <= 1000 and close[i] <= open[i]
if na(BOS_high_to_beat) or high[i] > BOS_high_to_beat
BOS_high_to_beat := high[i]
BOS_high_time := time[i]
i += 1
if BOS_isBosBull and i <= bar_index and i <= 1000 and high[i] > BOS_high_to_beat
BOS_high_to_beat := high[i]
BOS_high_time := time[i]
if BOS_isBosBull and close <= nz(BOS_high_to_beat, close)
BOS_isBosBull := false
if BOS_isBosBull
BOS_level := BOS_high_to_beat
BOS_left_ts := BOS_high_time
BOS_dir := 1
array.push(BOS_bosTimesBull, time)
if array.size(BOS_bosTimesBull) > 15
if array.size(BOS_bosTimesBull) > 0
array.shift(BOS_bosTimesBull)
if close < open and barstate.isconfirmed and BOS_dir == 0
BOS_isBosBear := true
int i2 = 1
while BOS_isBosBear and i2 <= bar_index and i2 <= 1000 and close[i2] <= open[i2]
if low[i2] < close or array.includes(BOS_bosTimesBear, time[i2])
BOS_isBosBear := false
i2 += 1
float BOS_low_to_beat = na
int BOS_low_time = na
while BOS_isBosBear and i2 <= bar_index and i2 <= 1000 and close[i2] >= open[i2]
if na(BOS_low_to_beat) or low[i2] < BOS_low_to_beat
BOS_low_to_beat := low[i2]
BOS_low_time := time[i2]
i2 += 1
if BOS_isBosBear and i2 <= bar_index and i2 <= 1000 and low[i2] < BOS_low_to_beat
BOS_low_to_beat := low[i2]
BOS_low_time := time[i2]
if BOS_isBosBear and close >= nz(BOS_low_to_beat, close)
BOS_isBosBear := false
if BOS_isBosBear
BOS_level := BOS_low_to_beat
BOS_left_ts := BOS_low_time
BOS_dir := -1
array.push(BOS_bosTimesBear, time)
if array.size(BOS_bosTimesBear) > 15
if array.size(BOS_bosTimesBear) > 0
array.shift(BOS_bosTimesBear)
bool BOS_is_new = BOS_dir != 0
[BOS_is_new, BOS_left_ts, time, BOS_level, BOS_dir]
// ─────────────────────────────────────────────────────────────────────────────
// INTRABAR 1-MINUTE DATA
// ─────────────────────────────────────────────────────────────────────────────
ib_times = request.security_lower_tf(syminfo.tickerid, "1", time)
ib_opens = request.security_lower_tf(syminfo.tickerid, "1", open)
ib_closes = request.security_lower_tf(syminfo.tickerid, "1", close)
ib_highs = request.security_lower_tf(syminfo.tickerid, "1", high)
ib_lows = request.security_lower_tf(syminfo.tickerid, "1", low)
ib_in_asia = request.security_lower_tf(syminfo.tickerid, "1", not na(time(timeframe.period, asia_time, "America/New_York")))
ib_in_london = request.security_lower_tf(syminfo.tickerid, "1", not na(time(timeframe.period, london_time, "America/New_York")))
ib_in_trade_window = request.security_lower_tf(syminfo.tickerid, "1", not na(time(timeframe.period, trade_window_time, "America/New_York")))
// ─────────────────────────────────────────────────────────────────────────────
// 15-MINUTE DATA FOR KEY ZONE
// ─────────────────────────────────────────────────────────────────────────────
[m15_high_val, m15_low_val, m15_time_val] = request.security(syminfo.tickerid, "15", [high, low, time], barmerge.gaps_off, barmerge.lookahead_off)
// 1-minute ATR — one value per chart bar; used for direction thresholds.
m1_atr = request.security(syminfo.tickerid, "1", ta.atr(14), barmerge.gaps_off, barmerge.lookahead_off)
// ─────────────────────────────────────────────────────────────────────────────
// 1-MINUTE BOS SIGNALS (chart-bar scope, confirmed on 1m bar close)
// bos_fire_event is true only on the FIRST chart bar of each new 1m bar that
// produced a BOS, matching the gating pattern from the TJR module.
// ─────────────────────────────────────────────────────────────────────────────
bos_htf_time = request.security(syminfo.tickerid, "1", time, barmerge.gaps_off, barmerge.lookahead_off)
[bos_ev_new, bos_ev_left_ts, bos_ev_right_ts, bos_ev_level, bos_ev_dir] = request.security(syminfo.tickerid, "1", BOS_core(), barmerge.gaps_off, barmerge.lookahead_off)
bos_tf_newbar = ta.change(bos_htf_time) != 0
bos_fire_event = bos_ev_new and bos_tf_newbar
// ─────────────────────────────────────────────────────────────────────────────
// SESSION INSTANCES & ARRAYS
// ─────────────────────────────────────────────────────────────────────────────
var Session asia = Session.new(name="Asia", time_window=asia_time, line_color=color.yellow)
var Session london = Session.new(name="London", time_window=london_time, line_color=color.new(color.teal, 0))
var array<Session> asia_sessions = array.new<Session>()
var array<Session> london_sessions = array.new<Session>()
const int MAX_SESSIONS = 25
var DailyState state = DailyState.new()
var bool prev_ib_in_asia = false
var bool prev_ib_in_london = false
var bool do_close_all = false
// BOS line arrays (for visualization)
var line[] bos_bull_lines = array.new_line()
var line[] bos_bear_lines = array.new_line()
const int BOS_MAX_LINES = 15
// ─────────────────────────────────────────────────────────────────────────────
// HELPER FUNCTIONS
// ─────────────────────────────────────────────────────────────────────────────
cleanup_session(Session sess) =>
if not na(sess.high_line)
line.delete(sess.high_line)
if not na(sess.low_line)
line.delete(sess.low_line)
if not na(sess.high_label)
label.delete(sess.high_label)
if not na(sess.low_label)
label.delete(sess.low_label)
copy_session(Session src) =>
Session.new(
name = src.name,
time_window = src.time_window,
line_color = src.line_color,
high = src.high,
low = src.low,
high_time = src.high_time,
low_time = src.low_time,
session_start_time = src.session_start_time,
high_valid = src.high_valid,
low_valid = src.low_valid,
high_invalid_time = src.high_invalid_time,
low_invalid_time = src.low_invalid_time,
high_line = src.high_line,
low_line = src.low_line,
high_label = src.high_label,
low_label = src.low_label)
invalidate_level(Session sess, bool is_high, int inv_time) =>
if is_high
sess.high_valid := false
sess.high_invalid_time := inv_time
if not na(sess.high_line)
line.set_x2(sess.high_line, inv_time)
if na(sess.high_label)
sess.high_label := label.new(x=inv_time, y=sess.high, text="", xloc=xloc.bar_time, style=label.style_label_left, color=color.new(sess.line_color, 70), textcolor=color.black, size=size.tiny, text_font_family=font.family_monospace)
else
sess.low_valid := false
sess.low_invalid_time := inv_time
if not na(sess.low_line)
line.set_x2(sess.low_line, inv_time)
if na(sess.low_label)
sess.low_label := label.new(x=inv_time, y=sess.low, text="", xloc=xloc.bar_time, style=label.style_label_left, color=color.new(sess.line_color, 70), textcolor=color.black, size=size.tiny, text_font_family=font.family_monospace)
process_session_start(Session sess, array<Session> sess_array, int ib_time, int chart_time, float ib_high, float ib_low) =>
if not na(sess.session_start_time)
sess_array.push(copy_session(sess))
if array.size(sess_array) > MAX_SESSIONS and array.size(sess_array) > 0
cleanup_session(sess_array.shift())
sess.high := ib_high
sess.low := ib_low
sess.high_time := chart_time
sess.low_time := chart_time
sess.session_start_time := ib_time
sess.high_valid := true
sess.low_valid := true
sess.high_invalid_time := na
sess.low_invalid_time := na
sess.high_line := na
sess.low_line := na
sess.high_label := na
sess.low_label := na
process_session_update(Session sess, float ib_high, float ib_low, int ib_time, int chart_time) =>
if na(sess.high) or ib_high > sess.high
sess.high := ib_high
sess.high_time := chart_time
if na(sess.low) or ib_low < sess.low
sess.low := ib_low
sess.low_time := chart_time
process_session_invalidation(Session sess, float ib_close, int ib_time) =>
if not na(sess.high) and sess.high_valid and ib_close > sess.high
invalidate_level(sess, true, ib_time)
if not na(sess.low) and sess.low_valid and ib_close < sess.low
invalidate_level(sess, false, ib_time)
process_archived_invalidation(array<Session> sess_array, float ib_close, int ib_time) =>
if array.size(sess_array) > 0
for i = 0 to array.size(sess_array) - 1
s = sess_array.get(i)
if not na(s.high) and s.high_valid and ib_close > s.high
invalidate_level(s, true, ib_time)
if not na(s.low) and s.low_valid and ib_close < s.low
invalidate_level(s, false, ib_time)
process_session_end(array<Session> sess_array, int ib_time) =>
if array.size(sess_array) > 0
prev_sess = sess_array.last()
if not na(prev_sess.high) and prev_sess.high_valid
invalidate_level(prev_sess, true, ib_time)
if not na(prev_sess.low) and prev_sess.low_valid
invalidate_level(prev_sess, false, ib_time)
process_session_drawings(Session sess) =>
if not na(sess.high) and not na(sess.high_time) and not na(sess.session_start_time)
high_x2 = sess.high_valid ? time : sess.high_invalid_time
if na(sess.high_line)
sess.high_line := line.new(x1=sess.high_time, y1=sess.high, x2=high_x2, y2=sess.high, xloc=xloc.bar_time, color=color.new(sess.line_color, 30), width=1, style=line.style_solid)
if not na(sess.high_line)
line.set_x1(sess.high_line, sess.high_time)
line.set_x2(sess.high_line, high_x2)
line.set_y1(sess.high_line, sess.high)
line.set_y2(sess.high_line, sess.high)
if not na(sess.low) and not na(sess.low_time) and not na(sess.session_start_time)
low_x2 = sess.low_valid ? time : sess.low_invalid_time
if na(sess.low_line)
sess.low_line := line.new(x1=sess.low_time, y1=sess.low, x2=low_x2, y2=sess.low, xloc=xloc.bar_time, color=color.new(sess.line_color, 30), width=1, style=line.style_solid)
if not na(sess.low_line)
line.set_x1(sess.low_line, sess.low_time)
line.set_x2(sess.low_line, low_x2)
line.set_y1(sess.low_line, sess.low)
line.set_y2(sess.low_line, sess.low)
process_archived_drawings(array<Session> sess_array) =>
if array.size(sess_array) > 0
for i = 0 to array.size(sess_array) - 1
s = sess_array.get(i)
if not na(s.high_line) and s.high_valid
line.set_x2(s.high_line, time)
if not na(s.low_line) and s.low_valid
line.set_x2(s.low_line, time)
// ─────────────────────────────────────────────────────────────────────────────
// SL / TP COMPUTATION
// anchor: the price level from which SL scan distance is measured and from
// which TP is projected. In Limit Order mode this is zone_mid;
// in BOS Confirmation mode this is the actual BOS bar close price.
// ─────────────────────────────────────────────────────────────────────────────
check_sl_candidate(float lvl, bool valid, string lbl, bool is_bull, float anchor,
float cur_price, float cur_dist, string cur_text) =>
new_price = cur_price
new_dist = cur_dist
new_text = cur_text
if valid and not na(lvl)
d = is_bull ? anchor - lvl : lvl - anchor
if d >= sl_scan_low and d <= sl_scan_high and d > cur_dist
new_dist := d
new_price := lvl
new_text := str.tostring(math.round(d, 1)) + "pts | " + lbl
[new_price, new_dist, new_text]
compute_sl_tp(float anchor, string dir) =>
is_bull = dir == "bullish"
[p1, d1, t1] = check_sl_candidate(asia.high, asia.high_valid, "asia high", is_bull, anchor, float(na), 0.0, "")
[p2, d2, t2] = check_sl_candidate(asia.low, asia.low_valid, "asia low", is_bull, anchor, float(na), 0.0, "")
[p3, d3, t3] = check_sl_candidate(london.high, london.high_valid, "london high", is_bull, anchor, float(na), 0.0, "")
[p4, d4, t4] = check_sl_candidate(london.low, london.low_valid, "london low", is_bull, anchor, float(na), 0.0, "")
bp = float(na)
bd = 0.0
bt = ""
if d1 > bd
bp := p1
bd := d1
bt := t1
if d2 > bd
bp := p2
bd := d2
bt := t2
if d3 > bd
bp := p3
bd := d3
bt := t3
if d4 > bd
bp := p4
bd := d4
bt := t4
if not na(bp)
tp_dist = rr_multiple * bd
tp = is_bull ? anchor + tp_dist : anchor - tp_dist
tp_txt = str.tostring(math.round(tp_dist, 1)) + "pts"
[bp, tp, bt, tp_txt]
else
fallback = sl_default
sl_fb = is_bull ? anchor - fallback : anchor + fallback
tp_fb = is_bull ? anchor + rr_multiple * fallback : anchor - rr_multiple * fallback
sl_txt = str.tostring(fallback) + "pts"
tp_txt = str.tostring(rr_multiple * fallback) + "pts"
[sl_fb, tp_fb, sl_txt, tp_txt]
// ─────────────────────────────────────────────────────────────────────────────
// MAIN INTRABAR PROCESSING LOOP
// ─────────────────────────────────────────────────────────────────────────────
n_ib = array.size(ib_times)
mintick = syminfo.mintick
if n_ib > 0
for i = 0 to n_ib - 1
t = array.get(ib_times, i)
o = array.get(ib_opens, i)
c = array.get(ib_closes, i)
h = array.get(ib_highs, i)
l = array.get(ib_lows, i)
in_a = array.get(ib_in_asia, i)
in_l = array.get(ib_in_london, i)
in_window = array.get(ib_in_trade_window, i)
prev_a = i == 0 ? prev_ib_in_asia : array.get(ib_in_asia, i - 1)
prev_l = i == 0 ? prev_ib_in_london : array.get(ib_in_london, i - 1)
ib_hour = hour(t, "America/New_York")
ib_minute = minute(t, "America/New_York")
in_ny_window = (ib_hour < 16) or (ib_hour == 16 and ib_minute < 15)
after_815 = (ib_hour > 8) or (ib_hour == 8 and ib_minute >= 15)
after_930 = (ib_hour > 9) or (ib_hour == 9 and ib_minute >= 30)
// ── Daily state reset at Asia session open (18:00) ────────────────────
if ib_hour == 18 and ib_minute == 0
state := DailyState.new()
// ── Asia session ──────────────────────────────────────────────────────
if show_asia
if in_a and not prev_a
process_session_start(asia, asia_sessions, t, time, h, l)
else if in_a
process_session_update(asia, h, l, t, time)
process_session_invalidation(asia, c, t)
process_archived_invalidation(asia_sessions, c, t)
if not in_a and prev_a
process_session_end(asia_sessions, t)
// ── London session ────────────────────────────────────────────────────
if show_london
if in_l and not prev_l
process_session_start(london, london_sessions, t, time, h, l)
else if in_l
process_session_update(london, h, l, t, time)
process_session_invalidation(london, c, t)
process_archived_invalidation(london_sessions, c, t)
if not in_l and prev_l
process_session_end(london_sessions, t)
// ── Clean Bias tracking (ONLY when enabled; ONLY during NY; ONLY until a trade is entered) ──
if require_clean_bias and not state.bias_locked_out and not state.trade_taken and in_ny_window and after_815 and not na(state.zone_high)
if l < state.zone_low
state.touched_below_since_815 := true
if h > state.zone_high
state.touched_above_since_815 := true
// ── Trade direction determination ─────────────────────────────────────
if in_ny_window and ib_hour == 9 and ib_minute == 30 and na(state.trade_dir) and not na(state.zone_high) and not state.bias_locked_out
bull_candidate = c > state.zone_high
bear_candidate = c < state.zone_low
if bull_candidate
if require_clean_bias and not state.trade_taken and state.touched_below_since_815
state.bias_locked_out := true
is_bear = c < o
y = is_bear ? h : l
st = is_bear ? label.style_label_down : label.style_label_up
label.new(x=t, y=y, text="Bias Rejected", xloc=xloc.bar_time, style=st,
color=color.new(color.gray, 75), textcolor=color.black, size=size.small, text_font_family=font.family_monospace)
else
state.trade_dir := "bullish"
state.dir_determined_time := t
else if bear_candidate
if require_clean_bias and not state.trade_taken and state.touched_above_since_815
state.bias_locked_out := true
is_bear = c < o
y = is_bear ? h : l
st = is_bear ? label.style_label_down : label.style_label_up
label.new(x=t, y=y, text="Bias Rejected", xloc=xloc.bar_time, style=st,
color=color.new(color.gray, 75), textcolor=color.black, size=size.small, text_font_family=font.family_monospace)
else
state.trade_dir := "bearish"
state.dir_determined_time := t
// ATR-based fallback for bars after 9:30 and before NY close (16:15).
if in_ny_window and after_930 and na(state.trade_dir) and not na(state.zone_high) and not state.bias_locked_out
upper_threshold = state.zone_high + m1_atr
lower_threshold = state.zone_low - m1_atr
bull_candidate = c > upper_threshold
bear_candidate = c < lower_threshold
if bull_candidate
if require_clean_bias and not state.trade_taken and state.touched_below_since_815
state.bias_locked_out := true
is_bear = c < o
y = is_bear ? h : l
st = is_bear ? label.style_label_down : label.style_label_up
label.new(x=t, y=y, text="Bias Rejected", xloc=xloc.bar_time, style=st,
color=color.new(color.gray, 75), textcolor=color.black, size=size.small, text_font_family=font.family_monospace)
else
state.trade_dir := "bullish"
state.dir_determined_time := t
else if bear_candidate
if require_clean_bias and not state.trade_taken and state.touched_above_since_815
state.bias_locked_out := true
is_bear = c < o
y = is_bear ? h : l
st = is_bear ? label.style_label_down : label.style_label_up
label.new(x=t, y=y, text="Bias Rejected", xloc=xloc.bar_time, style=st,
color=color.new(color.gray, 75), textcolor=color.black, size=size.small, text_font_family=font.family_monospace)
else
state.trade_dir := "bearish"
state.dir_determined_time := t
// Clear trade direction at NY futures session close (16:15) and flag close_all.
if ib_hour == 16 and ib_minute == 15
state.trade_dir := na
do_close_all := true
// Trade window filter — IMPORTANT: don't clear direction mid-trade.
if use_trade_window and not in_window
if not state.trade_active
state.trade_dir := na
// BOS mode: expire any pending tap when the window closes
if entry_mode == "BOS Confirmation"
state.tap_state := 0
state.awaiting_bos := false
state.tap_time := na
state.limit_pending := false
// ── LIMIT ORDER MODE: order placement ────────────────────────────────
if entry_mode == "Limit Order"
if not state.trade_banned and not na(state.trade_dir) and not state.limit_pending and not state.trade_active and na(state.limit_price)
state.limit_pending := true
state.limit_price := state.zone_mid
// ── LIMIT ORDER MODE: manual fill detection ───────────────────────────
if entry_mode == "Limit Order"
if state.limit_pending and not state.trade_active
filled = state.trade_dir == "bullish" ? l <= state.limit_price : h >= state.limit_price
if filled
state.limit_pending := false
state.trade_active := true
state.pos_opened := false
state.trade_taken := true // unconditional — one trade per day
state.entry_price := state.limit_price
state.entry_time := time
state.entry_ib_time := t
state.entry_dir := state.trade_dir
// Limit Order mode: anchor is zone_mid (entry price == zone_mid)
[computed_sl, computed_tp, computed_sl_lbl, computed_tp_lbl] = compute_sl_tp(state.zone_mid, state.trade_dir)
state.sl_price := computed_sl
state.tp_price := computed_tp
state.sl_label_text := computed_sl_lbl
state.tp_label_text := computed_tp_lbl
state.init_sl_price := computed_sl
state.init_tp_price := computed_tp
state.init_sl_dist := math.abs(state.entry_price - computed_sl)
state.init_tp_dist := math.abs(computed_tp - state.entry_price)
state.sl_mode := sl_manage_mode
state.be_triggered := false
state.be_time := na
state.exit_time := na
if sl_manage_mode == "Trail"
state.trail_dist := math.abs(computed_sl - state.limit_price)
raw_ticks = int(math.round(state.trail_dist / mintick))
state.trail_offset_ticks := math.max(raw_ticks, 1)
state.trail_best := state.entry_price
offset_px = state.trail_offset_ticks * mintick
state.trail_sl_price := state.entry_dir == "bullish" ? (state.entry_price - offset_px) : (state.entry_price + offset_px)
state.sl_price := state.trail_sl_price
// ── BOS CONFIRMATION MODE: tap detection ─────────────────────────────
// Runs only when no trade has been taken today, no active trade, direction
// is known, the zone is ready, and the instrument is not banned.
if entry_mode == "BOS Confirmation" and not state.trade_taken and not state.trade_banned and not state.trade_active and not na(state.trade_dir) and not na(state.zone_high)
in_window_for_tap = not use_trade_window or in_window
if in_window_for_tap
is_bull_day = state.trade_dir == "bullish"
is_bear_day = state.trade_dir == "bearish"
// ── Permanent range-through invalidation ──────────────────────
// If price advances fully through the opposing zone boundary, the
// tap setup is dead for the rest of the day — regardless of whether
// a tap was already armed or not. This prevents re-arming after
// the invalidation event (the prior bug: reset to 0 → retrace back
// through mid → re-armed → trade taken after range blow-through).
//
// Bearish day: price trades above zone_high → invalidated
// Bullish day: price trades below zone_low → invalidated
if not state.tap_invalidated
if is_bear_day and h > state.zone_high
state.tap_invalidated := true
state.tap_state := 0
state.awaiting_bos := false
state.tap_time := na
else if is_bull_day and l < state.zone_low
state.tap_invalidated := true
state.tap_state := 0
state.awaiting_bos := false
state.tap_time := na
// ── Tap arming — only if not permanently invalidated ──────────
if not state.tap_invalidated and state.tap_state == 0
// Bullish day: price dips to or below the midpoint → buy setup armed
if is_bull_day and l <= state.zone_mid
state.tap_state := 1
state.awaiting_bos := true
state.tap_time := t
// Bearish day: price rallies to or above the midpoint → sell setup armed
else if is_bear_day and h >= state.zone_mid
state.tap_state := -1
state.awaiting_bos := true
state.tap_time := t
// ── SL management updates (VISUAL ONLY; real orders handled by strategy.exit) ──
if state.trade_active and not na(state.entry_dir)
after_entry_1m = not na(state.entry_ib_time) ? (t > state.entry_ib_time) : true
is_long_mgmt = state.entry_dir == "bullish"
if state.sl_mode == "Move to BE" and not state.be_triggered and after_entry_1m
sl_d = state.init_sl_dist
tp_d = state.init_tp_dist
valid_geom = not na(sl_d) and not na(tp_d) and tp_d > 0 and sl_d > 0
if valid_geom
be_req = sl_d < tp_d ? sl_d : (0.5 * tp_d)
be_hit = is_long_mgmt ? (h >= state.entry_price + be_req) : (l <= state.entry_price - be_req)
if be_hit
state.be_triggered := true
state.be_time := time
state.sl_price := state.entry_price
else if state.sl_mode == "Trail" and not na(state.trail_offset_ticks) and not na(state.trail_best)
if after_entry_1m
if is_long_mgmt
state.trail_best := math.max(state.trail_best, h)
state.trail_sl_price := state.trail_best - state.trail_offset_ticks * mintick
else
state.trail_best := math.min(state.trail_best, l)
state.trail_sl_price := state.trail_best + state.trail_offset_ticks * mintick
state.sl_price := state.trail_sl_price
prev_ib_in_asia := array.get(ib_in_asia, n_ib - 1)
prev_ib_in_london := array.get(ib_in_london, n_ib - 1)
// ─────────────────────────────────────────────────────────────────────────────
// KEY ZONE (8:00-8:15 AM 15M CANDLE)
// ─────────────────────────────────────────────────────────────────────────────
m15_hour = hour(m15_time_val, "America/New_York")
m15_minute = minute(m15_time_val, "America/New_York")
if m15_hour == 8 and m15_minute == 0 and na(state.zone_high)
state.zone_high := m15_high_val
state.zone_low := m15_low_val
state.zone_mid := (m15_high_val + m15_low_val) / 2.0
state.zone_start_time := m15_time_val
range_w = state.zone_high - state.zone_low
if invalidate_large_range and range_w > 20.0
state.range_too_large := true
state.trade_banned := true
// ─────────────────────────────────────────────────────────────────────────────
// BOS CONFIRMATION MODE: Entry state initialization (chart-bar scope)
//
// Risk geometry anchored to BOS bar close (actual fill price).
// tap_invalidated is checked here as a belt-and-suspenders guard; it will
// already be true in the intrabar loop if the range was blown through.
// ─────────────────────────────────────────────────────────────────────────────
if entry_mode == "BOS Confirmation" and bos_fire_event and state.awaiting_bos and not state.trade_active and not state.trade_taken and not state.trade_banned and not state.tap_invalidated and not na(state.zone_mid)
direction_match = (state.tap_state == 1 and bos_ev_dir == 1) or (state.tap_state == -1 and bos_ev_dir == -1)
in_trade_window_now = not use_trade_window or not na(time(timeframe.period, trade_window_time, "America/New_York"))
if direction_match and in_trade_window_now
bos_entry_dir = state.tap_state == 1 ? "bullish" : "bearish"
bos_fill_price = close
// SL/TP anchored to actual fill price, not zone_mid
[computed_sl, computed_tp, computed_sl_lbl, computed_tp_lbl] = compute_sl_tp(bos_fill_price, bos_entry_dir)
state.trade_active := true
state.pos_opened := false
state.trade_taken := true // unconditional — one trade per day
state.entry_price := bos_fill_price
state.entry_time := time
state.entry_ib_time := time
state.entry_dir := bos_entry_dir
state.sl_price := computed_sl
state.tp_price := computed_tp
state.sl_label_text := computed_sl_lbl
state.tp_label_text := computed_tp_lbl
state.init_sl_price := computed_sl
state.init_tp_price := computed_tp
state.init_sl_dist := math.abs(bos_fill_price - computed_sl)
state.init_tp_dist := math.abs(computed_tp - bos_fill_price)
state.sl_mode := sl_manage_mode
state.be_triggered := false
state.be_time := na
state.exit_time := na
if sl_manage_mode == "Trail"
state.trail_dist := math.abs(computed_sl - bos_fill_price)
raw_ticks = int(math.round(state.trail_dist / mintick))
state.trail_offset_ticks := math.max(raw_ticks, 1)
state.trail_best := bos_fill_price
offset_px = state.trail_offset_ticks * mintick
state.trail_sl_price := bos_entry_dir == "bullish" ? (bos_fill_price - offset_px) : (bos_fill_price + offset_px)
state.sl_price := state.trail_sl_price
// Consume the tap
state.tap_state := 0
state.awaiting_bos := false
state.tap_time := na
// ─────────────────────────────────────────────────────────────────────────────
// STRATEGY ORDERS (chart-bar scope only)
// ─────────────────────────────────────────────────────────────────────────────
order_qty = int(math.round(strategy.default_entry_qty(close)))
// ── Limit Order mode ──────────────────────────────────────────────────────────
if entry_mode == "Limit Order"
if not state.trade_banned and state.limit_pending and strategy.position_size == 0 and not na(state.limit_price)
if state.trade_dir == "bullish"
strategy.entry("Long", strategy.long, qty=order_qty, limit=state.limit_price)
else if state.trade_dir == "bearish"
strategy.entry("Short", strategy.short, qty=order_qty, limit=state.limit_price)
if not state.limit_pending and na(state.entry_price) and strategy.position_size == 0
strategy.cancel_all()
// ── BOS Confirmation mode — market entry on BOS bar close ────────────────────
if entry_mode == "BOS Confirmation"
if state.trade_active and not state.pos_opened and strategy.position_size == 0 and not na(state.entry_price)
if state.entry_dir == "bullish"
strategy.entry("Long", strategy.long, qty=order_qty)
else if state.entry_dir == "bearish"
strategy.entry("Short", strategy.short, qty=order_qty)
// ── Shared: exits for any active position (both modes) ───────────────────────
if not na(state.entry_price) and not na(state.tp_price)
if strategy.position_size > 0
if state.sl_mode == "Trail" and not na(state.trail_offset_ticks)
strategy.exit("Long Exit", "Long",
limit = state.tp_price,
trail_price = state.entry_price,
trail_offset = state.trail_offset_ticks)
else if not na(state.sl_price)
strategy.exit("Long Exit", "Long", stop=state.sl_price, limit=state.tp_price)
else if strategy.position_size < 0
if state.sl_mode == "Trail" and not na(state.trail_offset_ticks)
strategy.exit("Short Exit", "Short",
limit = state.tp_price,
trail_price = state.entry_price,
trail_offset = state.trail_offset_ticks)
else if not na(state.sl_price)
strategy.exit("Short Exit", "Short", stop=state.sl_price, limit=state.tp_price)
if do_close_all
strategy.close_all(comment="NY Close")
do_close_all := false
// ─────────────────────────────────────────────────────────────────────────────
// KEEP VISUALS ACTIVE UNTIL THE POSITION IS ACTUALLY CLOSED
// ─────────────────────────────────────────────────────────────────────────────
if state.trade_active
if not state.pos_opened and strategy.position_size != 0
state.pos_opened := true
if state.pos_opened and strategy.position_size == 0
state.trade_active := false
state.exit_time := time
// ─────────────────────────────────────────────────────────────────────────────
// DRAWINGS — called once per chart bar after all intrabar processing
// ─────────────────────────────────────────────────────────────────────────────
if show_asia
process_session_drawings(asia)
process_archived_drawings(asia_sessions)
if show_london
process_session_drawings(london)
process_archived_drawings(london_sessions)
ib_last_time = n_ib > 0 ? array.get(ib_times, n_ib - 1) : na
ny_session_end_time = not na(asia.session_start_time) ? asia.session_start_time + 22 * 60 * 60 * 1000 : na
if not na(state.zone_high) and not na(state.zone_start_time) and not na(ny_session_end_time) and not na(ib_last_time)
box_end_x = math.min(ib_last_time, ny_session_end_time)
box_txt = state.range_too_large ? "Range Too Large" : ""
if na(state.zone_box)
state.zone_box := box.new(
left=state.zone_start_time, top=state.zone_high, right=box_end_x, bottom=state.zone_low,
xloc=xloc.bar_time,
bgcolor=color.new(color.teal, 85),
border_color=color.new(color.teal, 0),
border_width=1,
text=box_txt,
text_color=color.black,
text_halign=text.align_center,
text_valign=text.align_center,
text_size=size.normal)
else
box.set_right(state.zone_box, box_end_x)
box.set_text(state.zone_box, box_txt)
box.set_text_color(state.zone_box, color.black)
box.set_text_halign(state.zone_box, text.align_center)
box.set_text_valign(state.zone_box, text.align_center)
if not na(state.zone_mid) and not na(state.zone_start_time) and not na(ny_session_end_time) and not na(ib_last_time)
mid_end_x = math.min(ib_last_time, ny_session_end_time)
if na(state.zone_mid_line)
state.zone_mid_line := line.new(x1=state.zone_start_time, y1=state.zone_mid, x2=mid_end_x, y2=state.zone_mid, xloc=xloc.bar_time, color=color.new(color.black, 0), width=1, style=line.style_dashed)
else
line.set_x2(state.zone_mid_line, mid_end_x)
// ── BOS line visualization ────────────────────────────────────────────────────
if show_bos_lines and entry_mode == "BOS Confirmation" and state.entry_time == time
if bos_ev_dir == 1
bos_ln = line.new(x1=bos_ev_left_ts, y1=bos_ev_level, x2=bos_ev_right_ts, y2=bos_ev_level, xloc=xloc.bar_time, color=color.new(color.green, 0), width=2, extend=extend.none)
array.push(bos_bull_lines, bos_ln)
if array.size(bos_bull_lines) > BOS_MAX_LINES
if array.size(bos_bull_lines) > 0
line.delete(array.shift(bos_bull_lines))
else if bos_ev_dir == -1
bos_ln = line.new(x1=bos_ev_left_ts, y1=bos_ev_level, x2=bos_ev_right_ts, y2=bos_ev_level, xloc=xloc.bar_time, color=color.new(color.red, 0), width=2, extend=extend.none)
array.push(bos_bear_lines, bos_ln)
if array.size(bos_bear_lines) > BOS_MAX_LINES
if array.size(bos_bear_lines) > 0
line.delete(array.shift(bos_bear_lines))
in_asia_session_chart = not na(time(timeframe.period, asia_time, "America/New_York"))
in_london_session_chart = not na(time(timeframe.period, london_time, "America/New_York"))
asia_high_for_fill = show_asia and not na(asia.high) and in_asia_session_chart ? asia.high : na
asia_low_for_fill = show_asia and not na(asia.low) and in_asia_session_chart ? asia.low : na
london_high_for_fill = show_london and not na(london.high) and in_london_session_chart ? london.high : na
london_low_for_fill = show_london and not na(london.low) and in_london_session_chart ? london.low : na
asia_high_plot = plot(asia_high_for_fill, color=color.new(color.white, 100), linewidth=1, editable=false, title="Asia High Fill")
asia_low_plot = plot(asia_low_for_fill, color=color.new(color.white, 100), linewidth=1, editable=false, title="Asia Low Fill")
london_high_plot = plot(london_high_for_fill, color=color.new(color.white, 100), linewidth=1, editable=false, title="London High Fill")
london_low_plot = plot(london_low_for_fill, color=color.new(color.white, 100), linewidth=1, editable=false, title="London Low Fill")
fill(asia_high_plot, asia_low_plot, color=show_asia ? color.new(color.yellow, 90) : na, title="Asia Session Background")
fill(london_high_plot, london_low_plot, color=show_london ? color.new(color.teal, 90) : na, title="London Session Background")
bg_color = show_bg and not na(state.trade_dir) ? (state.trade_dir == "bullish" ? color.new(color.green, 95) : color.new(color.red, 95)) : na
bgcolor(bg_color, title="Trade Direction Background")
// ─────────────────────────────────────────────────────────────────────────────
// TRAIL VISUALIZATION VIA "TWO PLOTS + FILL"
// ─────────────────────────────────────────────────────────────────────────────
trail_mode_should_plot = (state.sl_mode == "Trail") and not na(state.entry_price) and not na(state.trail_sl_price) and (state.trade_active or (not state.trade_active and not na(state.exit_time) and time == state.exit_time))
trail_entry_series = trail_mode_should_plot ? state.entry_price : na
trail_sl_series = trail_mode_should_plot ? state.trail_sl_price : na
trail_entry_plot = plot(trail_entry_series, color=color.new(color.red, 100), linewidth=1, title="Trail Entry (Hidden)")
trail_sl_plot = plot(trail_sl_series, color=color.new(color.red, 100), linewidth=1, title="Trail SL (Hidden)")
fill(trail_entry_plot, trail_sl_plot, color=trail_mode_should_plot ? color.new(color.red, 70) : na, title="Trailing SL Fill")
// ─────────────────────────────────────────────────────────────────────────────
// TRADE VISUALISATION (boxes/labels)
// ─────────────────────────────────────────────────────────────────────────────
if not na(state.entry_time) and not na(state.entry_price) and not na(state.entry_dir)
is_long = state.entry_dir == "bullish"
tp_top = is_long ? state.tp_price : state.entry_price
tp_bottom = is_long ? state.entry_price : state.tp_price
right_edge = state.trade_active ? time : state.exit_time
be_mode = state.sl_mode == "Move to BE"
trail_mode = state.sl_mode == "Trail"
if na(state.tp_box)
state.tp_box := box.new(left=state.entry_time, top=tp_top, right=right_edge, bottom=tp_bottom, xloc=xloc.bar_time, bgcolor=color.new(color.green, 80), border_color=na)
state.tp_label := label.new(x=right_edge, y=state.tp_price, text=state.tp_label_text, xloc=xloc.bar_time, style=label.style_label_left, color=color.new(color.green, 60), textcolor=color.white, size=size.small, text_font_family=font.family_monospace)
else
box.set_right(state.tp_box, right_edge)
label.set_x(state.tp_label, right_edge)
if trail_mode
if na(state.trail_label)
state.trail_label := label.new(
x=right_edge,
y=state.trail_sl_price,
text=str.tostring(math.round(state.trail_dist, 2)) + " trail",
xloc=xloc.bar_time,
style=label.style_label_left,
color=color.new(color.red, 60),
textcolor=color.white,
size=size.small,
text_font_family=font.family_monospace)
else
label.set_x(state.trail_label, right_edge)
label.set_y(state.trail_label, state.trail_sl_price)
else
sl_right = be_mode and state.be_triggered ? state.be_time : right_edge
sl_top = is_long ? state.entry_price : state.sl_price
sl_bottom = is_long ? state.sl_price : state.entry_price
if na(state.sl_box)
state.sl_box := box.new(left=state.entry_time, top=sl_top, right=sl_right, bottom=sl_bottom, xloc=xloc.bar_time, bgcolor=color.new(color.red, 80), border_color=na)
state.sl_label := label.new(x=sl_right, y=state.sl_price, text=state.sl_label_text, xloc=xloc.bar_time, style=label.style_label_left, color=color.new(color.red, 60), textcolor=color.white, size=size.small, text_font_family=font.family_monospace)
else
box.set_right(state.sl_box, sl_right)
label.set_x(state.sl_label, sl_right)
if be_mode and state.be_triggered
if na(state.be_label)
state.be_label := label.new(x=right_edge, y=state.entry_price, text="B/E", xloc=xloc.bar_time, style=label.style_label_left, color=color.new(color.red, 60), textcolor=color.white, size=size.small, text_font_family=font.family_monospace)
else
label.set_x(state.be_label, right_edge)
@danielconde03
Copy link
Copy Markdown

Super interesting how did you code this

@rebeccabibyegt
Copy link
Copy Markdown

rebeccabibyegt commented Mar 17, 2026

where's code from your other videos? like justin werlein's strategy you claimed to code, etc. @deltatrend

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment