-
-
Save deltatrend/ec550d13ea6246f25e41023e77426247 to your computer and use it in GitHub Desktop.
| //@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) |
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.
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?
Super interesting how did you code this
where's code from your other videos? like justin werlein's strategy you claimed to code, etc. @deltatrend
Learning PineScript myself. This was SUPER helpful!