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)
@deltatrend
Copy link
Copy Markdown
Author

Are you Trying to make people lose more money?

Obviously not – did you watch my video explaining that this TikTok influencer strategy does not generate alpha, and has uninteresting prop firm performance?

I've posted this for auditability, not as financial advice. It's unlikely that you don't know this, assuming you arrived here from my video explicitly condemning this strategy.

@Zeezudee26767
Copy link
Copy Markdown

Hey i got a question, wouldn’t filled = state.trade_dir == "bullish" ? l <= state.limit_price : h >= state.limit_price
assume fill if price touches midline, but in reality, limit orders fill at limit price or better? And also for ATR Fallback, checks every bar after 9:30, but uses m1_atr so if on 15m ATR updates slowly and 1m its computationally heavy? And also wouldnt if require_clean_bias and not state.trade_taken and state.touched_below_since_815
be too restrictive for volatile opens like if price touches low at 8:16 but closes above midline at 9:30 then bias is rejected?

@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