Your sizing rule is the right approach. Adjusting size on PB2 to recover a PB1 loss is equity curve suicide. It contaminates the risk model and turns a structural entry system into a martingale trap.
To align with a pure price-action approach, I have rewritten the Pine Script. This version strips out lagging indicators (like the ATR used previously) and relies strictly on market microstructure. It establishes the initial drive via an Initial Balance (IB) breakout (the opening hour range) and tracks structural pivot points to label the pullbacks.
Code: Select all
//@version=5
indicator("Session Structure & Pullback Sequence", overlay=true, max_labels_count=500)
// =============================================================================
// INPUTS
// =============================================================================
grp_sess = "Session & Microstructure"
sess_time = input.session("0800-1600", title="Trading Session", group=grp_sess)
ib_bars = input.int(12, title="Initial Balance Bars (e.g. 12 on 5m = 1H)", minval=1, group=grp_sess, tooltip="Defines the opening range. A break of this range establishes the drive direction.")
pivot_len = input.int(3, title="Pivot Length (L/R)", minval=1, group=grp_sess, tooltip="Number of bars required to confirm a structural swing high/low.")
// =============================================================================
// SESSION STATE MANAGEMENT
// =============================================================================
var bool in_session = false
var int bar_count = 0
var float ib_high = na
var float ib_low = na
var int drive_dir = 0 // 1 = Bullish Drive, -1 = Bearish Drive
var int pb_count = 0
// Detect session boundaries
is_active = time(timeframe.period, sess_time) != 0
new_sess = is_active and not is_active[1]
if new_sess
in_session := true
bar_count := 0
ib_high := high
ib_low := low
drive_dir := 0
pb_count := 0
if in_session and not is_active
in_session := false
// =============================================================================
// INITIAL BALANCE & DRIVE DETECTION
// =============================================================================
if in_session
bar_count += 1
// Build Initial Balance (IB) range
if bar_count <= ib_bars
ib_high := math.max(ib_high, high)
ib_low := math.min(ib_low, low)
// Establish Drive Direction via IB Breakout
if bar_count > ib_bars and drive_dir == 0
if close > ib_high
drive_dir := 1
else if close < ib_low
drive_dir := -1
// =============================================================================
// PULLBACK IDENTIFICATION (RAW PRICE ACTION)
// =============================================================================
ph = ta.pivothigh(high, pivot_len, pivot_len)
pl = ta.pivotlow(low, pivot_len, pivot_len)
// Track Bullish Pullbacks (Pivot Lows forming after a Bullish Drive)
if in_session and drive_dir == 1 and not na(pl)
pb_count += 1
if pb_count == 1
label.new(bar_index[pivot_len], pl, "PB1 (Inducement?)", style=label.style_label_up, color=color.new(#FF9800, 10), textcolor=color.white, size=size.small)
else if pb_count == 2
label.new(bar_index[pivot_len], pl, "PB2 (Structural)", style=label.style_label_up, color=color.new(#4CAF50, 10), textcolor=color.white, size=size.small)
// Track Bearish Pullbacks (Pivot Highs forming after a Bearish Drive)
if in_session and drive_dir == -1 and not na(ph)
pb_count += 1
if pb_count == 1
label.new(bar_index[pivot_len], ph, "PB1 (Inducement?)", style=label.style_label_down, color=color.new(#FF9800, 10), textcolor=color.white, size=size.small)
else if pb_count == 2
label.new(bar_index[pivot_len], ph, "PB2 (Structural)", style=label.style_label_down, color=color.new(#4CAF50, 10), textcolor=color.white, size=size.small)
// =============================================================================
// VISUAL RENDERING
// =============================================================================
// Draw IB Range for context
var line ib_top_line = na
var line ib_bot_line = na
if new_sess
ib_top_line := line.new(bar_index, high, bar_index, high, color=color.new(color.gray, 50), style=line.style_dashed)
ib_bot_line := line.new(bar_index, low, bar_index, low, color=color.new(color.gray, 50), style=line.style_dashed)
if in_session and bar_count <= ib_bars
line.set_y2(ib_top_line, ib_high)
line.set_y1(ib_top_line, ib_high)
line.set_x2(ib_top_line, bar_index)
line.set_y2(ib_bot_line, ib_low)
line.set_y1(ib_bot_line, ib_low)
line.set_x2(ib_bot_line, bar_index)
if in_session and bar_count > ib_bars
line.set_x2(ib_top_line, bar_index)
line.set_x2(ib_bot_line, bar_index)
bgcolor(is_active ? color.new(color.navy, 96) : na, title="Session Background")