OPEN-SOURCE SCRIPT
Price Action Concepts +2

// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org/MPL/2.0/
// © iss2k/StratifyTrade – modified to add lines, fixed levels & Heikin‑Ashi
//version=5
indicator("Price Action Concepts +", shorttitle = "PA Concepts+", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)
//-----------------------------------------------------------------------------{
//Boolean set
//-----------------------------------------------------------------------------{
s_bos = 0
s_choch = 1
i_bos = 2
i_choch = 3
i_pp_choch = 4
green_candle = 5
red_candle = 6
boolean =
array.from(
false // s_bos
, false // s_choch
, false // i_bos
, false // i_choch
, false // i_pp_choch
, false // up
, false // dn
)
//-----------------------------------------------------------------------------{
// User inputs
//-----------------------------------------------------------------------------{
//
//
//==> Market Structure
show_swing_ms = input.string ("All" , "Swing " , inline = "1", group = "MARKET STRUCTURE" , options = ["All", "CHoCH", "BOS", "None"])
show_internal_ms = input.string ("All" , "Internal" , inline = "2", group = "MARKET STRUCTURE" , options = ["All", "CHoCH", "BOS", "CHoCH+", "None"])
internal_r_lookback = input.int (5 , "" , inline = "2", group = "MARKET STRUCTURE" , minval = 2)
swing_r_lookback = input.int (50 , "" , inline = "1", group = "MARKET STRUCTURE" , minval = 2)
plotcandle_bool = input.bool (false , "Plotcandle" , inline = "3", group = "MARKET STRUCTURE" , tooltip = "Plot a better coloring chart (require disable the current ticker)")
barcolor_bool = input.bool (false , "Bar Color" , inline = "4", group = "MARKET STRUCTURE" , tooltip = "Normal bar coloring")
internal_l_lookback = math.round (internal_r_lookback)
swing_l_lookback = (swing_r_lookback )
i_ms_up_bos = input.color (#42bda8 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_up_choch = input.color (#056656 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_dn_bos = input.color (#ff5252 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_dn_choch = input.color (#b22833 , "" , inline = "2", group = "MARKET STRUCTURE")
s_ms_up_bos = input.color (#42bda8 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_up_choch = input.color (#056656 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_dn_bos = input.color (#ff5252 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_dn_choch = input.color (#b22833 , "" , inline = "1", group = "MARKET STRUCTURE")
//
//
//==> PHL
lvl_daily = input.bool (false , "Day " , inline = "1", group = "HIGHS & LOWS MTF")
lvl_weekly = input.bool (false , "Week " , inline = "2", group = "HIGHS & LOWS MTF")
lvl_monthly = input.bool (false , "Month" , inline = "3", group = "HIGHS & LOWS MTF")
lvl_yearly = input.bool (false , "Year " , inline = "4", group = "HIGHS & LOWS MTF")
css_d = input.color (color.blue , "" , inline = "1", group = "HIGHS & LOWS MTF")
css_w = input.color (color.blue , "" , inline = "2", group = "HIGHS & LOWS MTF")
css_m = input.color (color.blue , "" , inline = "3", group = "HIGHS & LOWS MTF")
css_y = input.color (color.blue , "" , inline = "4", group = "HIGHS & LOWS MTF")
s_d = input.string ('⎯⎯⎯' , '' , inline = '1', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_w = input.string ('⎯⎯⎯' , '' , inline = '2', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_m = input.string ('⎯⎯⎯' , '' , inline = '3', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_y = input.string ('⎯⎯⎯' , '' , inline = '4', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
//
//
//==> Volumetric Order Blocks
ob_show = input.bool (true , "Show Last " , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volumetric order blocks on the chart \n\n[Input] Ammount of volumetric order blocks to show")
ob_num = input.int (2 , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Orderblocks number", minval = 1, maxval = 10)
ob_metrics_show = input.bool (true , "Internal Buy/Sell Activity" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volume metrics that have formed the orderblock")
css_metric_up = input.color (color.new(color.teal, 50) , " " , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
css_metric_dn = input.color (color.new(color.red , 50) , "" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
ob_swings = input.bool (false , "Swing Order Blocks" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display swing volumetric order blocks")
css_swing_up = input.color (color.new(#42bda8, 40) , " " , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
css_swing_dn = input.color (color.new(#ff5252, 40) , "" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
ob_lookback_bool = input.bool (false , "Lenght " , inline = "3", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "[Enable] Use the lenght period to determine the lowest/highest point of the orderblock creation\n\n[Disable] Use the last opposite candle to determine the orderblock creation")
ob_filter = input.string ("None" , "Filtering " , inline = "d", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Filter out volumetric order blocks by BOS/CHoCH/CHoCH+", options = ["None", "BOS", "CHoCH", "CHoCH+"])
ob_looback = input.int (5 , "" , inline = "3", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Lookback to find lowest point for the construction of the volumetric order blocks")
ob_mitigation = input.string ("Absolute" , "Mitigation " , inline = "4", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Trigger to remove volumetric order blocks", options = ["Absolute", "Middle"])
ob_timeframe = input.timeframe ("" , "Timeframe " , inline = "5", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Timeframe of the volumetric order blocks")
use_grayscale = input.bool (true , "Grayscale" , inline = "6", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Use gray as basic order blocks color")
use_show_metric = input.bool (true , "Show Metrics" , inline = "7", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show volume associated with the orderblock and his relevance")
use_middle_line = input.bool (true , "Show Middle-Line" , inline = "8", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show mid-line order blocks")
use_overlap = input.bool (true , "Hide Overlap" , inline = "9", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Hide overlapping order blocks")
use_overlap_method = input.string ("Previous" , "Overlap Method " , inline = "Z", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "[Recent] Preserve the most recent volumetric order blocks\n\n[Previous] Preserve the previous volumetric order blocks", options = ["Recent", "Previous"])
ob_bull_css = input.color (color.new(color.teal, 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
ob_bear_css = input.color (color.new(color.red , 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
//
//
//==> Premium/Discount Zone
show_lbl = input.bool (false , "Show swing point" , inline = "1", group = "High and Low" , tooltip = "Display swing point")
show_mtb = input.bool (false , "Show High/Low/Equilibrium" , inline = "2", group = "High and Low" , tooltip = "Display Strong/Weak High And Low and Equilibrium")
toplvl = input.color (color.red , "Premium Zone " , inline = "3", group = "High and Low")
midlvl = input.color (color.white , "Equilibrium Zone" , inline = "4", group = "High and Low")
btmlvl = input.color (color.teal , "Discount Zone " , inline = "5", group = "High and Low")
//
//
//==> FVG
fvg_enable = input.bool (false , "FVG " , inline = "1", group = "FVG" , tooltip = "Display fair value gap")
fvg_upcss = input.color (color.new(color.aqua, 80) , "" , inline = "1", group = "FVG")
fvg_dncss = input.color (color.new(color.red , 80) , "" , inline = "1", group = "FVG")
fvg_extend = input.int (10 , "Extend FVG" , inline = "2", group = "FVG" , tooltip = "Extend FVG")
fvg_src = input.string ("Close" , "Mitigation " , inline = "3", group = "FVG" , tooltip = "[Close] Use the close of the body as trigger\n\n[Wick] Use the extreme point of the body as trigger", options = ["Close", "Wick"])
fvg_tf = input.timeframe ("" , "Timeframe " , inline = "4", group = "FVG" , tooltip = "Timeframe of the fair value gap")
//
//
//==> EXTRA SETTINGS (added for picture 15 look)
show_ob_lines = input.bool (true , "Show OB Lines (instead of boxes)", group = "EXTRA SETTINGS")
line_extend_bars = input.int (50 , "Line Extension (bars)" , group = "EXTRA SETTINGS")
show_fixed_lines = input.bool (false , "Show Fixed Price Levels" , group = "EXTRA SETTINGS")
fixed_prices = input.string ("4983.772,4982,4980,4978.74,4979,4975.276,4974.5,4972,4971,4969.024", "Fixed Prices (comma)", group = "EXTRA SETTINGS")
fixed_line_color = input.color (color.gray , "Fixed Line Color" , group = "EXTRA SETTINGS")
show_heikin_ashi = input.bool (false , "Overlay Heikin‑Ashi" , group = "EXTRA SETTINGS")
//
//
//==> Utility Colors
t = color.t (ob_bull_css)
invcol = color.new (color.white , 100)
switch use_grayscale
true => ob_bull_css := color.gray, ob_bear_css := color.gray
=> na
//-----------------------------------------------------------------------------{
// Switch market strcture visuals
//-----------------------------------------------------------------------------{
switch show_swing_ms
"All" => boolean.set(s_bos, true ), boolean.set(s_choch, true )
"CHoCH" => boolean.set(s_bos, false), boolean.set(s_choch, true )
"BOS" => boolean.set(s_bos, true ), boolean.set(s_choch, false)
"None" => boolean.set(s_bos, false), boolean.set(s_choch, false)
=> na
switch show_internal_ms
"All" => boolean.set(i_bos, true ), boolean.set(i_choch, true ), boolean.set(i_pp_choch, true )
"CHoCH" => boolean.set(i_bos, false), boolean.set(i_choch, true ), boolean.set(i_pp_choch, false)
"BOS" => boolean.set(i_bos, true ), boolean.set(i_choch, false), boolean.set(i_pp_choch, false)
"CHoCH+" => boolean.set(i_bos, false), boolean.set(i_choch, false), boolean.set(i_pp_choch, true )
"None" => boolean.set(i_bos, false), boolean.set(i_choch, false), boolean.set(i_pp_choch, false)
=> na
//-----------------------------------------------------------------------------{
// Custom Type (renamed from 'bar' to 'BarData')
//-----------------------------------------------------------------------------{
type BarData
float o = open
float c = close
float h = high
float l = low
float v = volume
int n = bar_index
int t = time
string xt = xloc.bar_time
string xn = xloc.bar_index // fixed typo: was yloc.bar_index
type bin
float [] i_hpoint
float [] i_lpoint
int [] i_nBull
int [] i_nBear
float [] s_hpoint
float [] s_lpoint
int [] s_nBull
int [] s_nBear
float [] up_ms_logs
float [] dn_ms_logs
string[] i_bulltxt
string[] i_beartxt
type ob
float[] up_vol
float[] up_top
float[] up_bottom
int [] up_current_time
int [] up_last_time
int [] up_n
float[] dn_vol
float[] dn_top
float[] dn_bottom
int [] dn_current_time
int [] dn_last_time
int [] dn_n
float[] metric_up
float[] metric_dn
type Zphl
line top
line bottom
label top_label
label bottom_label
bool stopcross
bool sbottomcross
bool itopcross
bool ibottomcross
string txtup
string txtdn
float topy
float bottomy
float topx
float bottomx
float tup
float tdn
int tupx
int tdnx
float itopy
float itopx
float ibottomy
float ibottomx
type tfvg
box[] bull_max
box[] bull_min
box[] bear_max
box[] bear_min
//-----------------------------------------------------------------------------{
// Type set
//-----------------------------------------------------------------------------{
BarData b = BarData.new() // updated to use BarData
// ---- Heikin‑Ashi calculation (if overlay is on) ----
var float ha_open = na
var float ha_close = na
float ha_high = na
float ha_low = na
if show_heikin_ashi
ha_close := (open + high + low + close) / 4
ha_open := na(ha_open[1]) ? (open + close) / 2 : (ha_open[1] + ha_close[1]) / 2
ha_high := math.max(high, ha_open, ha_close)
ha_low := math.min(low , ha_open, ha_close)
// ----------------------------------------------------
var pp = bin.new(
array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< string >(1, na)
, array.new< string >(1, na)
)
var obv = ob.new(
array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
)
var phl = Zphl.new(
na
, na
, label.new(na , na , color = invcol , textcolor = i_ms_dn_bos , style = label.style_label_down , size = size.tiny , text = "")
, label.new(na , na , color = invcol , textcolor = i_ms_up_bos , style = label.style_label_up , size = size.tiny , text = "")
, true
, true
, true
, true
, ""
, ""
, 0
, 0
, 0
, 0
, high
, low
, 0
, 0
, 0
, 0
, 0
, 0
)
var fvg = tfvg.new(
array.new<box>()
, array.new<box>()
, array.new<box>()
, array.new<box>()
)
var msline = array.new<line>(0)
// ---- Additional arrays for lines/labels (for OB lines) ----
var line[] ob_top_lines = array.new<line>()
var line[] ob_bottom_lines = array.new<line>()
var line[] ob_mid_lines = array.new<line>()
var label[] ob_labels = array.new<label>()
// ----------------------------------------------------------
obsec(float src) =>
id = request.security("", ob_timeframe, src)
id
lstyle(style) =>
out = switch style
'⎯⎯⎯' => line.style_solid
'----' => line.style_dashed
'····' => line.style_dotted
zhl(len)=>
upper = ta.highest(len)
lower = ta.lowest(len)
var float out = 0
out := b.h[len] > upper ? 0 : b.l[len] < lower ? 1 : out[1]
top = out == 0 and out[1] != 0 ? b.h[len] : 0
btm = out == 1 and out[1] != 1 ? b.l[len] : 0
[top, btm]
[top , btm ] = zhl(swing_r_lookback )
[itop, ibtm] = zhl(internal_r_lookback)
mtfphl(h, l ,tf ,css, pdhl_style) =>
var line hl = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var line ll = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var label lbl = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}L', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
var label hlb = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}H', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
hy = ta.valuewhen(h != h[1] , h , 1)
hx = ta.valuewhen(h == high , time , 1)
ly = ta.valuewhen(l != l[1] , l , 1)
lx = ta.valuewhen(l == low , time , 1)
if barstate.islast
extension = time + (time - time[1]) * 50 // difference in time
line.set_xy1(hl , hx , hy)
line.set_xy2(hl , extension , hy)
label.set_xy(hlb, extension , hy)
line.set_xy1(ll , lx , ly)
line.set_xy2(ll , extension , ly)
label.set_xy(lbl, extension , ly)
upphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
if top
phl.stopcross := true
phl.txtup := top > phl.topy ? "HH" : "LH"
if show_lbl
topl = label.new(
b.n - swing_r_lookback
, top
, phl.txtup
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
line.delete(phl.top[1])
phl.top := line.new(
b.n - swing_r_lookback
, top
, b.n
, top
, color = toplvl)
phl.topy := top
phl.topx := b.n - swing_r_lookback
phl.tup := top
phl.tupx := b.n - swing_r_lookback
if itop
phl.itopcross := true
phl.itopy := itop
phl.itopx := b.n - internal_r_lookback
phl.tup := math.max(high, phl.tup)
phl.tupx := phl.tup == high ? b.n : phl.tupx
if barstate.islast
line.set_xy1(
phl.top
, phl.tupx
, phl.tup
)
line.set_xy2(
phl.top
, b.n + 50
, phl.tup
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tup
)
dist = math.abs((phl.tup - close) / close) * 1000
label.set_text (lbl, trend < 0 ? "Strong High" + " (" + str.tostring(math.round(dist,0)) + "%)" : "Weak High" + " (" + str.tostring(math.round(dist,0)) + "%)")
dnphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
if btm
phl.sbottomcross := true
phl.txtdn := btm > phl.bottomy ? "HH" : "LH"
if show_lbl
btml = label.new(
b.n - swing_r_lookback
, btm, phl.txtdn
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
line.delete(phl.bottom[1])
phl.bottom := line.new(
b.n - swing_r_lookback
, btm
, b.n
, btm
, color = btmlvl
)
phl.bottomy := btm
phl.bottomx := b.n - swing_r_lookback
phl.tdn := btm
phl.tdnx := b.n - swing_r_lookback
if ibtm
phl.ibottomcross := true
phl.ibottomy := ibtm
phl.ibottomx := b.n - internal_r_lookback
phl.tdn := math.min(low, phl.tdn)
phl.tdnx := phl.tdn == low ? b.n : phl.tdnx
if barstate.islast
line.set_xy1(
phl.bottom
, phl.tdnx
, phl.tdn
)
line.set_xy2(
phl.bottom
, b.n + 50
, phl.tdn
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tdn
)
dist = math.abs((phl.tdn - close) / close) * 1000
label.set_text (lbl, trend > 0 ? "Strong Low" + " (" + str.tostring(math.round(dist,0)) + "%)" : "Weak Low" + " (" + str.tostring(math.round(dist,0)) + "%)")
midphl() =>
avg = math.avg(phl.bottom.get_y2(), phl.top.get_y2())
var line l = line.new(
y1 = avg
, y2 = avg
, x1 = b.n - swing_r_lookback
, x2 = b.n + 50
, color = midlvl
, style = line.style_solid
)
var label lbl = label.new(
x = b.n + 50
, y = avg
, text = "Equilibrium"
, style = label.style_label_left
, color = invcol
, textcolor = midlvl
, size = size.small
)
if barstate.islast
more = (phl.bottom.get_x1() + phl.bottom.get_x2()) > (phl.top.get_x1() + phl.top.get_x2()) ? phl.top.get_x1() : phl.bottom.get_x1()
line.set_xy1(l , more , avg)
line.set_xy2(l , b.n + 50, avg)
label.set_x (lbl , b.n + 50 )
label.set_y (lbl , avg )
dist = math.abs((l.get_y2() - close) / close) * 1000
label.set_text (lbl, "Equilibrium (" + str.tostring(math.round(dist,0)) + "%)")
vol() =>
float posVol = 0.0
float negVol = 0.0
switch
close > open => posVol += volume
close < open => negVol -= volume
close >= close[1] => posVol += volume
close < close[1] => negVol -= volume
[posVol, negVol]
[upvol, dnvol] = request.security("", ob_timeframe, vol())
volblock(bool condition, bool bull, string mitigation, float top, float bottom, int left, int right, color css, float upx, float dnx, int position, float upv, float dnv) =>
var ob_top = array.new< float >()
var ob_bottom = array.new< float >()
var ob_left = array.new< int >()
var ob_right = array.new< int >()
var ob_avg = array.new< float >()
var ob_vol = array.new< float >()
var redvol = array.new< float >()
var greenvol = array.new< float >()
var pos = array.new< int >()
var when = array.new< int >()
var countup = array.new< int >()
var countdn = array.new< int >()
var orderblock = array.new< box >()
var continuationorderblock = array.new< box >()
var uporderblock = array.new< box >()
var dnorderblock = array.new< box >()
var middlelvl = array.new< line >()
float sum = math.abs(ob_vol.sum())
if condition
paperlow = array.new< float >()
paperhigh = array.new< float >()
paperindex = array.new< int >()
paperlow .clear()
paperhigh.clear()
float avg = na
var bool isna = na
for i = 1 to ob_looback + 1
paperlow .push(low )
paperhigh .push(high)
paperindex.push(time)
if ob_lookback_bool == false
avg := math.avg(top, bottom)
ob_top .unshift(top )
ob_bottom.unshift(bottom)
ob_left .unshift(left )
if ob_lookback_bool
avg := bull ? math.avg(paperlow.min(), paperhigh.get(paperlow.indexof(paperlow.min()))) : math.avg(paperhigh.max(), paperlow.get(paperhigh.indexof(paperhigh.max())))
ob_top .unshift(bull ? paperhigh .get(paperlow.indexof(paperlow.min())) : paperhigh .max())
ob_bottom.unshift(bull ? paperlow .min() : paperlow .get(paperhigh.indexof(paperhigh.max())))
ob_left .unshift(bull ? paperindex.get(paperlow.indexof(paperlow.min())) : paperindex.get(paperhigh.indexof(paperhigh.max())))
ob_right .unshift(right )
ob_avg .unshift(avg )
ob_vol .unshift(bull ? dnv : upv)
redvol .unshift(dnx )
greenvol .unshift(upx )
pos .unshift(position )
when .unshift(1 )
countup .unshift(0 )
countdn .unshift(0 )
if use_overlap
int num = use_overlap_method == "Recent" ? 1 : 0
if ob_avg.size() > 1
if (bull ? ob_bottom.first() < ob_top.get(1) : ob_top.first() > ob_bottom.get(1))
ob_top .remove(num)
ob_bottom.remove(num)
ob_left .remove(num)
ob_right .remove(num)
ob_avg .remove(num)
ob_vol .remove(num)
redvol .remove(num)
greenvol .remove(num)
pos .remove(num)
when .remove(num)
countup .remove(num)
countdn .remove(num)
if barstate.isconfirmed
out = switch mitigation
"Middle" => ob_avg
"Absolute" => bull ? ob_bottom : ob_top
target = out
for stuff in target
idx = target.indexof(stuff)
if (bull ? close < stuff : close > stuff)
ob_top .remove(idx)
ob_bottom.remove(idx)
ob_left .remove(idx)
ob_right .remove(idx)
ob_avg .remove(idx)
ob_vol .remove(idx)
redvol .remove(idx)
greenvol .remove(idx)
pos .remove(idx)
when .remove(idx)
countup .remove(idx)
countdn .remove(idx)
if true
if barstate.isfirst
for i = 0 to ob_num - 1
orderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
continuationorderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.right, bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
uporderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
dnorderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
middlelvl .unshift(line.new(na, na, na, na, xloc = xloc.bar_time, extend = extend.none , color = color.new(css,0), style = line.style_dashed, width = 1))
// ---- Also create placeholder line/label objects for the line style ----
array.unshift(ob_top_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_bottom_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_mid_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_labels, label.new(na, na, xloc = xloc.bar_index))
// ------------------------------------------------------------------------
if barstate.islast
if ob_top.size() > 0
for i = 0 to math.min(ob_num - 1, ob_top.size() - 1)
get_orderblock = orderblock .get(i)
get_continuationorderblock = continuationorderblock.get(i)
get_uporderblock = uporderblock .get(i)
get_dnorderblock = dnorderblock .get(i)
get_middlelvl = middlelvl .get(i)
// MAIN ORDERBLOCK
box.set_top (get_orderblock, ob_top .get(i) )
box.set_bottom(get_orderblock, ob_bottom .get(i) )
box.set_left (get_orderblock, ob_left .get(i) )
box.set_right (get_orderblock, time )
// ---- CONTINUATION: either boxes (default) or lines (if show_ob_lines) ----
if show_ob_lines
// Delete old continuation box (not needed)
box.delete(get_continuationorderblock)
// Draw lines instead
right_index = bar_index + line_extend_bars
// Top line
line.set_xy1(array.get(ob_top_lines, i), ob_left.get(i), ob_top.get(i))
line.set_xy2(array.get(ob_top_lines, i), right_index , ob_top.get(i))
line.set_color(array.get(ob_top_lines, i), css)
line.set_width(array.get(ob_top_lines, i), 1)
line.set_style(array.get(ob_top_lines, i), line.style_solid)
// Bottom line
line.set_xy1(array.get(ob_bottom_lines, i), ob_left.get(i), ob_bottom.get(i))
line.set_xy2(array.get(ob_bottom_lines, i), right_index , ob_bottom.get(i))
line.set_color(array.get(ob_bottom_lines, i), css)
line.set_width(array.get(ob_bottom_lines, i), 1)
line.set_style(array.get(ob_bottom_lines, i), line.style_solid)
// Middle line (optional)
if use_middle_line
line.set_xy1(array.get(ob_mid_lines, i), ob_left.get(i), ob_avg.get(i))
line.set_xy2(array.get(ob_mid_lines, i), right_index , ob_avg.get(i))
line.set_color(array.get(ob_mid_lines, i), color.new(css,70))
line.set_width(array.get(ob_mid_lines, i), 1)
line.set_style(array.get(ob_mid_lines, i), line.style_dashed)
else
line.delete(array.get(ob_mid_lines, i))
// Volume label
if use_show_metric
vol_text = str.tostring(math.round(ob_vol.get(i) / 1000, 3)) + "K"
// Calculate percentage relative to the total volume of all displayed OBs
total_vol = 0.0
for j=0 to math.min(ob_num-1, ob_vol.size()-1)
total_vol += math.abs(ob_vol.get(j))
perc = math.round(ob_vol.get(i) / total_vol * 100, 1)
vol_text += " (" + str.tostring(perc) + "%)"
label.set_x(array.get(ob_labels, i), right_index)
label.set_y(array.get(ob_labels, i), ob_avg.get(i))
label.set_text(array.get(ob_labels, i), vol_text)
label.set_color(array.get(ob_labels, i), color.new(color.white,100))
label.set_textcolor(array.get(ob_labels, i), css)
label.set_style(array.get(ob_labels, i), label.style_label_left)
label.set_size(array.get(ob_labels, i), size.small)
else
label.delete(array.get(ob_labels, i))
else
// Use original continuation box
box.set_top (get_continuationorderblock, ob_top .get(i) )
box.set_bottom(get_continuationorderblock, ob_bottom.get(i) )
box.set_left (get_continuationorderblock, time )
box.set_right (get_continuationorderblock, time + (time - time[1]) * 100)
// Delete any lines from previous line mode
line.delete(array.get(ob_top_lines, i))
line.delete(array.get(ob_bottom_lines, i))
line.delete(array.get(ob_mid_lines, i))
label.delete(array.get(ob_labels, i))
// -------------------------------------------------------------------------
// MIDDLE LEVEL (inside main box) – keep as before
if use_middle_line and not show_ob_lines // only show inside box if not using lines? can keep both, but we already drew mid line above if lines enabled.
line.set_x1(get_middlelvl, ob_left .get(i))
line.set_x2(get_middlelvl, time )
line.set_y1(get_middlelvl, ob_avg .get(i))
line.set_y2(get_middlelvl, ob_avg .get(i))
if ob_metrics_show
box.set_top (get_uporderblock , ob_top .get(i))
box.set_bottom(get_uporderblock , ob_avg .get(i))
box.set_left (get_uporderblock , ob_left .get(i))
box.set_right (get_uporderblock , ob_left .get(i))
box.set_bgcolor(get_uporderblock , css_metric_up )
box.set_top (get_dnorderblock, ob_avg .get(i))
box.set_bottom(get_dnorderblock, ob_bottom .get(i))
box.set_left (get_dnorderblock, ob_left .get(i))
box.set_right (get_dnorderblock, ob_left .get(i))
box.set_bgcolor(get_dnorderblock, css_metric_dn )
takeup = box.get_right(get_uporderblock)
takedn = box.get_right(get_dnorderblock)
box.set_right (get_dnorderblock, takedn + (time - time[1]) * countdn.get(i))
box.set_right (get_uporderblock, takeup + (time - time[1]) * countup.get(i))
if use_show_metric and not show_ob_lines // only show text inside continuation box if not using lines
box.set_text (get_continuationorderblock, str.tostring(math.round(ob_vol.get(i) / 1000,3)) + "K" + " (" + str.tostring( math.abs(math.round((ob_vol.get(i) - (i == 0 ? sum : ob_vol.get(0)) ) / (i == 0 ? sum : ob_vol.get(0)) ,3)) * 10) + "%)")
box.set_text_size (get_continuationorderblock, size.auto)
box.set_text_halign(get_continuationorderblock, text.align_left)
box.set_text_color (get_continuationorderblock, color.new(css,0))
if ob_metrics_show
if barstate.isconfirmed and when.size() > 0
for i = 0 to countup.size() - 1
if redvol.get(i) < greenvol.get(i)
switch when.get(i)
1 => countup.set(i, countup.get(i) + 1), when.set(i, 2)
2 => countup.set(i, countup.get(i) + 1), when.set(i, 3)
3 => countdn.set(i, countdn.get(i) + 1), when.set(i, 1)
if redvol.get(i) > greenvol.get(i)
switch when.get(i)
1 => countdn.set(i, countdn.get(i) + 1), when.set(i, 2)
2 => countdn.set(i, countdn.get(i) + 1), when.set(i, 3)
3 => countup.set(i, countup.get(i) + 1), when.set(i, 1)
f_line(x, y, z, css, txt, down, size, style) =>
var line id = na
var label lbl = na
id := line.new(
x
, y
, z
, y
, xloc = b.xn
, color = css
, width = 1
, style = style
)
if msline.size() == 450 // Limit to 450 to avoid issue with other drawing object
line.delete(msline.shift())
msline.push(id)
lbl := label.new(
int(math.avg(x, z))
, y
, txt
, color = invcol
, textcolor = css
, style = down ? label.style_label_down : label.style_label_up
, size = size
)
//-----------------------------------------------------------------------------{
// Market strcture pivot point
//-----------------------------------------------------------------------------{
switch
b.c > b.o => boolean.set(green_candle, true)
b.c < b.o => boolean.set(red_candle , true)
_v = request.security("", ob_timeframe, volume)
switch
boolean.get(green_candle) => obv.up_bottom.push(obsec(low)), obv.up_top.push(obsec(high)), obv.up_vol.push(math.abs(_v)), obv.up_current_time.push(time), obv.up_last_time.push(time[1]), obv.up_n.push(bar_index), obv.metric_up.push(upvol), obv.metric_dn.push(math.abs(dnvol))
boolean.get(red_candle) => obv.dn_bottom.push(obsec(low)), obv.dn_top.push(obsec(high)), obv.dn_vol.push(math.abs(_v)), obv.dn_current_time.push(time), obv.dn_last_time.push(time[1]), obv.dn_n.push(bar_index), obv.metric_up.push(upvol), obv.metric_dn.push(math.abs(dnvol))
=> na
ph_i = ta.pivothigh(b.h , internal_l_lookback , internal_r_lookback)
pl_i = ta.pivotlow (b.l , internal_l_lookback , internal_r_lookback)
ph_s = ta.pivothigh(b.h , swing_l_lookback , swing_r_lookback )
pl_s = ta.pivotlow (b.l , swing_l_lookback , swing_r_lookback )
switch
ph_i => pp.i_hpoint.clear(), pp.i_nBull.clear(), pp.i_hpoint.push(b.h[internal_r_lookback]), pp.i_nBull.push(b.n[internal_r_lookback]), pp.up_ms_logs.push(b.h[internal_r_lookback])
pl_i => pp.i_lpoint.clear(), pp.i_nBear.clear(), pp.i_lpoint.push(b.l[internal_r_lookback]), pp.i_nBear.push(b.n[internal_r_lookback]), pp.dn_ms_logs.push(b.l[internal_r_lookback])
ph_s => pp.s_hpoint.clear(), pp.s_nBull.clear(), pp.s_hpoint.push(b.h[swing_r_lookback]) , pp.s_nBull.push(b.n[swing_r_lookback] )
pl_s => pp.s_lpoint.clear(), pp.s_nBear.clear(), pp.s_lpoint.push(b.l[swing_r_lookback]) , pp.s_nBear.push(b.n[swing_r_lookback] )
=> na
//-----------------------------------------------------------------------------{
// strcture set
//-----------------------------------------------------------------------------{
method structure(bin zz) =>
var color css = na
var int count = 0
var int trend = 0
var int itrend = 0
var bool ob_bear = false
bool bear_ob = false
bool bull_ob = false
bool swing_up = false
bool swing_dn = false
if true
//-----------------------------------------------------------------------------{
//Internal structure bullish
//-----------------------------------------------------------------------------{
if zz.dn_ms_logs.size() > 1 and zz.i_hpoint.size() > 0 and zz.i_nBull.size() > 0
if ta.crossover(b.c, zz.i_hpoint.last())
bool choch = na
string txt = na
if itrend < 0
choch := true
switch
choch and not (zz.dn_ms_logs.last() > zz.dn_ms_logs.get(zz.dn_ms_logs.indexof(zz.dn_ms_logs.last()) - 1)) => txt := "CHoCH"
choch and (zz.dn_ms_logs.last() > zz.dn_ms_logs.get(zz.dn_ms_logs.indexof(zz.dn_ms_logs.last()) - 1)) => txt := "CHoCH+"
not choch => txt := "BOS"
itrend := 1
switch txt
"CHoCH" => css := i_ms_up_choch
"BOS" => css := i_ms_up_bos
"CHoCH+" => css := i_ms_up_choch
=> css
if ((txt == "BOS" and boolean.get(i_bos) == true) or (txt == "CHoCH" and boolean.get(i_choch) == true) or (txt =="CHoCH+" and boolean.get(i_pp_choch) == true))
f_line(zz.i_nBull.last(), zz.i_hpoint.last(), b.n, i_ms_up_bos, txt, true, size.small, line.style_dashed)
switch
ob_filter == "None" => bull_ob := true
ob_filter == "BOS" and txt == "BOS" => bull_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bull_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bull_ob := true
=> bull_ob := false
zz.i_nBull.clear ()
zz.i_hpoint.clear()
zz.i_bulltxt.push(txt)
//-----------------------------------------------------------------------------{
//Internal structure bearish
//-----------------------------------------------------------------------------{
if zz.up_ms_logs.size() > 1 and zz.i_lpoint.size() > 0 and zz.i_nBear.size() > 0
if ta.crossunder(b.c, zz.i_lpoint.last())
bool choch = na
string txt = na
if itrend > 0
choch := true
switch
choch and not (zz.up_ms_logs.last() < zz.up_ms_logs.get(zz.up_ms_logs.indexof(zz.up_ms_logs.last()) - 1)) => txt := "CHoCH"
choch and (zz.up_ms_logs.last() < zz.up_ms_logs.get(zz.up_ms_logs.indexof(zz.up_ms_logs.last()) - 1)) => txt := "CHoCH+"
not choch => txt := "BOS"
itrend := -1
switch txt
"CHoCH" => css := i_ms_dn_choch
"BOS" => css := i_ms_dn_bos
"CHoCH+" => css := i_ms_dn_choch
=> css
if ((txt == "BOS" and boolean.get(i_bos) == true) or (txt == "CHoCH" and boolean.get(i_choch) == true) or (txt =="CHoCH+" and boolean.get(i_pp_choch) == true))
f_line(zz.i_nBear.last(), zz.i_lpoint.last(), b.n, i_ms_dn_bos, txt, false, size.small, line.style_dashed)
switch
ob_filter == "None" => bear_ob := true
ob_filter == "BOS" and txt == "BOS" => bear_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bear_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bear_ob := true
=> bear_ob := false
zz.i_nBear.clear ()
zz.i_lpoint.clear()
zz.i_beartxt.push(txt)
//-----------------------------------------------------------------------------{
//Swing structure bullish
//-----------------------------------------------------------------------------{
if zz.s_hpoint.size() > 0
if ta.crossover(b.c, zz.s_hpoint.last())
bool choch = na
string txt = na
if trend < 0
choch := true
txt := choch ? "CHoCH" : "BOS"
trend := 1
css := i_ms_up_bos
if ((txt == "BOS" and boolean.get(s_bos) == true) or (txt == "CHoCH" and boolean.get(s_choch) == true))
f_line(zz.s_nBull.last(), zz.s_hpoint.last(), b.n, i_ms_up_bos, txt, true, size.small, line.style_solid)
zz.s_nBull.clear ()
zz.s_hpoint.clear()
if ob_swings
swing_up := true
//-----------------------------------------------------------------------------{
//Swing structure bearish
//-----------------------------------------------------------------------------{
if zz.s_lpoint.size() > 0
if ta.crossunder(b.c, zz.s_lpoint.last())
bool choch = na
string txt = na
if trend > 0
choch := true
txt := choch ? "CHoCH" : "BOS"
trend := -1
css := i_ms_dn_bos
if ((txt == "BOS" and boolean.get(s_bos) == true) or (txt == "CHoCH" and boolean.get(s_choch) == true))
f_line(zz.s_nBear.last(), zz.s_lpoint.last(), b.n, i_ms_dn_bos, txt, false, size.small, line.style_solid)
zz.s_nBear.clear ()
zz.s_lpoint.clear()
if ob_swings
swing_dn := true
[css, bear_ob, bull_ob, swing_up, swing_dn, trend]
[css, bear_ob_check, bull_ob_check, swing_up_check, swing_dn_check, trend] = pp.structure()
if show_mtb
upphl (trend)
dnphl (trend)
midphl( )
p_css = css
b_css = css
w_css = css
p_css := plotcandle_bool ? css : na
b_css := barcolor_bool ? css : na
w_css := plotcandle_bool ? color.rgb(120, 123, 134, 50) : na
hl () => [high, low ]
ohlc() => [close[1], open[1], high, low, high[2], low[2]]
[pdh, pdl] = request.security(syminfo.tickerid , 'D' , hl() , lookahead = barmerge.lookahead_on)
[pwh, pwl] = request.security(syminfo.tickerid , 'W' , hl() , lookahead = barmerge.lookahead_on)
[pmh, pml] = request.security(syminfo.tickerid , 'M' , hl() , lookahead = barmerge.lookahead_on)
[pyh, pyl] = request.security(syminfo.tickerid , '12M', hl() , lookahead = barmerge.lookahead_on)
f_fvg(fvg_tf, fvg_extend, bear_fvg_css, bull_fvg_css, fvg_src) =>
[c1, o1, h0, l0, h2, l2] = request.security(syminfo.tickerid, fvg_tf, ohlc())
bull_fvg = false
bear_fvg = false
delta = (c1 - o1) / o1 * 100
change_tf = timeframe.change(fvg_tf)
threshold = 0
bull_fvg := l0 > h2 and c1 > h2 and delta > threshold and change_tf
bear_fvg := h0 < l2 and c1 < l2 and -delta > threshold and change_tf
if bull_fvg
fvg.bull_max.unshift(
box.new(
b.n - 1
, l0
, b.n + fvg_extend
, math.avg(l0, h2)
, border_color = bull_fvg_css
, bgcolor = bull_fvg_css)
)
fvg.bull_min.unshift(
box.new(
b.n - 1
, math.avg(l0, h2)
, b.n + fvg_extend
, h2
, border_color = bull_fvg_css
, bgcolor = bull_fvg_css)
)
if bear_fvg
fvg.bear_max.unshift(
box.new(
b.n - 1
, h0
, b.n + fvg_extend
, math.avg(h0, l2)
, border_color = bear_fvg_css
, bgcolor = bear_fvg_css)
)
fvg.bear_min.unshift(
box.new(
b.n - 1
, l2
, b.n + fvg_extend
, math.avg(h0, l2)
, border_color = bear_fvg_css
, bgcolor = bear_fvg_css)
)
for bx in fvg.bull_min
if (fvg_src == "Wick" ? low : close) < box.get_bottom(bx)
bx.delete()
fvg.bull_max.get(array.indexof(fvg.bull_min, bx)).delete()
for bx in fvg.bear_min
if (fvg_src == "Wick" ? high : close) > box.get_top(bx)
bx.delete()
fvg.bear_max.get(array.indexof(fvg.bear_min, bx)).delete()
if fvg_enable
f_fvg(fvg_tf , fvg_extend, fvg_dncss, fvg_upcss, fvg_src)
if lvl_daily
mtfphl(pdh , pdl , 'D' , css_d, s_d)
if lvl_weekly
mtfphl(pmh , pml , 'W' , css_w, s_w)
if lvl_monthly
mtfphl(pwh , pwl, 'M' , css_m, s_m)
if lvl_yearly
mtfphl(pyh , pyl , '12M', css_y, s_y)
if ob_show
volblock(bull_ob_check , true , ob_mitigation , obv.dn_top.last() , obv.dn_bottom.last() , obv.dn_current_time.last() , time , ob_bull_css , obv.metric_up.last() , obv.metric_dn.last() , obv.dn_n.last() , obv.up_vol.last() , obv.dn_vol.last())
volblock(bear_ob_check , false , ob_mitigation , obv.up_top.last() , obv.up_bottom.last() , obv.up_current_time.last() , time , ob_bear_css , obv.metric_up.last() , obv.metric_dn.last() , obv.up_n.last() , obv.up_vol.last() , obv.dn_vol.last())
if ob_swings
volblock(swing_up_check , true , ob_mitigation , obv.dn_top.last() , obv.dn_bottom.last() , obv.dn_current_time.last() , time , css_swing_up , obv.metric_up.last() , obv.metric_dn.last() , obv.dn_n.last() , obv.up_vol.last() , obv.dn_vol.last())
volblock(swing_dn_check , false , ob_mitigation , obv.up_top.last() , obv.up_bottom.last() , obv.up_current_time.last() , time , css_swing_dn , obv.metric_up.last() , obv.metric_dn.last() , obv.up_n.last() , obv.up_vol.last() , obv.dn_vol.last())
// ---- Fixed price levels ----
if show_fixed_lines
prices_str = fixed_prices
prices_array = str.split(prices_str, ",")
for price_str in prices_array
price = str.tonumber(price_str)
if not na(price)
line.new(
x1 = bar_index - line_extend_bars, y1 = price,
x2 = bar_index + line_extend_bars, y2 = price,
xloc = xloc.bar_index, color = fixed_line_color, width = 1, style = line.style_solid)
// ----------------------------
// ---- Plot candles (Heikin‑Ashi or normal) - FIXED: moved to global scope ----
plot_open = show_heikin_ashi ? ha_open : open
plot_high = show_heikin_ashi ? ha_high : high
plot_low = show_heikin_ashi ? ha_low : low
plot_close = show_heikin_ashi ? ha_close : close
plot_color = show_heikin_ashi ? color.rgb(33,150,243) : p_css
plot_wick = show_heikin_ashi ? color.gray : w_css
plot_border = show_heikin_ashi ? color.rgb(33,150,243) : p_css
plotcandle(plot_open, plot_high, plot_low, plot_close,
color = plot_color, wickcolor = plot_wick, bordercolor = plot_border,
title = "Price Action Candles", // fixed constant title
editable = false)
barcolor(b_css, editable = false)
// © iss2k/StratifyTrade – modified to add lines, fixed levels & Heikin‑Ashi
//version=5
indicator("Price Action Concepts +", shorttitle = "PA Concepts+", overlay = true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)
//-----------------------------------------------------------------------------{
//Boolean set
//-----------------------------------------------------------------------------{
s_bos = 0
s_choch = 1
i_bos = 2
i_choch = 3
i_pp_choch = 4
green_candle = 5
red_candle = 6
boolean =
array.from(
false // s_bos
, false // s_choch
, false // i_bos
, false // i_choch
, false // i_pp_choch
, false // up
, false // dn
)
//-----------------------------------------------------------------------------{
// User inputs
//-----------------------------------------------------------------------------{
//
//
//==> Market Structure
show_swing_ms = input.string ("All" , "Swing " , inline = "1", group = "MARKET STRUCTURE" , options = ["All", "CHoCH", "BOS", "None"])
show_internal_ms = input.string ("All" , "Internal" , inline = "2", group = "MARKET STRUCTURE" , options = ["All", "CHoCH", "BOS", "CHoCH+", "None"])
internal_r_lookback = input.int (5 , "" , inline = "2", group = "MARKET STRUCTURE" , minval = 2)
swing_r_lookback = input.int (50 , "" , inline = "1", group = "MARKET STRUCTURE" , minval = 2)
plotcandle_bool = input.bool (false , "Plotcandle" , inline = "3", group = "MARKET STRUCTURE" , tooltip = "Plot a better coloring chart (require disable the current ticker)")
barcolor_bool = input.bool (false , "Bar Color" , inline = "4", group = "MARKET STRUCTURE" , tooltip = "Normal bar coloring")
internal_l_lookback = math.round (internal_r_lookback)
swing_l_lookback = (swing_r_lookback )
i_ms_up_bos = input.color (#42bda8 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_up_choch = input.color (#056656 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_dn_bos = input.color (#ff5252 , "" , inline = "2", group = "MARKET STRUCTURE")
i_ms_dn_choch = input.color (#b22833 , "" , inline = "2", group = "MARKET STRUCTURE")
s_ms_up_bos = input.color (#42bda8 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_up_choch = input.color (#056656 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_dn_bos = input.color (#ff5252 , "" , inline = "1", group = "MARKET STRUCTURE")
s_ms_dn_choch = input.color (#b22833 , "" , inline = "1", group = "MARKET STRUCTURE")
//
//
//==> PHL
lvl_daily = input.bool (false , "Day " , inline = "1", group = "HIGHS & LOWS MTF")
lvl_weekly = input.bool (false , "Week " , inline = "2", group = "HIGHS & LOWS MTF")
lvl_monthly = input.bool (false , "Month" , inline = "3", group = "HIGHS & LOWS MTF")
lvl_yearly = input.bool (false , "Year " , inline = "4", group = "HIGHS & LOWS MTF")
css_d = input.color (color.blue , "" , inline = "1", group = "HIGHS & LOWS MTF")
css_w = input.color (color.blue , "" , inline = "2", group = "HIGHS & LOWS MTF")
css_m = input.color (color.blue , "" , inline = "3", group = "HIGHS & LOWS MTF")
css_y = input.color (color.blue , "" , inline = "4", group = "HIGHS & LOWS MTF")
s_d = input.string ('⎯⎯⎯' , '' , inline = '1', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_w = input.string ('⎯⎯⎯' , '' , inline = '2', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_m = input.string ('⎯⎯⎯' , '' , inline = '3', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
s_y = input.string ('⎯⎯⎯' , '' , inline = '4', group = 'HIGHS & LOWS MTF' , options = ['⎯⎯⎯', '----', '····'])
//
//
//==> Volumetric Order Blocks
ob_show = input.bool (true , "Show Last " , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volumetric order blocks on the chart \n\n[Input] Ammount of volumetric order blocks to show")
ob_num = input.int (2 , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Orderblocks number", minval = 1, maxval = 10)
ob_metrics_show = input.bool (true , "Internal Buy/Sell Activity" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display volume metrics that have formed the orderblock")
css_metric_up = input.color (color.new(color.teal, 50) , " " , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
css_metric_dn = input.color (color.new(color.red , 50) , "" , inline = "2", group = "VOLUMETRIC ORDER BLOCKS")
ob_swings = input.bool (false , "Swing Order Blocks" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Display swing volumetric order blocks")
css_swing_up = input.color (color.new(#42bda8, 40) , " " , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
css_swing_dn = input.color (color.new(#ff5252, 40) , "" , inline = "a", group = "VOLUMETRIC ORDER BLOCKS")
ob_lookback_bool = input.bool (false , "Lenght " , inline = "3", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "[Enable] Use the lenght period to determine the lowest/highest point of the orderblock creation\n\n[Disable] Use the last opposite candle to determine the orderblock creation")
ob_filter = input.string ("None" , "Filtering " , inline = "d", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Filter out volumetric order blocks by BOS/CHoCH/CHoCH+", options = ["None", "BOS", "CHoCH", "CHoCH+"])
ob_looback = input.int (5 , "" , inline = "3", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Lookback to find lowest point for the construction of the volumetric order blocks")
ob_mitigation = input.string ("Absolute" , "Mitigation " , inline = "4", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Trigger to remove volumetric order blocks", options = ["Absolute", "Middle"])
ob_timeframe = input.timeframe ("" , "Timeframe " , inline = "5", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Timeframe of the volumetric order blocks")
use_grayscale = input.bool (true , "Grayscale" , inline = "6", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Use gray as basic order blocks color")
use_show_metric = input.bool (true , "Show Metrics" , inline = "7", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show volume associated with the orderblock and his relevance")
use_middle_line = input.bool (true , "Show Middle-Line" , inline = "8", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Show mid-line order blocks")
use_overlap = input.bool (true , "Hide Overlap" , inline = "9", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "Hide overlapping order blocks")
use_overlap_method = input.string ("Previous" , "Overlap Method " , inline = "Z", group = "VOLUMETRIC ORDER BLOCKS" , tooltip = "[Recent] Preserve the most recent volumetric order blocks\n\n[Previous] Preserve the previous volumetric order blocks", options = ["Recent", "Previous"])
ob_bull_css = input.color (color.new(color.teal, 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
ob_bear_css = input.color (color.new(color.red , 90) , "" , inline = "1", group = "VOLUMETRIC ORDER BLOCKS")
//
//
//==> Premium/Discount Zone
show_lbl = input.bool (false , "Show swing point" , inline = "1", group = "High and Low" , tooltip = "Display swing point")
show_mtb = input.bool (false , "Show High/Low/Equilibrium" , inline = "2", group = "High and Low" , tooltip = "Display Strong/Weak High And Low and Equilibrium")
toplvl = input.color (color.red , "Premium Zone " , inline = "3", group = "High and Low")
midlvl = input.color (color.white , "Equilibrium Zone" , inline = "4", group = "High and Low")
btmlvl = input.color (color.teal , "Discount Zone " , inline = "5", group = "High and Low")
//
//
//==> FVG
fvg_enable = input.bool (false , "FVG " , inline = "1", group = "FVG" , tooltip = "Display fair value gap")
fvg_upcss = input.color (color.new(color.aqua, 80) , "" , inline = "1", group = "FVG")
fvg_dncss = input.color (color.new(color.red , 80) , "" , inline = "1", group = "FVG")
fvg_extend = input.int (10 , "Extend FVG" , inline = "2", group = "FVG" , tooltip = "Extend FVG")
fvg_src = input.string ("Close" , "Mitigation " , inline = "3", group = "FVG" , tooltip = "[Close] Use the close of the body as trigger\n\n[Wick] Use the extreme point of the body as trigger", options = ["Close", "Wick"])
fvg_tf = input.timeframe ("" , "Timeframe " , inline = "4", group = "FVG" , tooltip = "Timeframe of the fair value gap")
//
//
//==> EXTRA SETTINGS (added for picture 15 look)
show_ob_lines = input.bool (true , "Show OB Lines (instead of boxes)", group = "EXTRA SETTINGS")
line_extend_bars = input.int (50 , "Line Extension (bars)" , group = "EXTRA SETTINGS")
show_fixed_lines = input.bool (false , "Show Fixed Price Levels" , group = "EXTRA SETTINGS")
fixed_prices = input.string ("4983.772,4982,4980,4978.74,4979,4975.276,4974.5,4972,4971,4969.024", "Fixed Prices (comma)", group = "EXTRA SETTINGS")
fixed_line_color = input.color (color.gray , "Fixed Line Color" , group = "EXTRA SETTINGS")
show_heikin_ashi = input.bool (false , "Overlay Heikin‑Ashi" , group = "EXTRA SETTINGS")
//
//
//==> Utility Colors
t = color.t (ob_bull_css)
invcol = color.new (color.white , 100)
switch use_grayscale
true => ob_bull_css := color.gray, ob_bear_css := color.gray
=> na
//-----------------------------------------------------------------------------{
// Switch market strcture visuals
//-----------------------------------------------------------------------------{
switch show_swing_ms
"All" => boolean.set(s_bos, true ), boolean.set(s_choch, true )
"CHoCH" => boolean.set(s_bos, false), boolean.set(s_choch, true )
"BOS" => boolean.set(s_bos, true ), boolean.set(s_choch, false)
"None" => boolean.set(s_bos, false), boolean.set(s_choch, false)
=> na
switch show_internal_ms
"All" => boolean.set(i_bos, true ), boolean.set(i_choch, true ), boolean.set(i_pp_choch, true )
"CHoCH" => boolean.set(i_bos, false), boolean.set(i_choch, true ), boolean.set(i_pp_choch, false)
"BOS" => boolean.set(i_bos, true ), boolean.set(i_choch, false), boolean.set(i_pp_choch, false)
"CHoCH+" => boolean.set(i_bos, false), boolean.set(i_choch, false), boolean.set(i_pp_choch, true )
"None" => boolean.set(i_bos, false), boolean.set(i_choch, false), boolean.set(i_pp_choch, false)
=> na
//-----------------------------------------------------------------------------{
// Custom Type (renamed from 'bar' to 'BarData')
//-----------------------------------------------------------------------------{
type BarData
float o = open
float c = close
float h = high
float l = low
float v = volume
int n = bar_index
int t = time
string xt = xloc.bar_time
string xn = xloc.bar_index // fixed typo: was yloc.bar_index
type bin
float [] i_hpoint
float [] i_lpoint
int [] i_nBull
int [] i_nBear
float [] s_hpoint
float [] s_lpoint
int [] s_nBull
int [] s_nBear
float [] up_ms_logs
float [] dn_ms_logs
string[] i_bulltxt
string[] i_beartxt
type ob
float[] up_vol
float[] up_top
float[] up_bottom
int [] up_current_time
int [] up_last_time
int [] up_n
float[] dn_vol
float[] dn_top
float[] dn_bottom
int [] dn_current_time
int [] dn_last_time
int [] dn_n
float[] metric_up
float[] metric_dn
type Zphl
line top
line bottom
label top_label
label bottom_label
bool stopcross
bool sbottomcross
bool itopcross
bool ibottomcross
string txtup
string txtdn
float topy
float bottomy
float topx
float bottomx
float tup
float tdn
int tupx
int tdnx
float itopy
float itopx
float ibottomy
float ibottomx
type tfvg
box[] bull_max
box[] bull_min
box[] bear_max
box[] bear_min
//-----------------------------------------------------------------------------{
// Type set
//-----------------------------------------------------------------------------{
BarData b = BarData.new() // updated to use BarData
// ---- Heikin‑Ashi calculation (if overlay is on) ----
var float ha_open = na
var float ha_close = na
float ha_high = na
float ha_low = na
if show_heikin_ashi
ha_close := (open + high + low + close) / 4
ha_open := na(ha_open[1]) ? (open + close) / 2 : (ha_open[1] + ha_close[1]) / 2
ha_high := math.max(high, ha_open, ha_close)
ha_low := math.min(low , ha_open, ha_close)
// ----------------------------------------------------
var pp = bin.new(
array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< string >(1, na)
, array.new< string >(1, na)
)
var obv = ob.new(
array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< int >(1, na)
, array.new< float >(1, na)
, array.new< float >(1, na)
)
var phl = Zphl.new(
na
, na
, label.new(na , na , color = invcol , textcolor = i_ms_dn_bos , style = label.style_label_down , size = size.tiny , text = "")
, label.new(na , na , color = invcol , textcolor = i_ms_up_bos , style = label.style_label_up , size = size.tiny , text = "")
, true
, true
, true
, true
, ""
, ""
, 0
, 0
, 0
, 0
, high
, low
, 0
, 0
, 0
, 0
, 0
, 0
)
var fvg = tfvg.new(
array.new<box>()
, array.new<box>()
, array.new<box>()
, array.new<box>()
)
var msline = array.new<line>(0)
// ---- Additional arrays for lines/labels (for OB lines) ----
var line[] ob_top_lines = array.new<line>()
var line[] ob_bottom_lines = array.new<line>()
var line[] ob_mid_lines = array.new<line>()
var label[] ob_labels = array.new<label>()
// ----------------------------------------------------------
obsec(float src) =>
id = request.security("", ob_timeframe, src)
id
lstyle(style) =>
out = switch style
'⎯⎯⎯' => line.style_solid
'----' => line.style_dashed
'····' => line.style_dotted
zhl(len)=>
upper = ta.highest(len)
lower = ta.lowest(len)
var float out = 0
out := b.h[len] > upper ? 0 : b.l[len] < lower ? 1 : out[1]
top = out == 0 and out[1] != 0 ? b.h[len] : 0
btm = out == 1 and out[1] != 1 ? b.l[len] : 0
[top, btm]
[top , btm ] = zhl(swing_r_lookback )
[itop, ibtm] = zhl(internal_r_lookback)
mtfphl(h, l ,tf ,css, pdhl_style) =>
var line hl = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var line ll = line.new(
na
, na
, na
, na
, xloc = xloc.bar_time
, color = css
, style = lstyle(pdhl_style)
)
var label lbl = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}L', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
var label hlb = label.new(
na
, na
, xloc = xloc.bar_time
, text = str.format('P{0}H', tf)
, color = invcol
, textcolor = css
, size = size.small
, style = label.style_label_left
)
hy = ta.valuewhen(h != h[1] , h , 1)
hx = ta.valuewhen(h == high , time , 1)
ly = ta.valuewhen(l != l[1] , l , 1)
lx = ta.valuewhen(l == low , time , 1)
if barstate.islast
extension = time + (time - time[1]) * 50 // difference in time
line.set_xy1(hl , hx , hy)
line.set_xy2(hl , extension , hy)
label.set_xy(hlb, extension , hy)
line.set_xy1(ll , lx , ly)
line.set_xy2(ll , extension , ly)
label.set_xy(lbl, extension , ly)
upphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
if top
phl.stopcross := true
phl.txtup := top > phl.topy ? "HH" : "LH"
if show_lbl
topl = label.new(
b.n - swing_r_lookback
, top
, phl.txtup
, color = invcol
, textcolor = toplvl
, style = label.style_label_down
, size = size.small
)
line.delete(phl.top[1])
phl.top := line.new(
b.n - swing_r_lookback
, top
, b.n
, top
, color = toplvl)
phl.topy := top
phl.topx := b.n - swing_r_lookback
phl.tup := top
phl.tupx := b.n - swing_r_lookback
if itop
phl.itopcross := true
phl.itopy := itop
phl.itopx := b.n - internal_r_lookback
phl.tup := math.max(high, phl.tup)
phl.tupx := phl.tup == high ? b.n : phl.tupx
if barstate.islast
line.set_xy1(
phl.top
, phl.tupx
, phl.tup
)
line.set_xy2(
phl.top
, b.n + 50
, phl.tup
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tup
)
dist = math.abs((phl.tup - close) / close) * 1000
label.set_text (lbl, trend < 0 ? "Strong High" + " (" + str.tostring(math.round(dist,0)) + "%)" : "Weak High" + " (" + str.tostring(math.round(dist,0)) + "%)")
dnphl(trend) =>
var label lbl = label.new(
na
, na
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
if btm
phl.sbottomcross := true
phl.txtdn := btm > phl.bottomy ? "HH" : "LH"
if show_lbl
btml = label.new(
b.n - swing_r_lookback
, btm, phl.txtdn
, color = invcol
, textcolor = btmlvl
, style = label.style_label_up
, size = size.small
)
line.delete(phl.bottom[1])
phl.bottom := line.new(
b.n - swing_r_lookback
, btm
, b.n
, btm
, color = btmlvl
)
phl.bottomy := btm
phl.bottomx := b.n - swing_r_lookback
phl.tdn := btm
phl.tdnx := b.n - swing_r_lookback
if ibtm
phl.ibottomcross := true
phl.ibottomy := ibtm
phl.ibottomx := b.n - internal_r_lookback
phl.tdn := math.min(low, phl.tdn)
phl.tdnx := phl.tdn == low ? b.n : phl.tdnx
if barstate.islast
line.set_xy1(
phl.bottom
, phl.tdnx
, phl.tdn
)
line.set_xy2(
phl.bottom
, b.n + 50
, phl.tdn
)
label.set_x(
lbl
, b.n + 50
)
label.set_y(
lbl
, phl.tdn
)
dist = math.abs((phl.tdn - close) / close) * 1000
label.set_text (lbl, trend > 0 ? "Strong Low" + " (" + str.tostring(math.round(dist,0)) + "%)" : "Weak Low" + " (" + str.tostring(math.round(dist,0)) + "%)")
midphl() =>
avg = math.avg(phl.bottom.get_y2(), phl.top.get_y2())
var line l = line.new(
y1 = avg
, y2 = avg
, x1 = b.n - swing_r_lookback
, x2 = b.n + 50
, color = midlvl
, style = line.style_solid
)
var label lbl = label.new(
x = b.n + 50
, y = avg
, text = "Equilibrium"
, style = label.style_label_left
, color = invcol
, textcolor = midlvl
, size = size.small
)
if barstate.islast
more = (phl.bottom.get_x1() + phl.bottom.get_x2()) > (phl.top.get_x1() + phl.top.get_x2()) ? phl.top.get_x1() : phl.bottom.get_x1()
line.set_xy1(l , more , avg)
line.set_xy2(l , b.n + 50, avg)
label.set_x (lbl , b.n + 50 )
label.set_y (lbl , avg )
dist = math.abs((l.get_y2() - close) / close) * 1000
label.set_text (lbl, "Equilibrium (" + str.tostring(math.round(dist,0)) + "%)")
vol() =>
float posVol = 0.0
float negVol = 0.0
switch
close > open => posVol += volume
close < open => negVol -= volume
close >= close[1] => posVol += volume
close < close[1] => negVol -= volume
[posVol, negVol]
[upvol, dnvol] = request.security("", ob_timeframe, vol())
volblock(bool condition, bool bull, string mitigation, float top, float bottom, int left, int right, color css, float upx, float dnx, int position, float upv, float dnv) =>
var ob_top = array.new< float >()
var ob_bottom = array.new< float >()
var ob_left = array.new< int >()
var ob_right = array.new< int >()
var ob_avg = array.new< float >()
var ob_vol = array.new< float >()
var redvol = array.new< float >()
var greenvol = array.new< float >()
var pos = array.new< int >()
var when = array.new< int >()
var countup = array.new< int >()
var countdn = array.new< int >()
var orderblock = array.new< box >()
var continuationorderblock = array.new< box >()
var uporderblock = array.new< box >()
var dnorderblock = array.new< box >()
var middlelvl = array.new< line >()
float sum = math.abs(ob_vol.sum())
if condition
paperlow = array.new< float >()
paperhigh = array.new< float >()
paperindex = array.new< int >()
paperlow .clear()
paperhigh.clear()
float avg = na
var bool isna = na
for i = 1 to ob_looback + 1
paperlow .push(low )
paperhigh .push(high)
paperindex.push(time)
if ob_lookback_bool == false
avg := math.avg(top, bottom)
ob_top .unshift(top )
ob_bottom.unshift(bottom)
ob_left .unshift(left )
if ob_lookback_bool
avg := bull ? math.avg(paperlow.min(), paperhigh.get(paperlow.indexof(paperlow.min()))) : math.avg(paperhigh.max(), paperlow.get(paperhigh.indexof(paperhigh.max())))
ob_top .unshift(bull ? paperhigh .get(paperlow.indexof(paperlow.min())) : paperhigh .max())
ob_bottom.unshift(bull ? paperlow .min() : paperlow .get(paperhigh.indexof(paperhigh.max())))
ob_left .unshift(bull ? paperindex.get(paperlow.indexof(paperlow.min())) : paperindex.get(paperhigh.indexof(paperhigh.max())))
ob_right .unshift(right )
ob_avg .unshift(avg )
ob_vol .unshift(bull ? dnv : upv)
redvol .unshift(dnx )
greenvol .unshift(upx )
pos .unshift(position )
when .unshift(1 )
countup .unshift(0 )
countdn .unshift(0 )
if use_overlap
int num = use_overlap_method == "Recent" ? 1 : 0
if ob_avg.size() > 1
if (bull ? ob_bottom.first() < ob_top.get(1) : ob_top.first() > ob_bottom.get(1))
ob_top .remove(num)
ob_bottom.remove(num)
ob_left .remove(num)
ob_right .remove(num)
ob_avg .remove(num)
ob_vol .remove(num)
redvol .remove(num)
greenvol .remove(num)
pos .remove(num)
when .remove(num)
countup .remove(num)
countdn .remove(num)
if barstate.isconfirmed
out = switch mitigation
"Middle" => ob_avg
"Absolute" => bull ? ob_bottom : ob_top
target = out
for stuff in target
idx = target.indexof(stuff)
if (bull ? close < stuff : close > stuff)
ob_top .remove(idx)
ob_bottom.remove(idx)
ob_left .remove(idx)
ob_right .remove(idx)
ob_avg .remove(idx)
ob_vol .remove(idx)
redvol .remove(idx)
greenvol .remove(idx)
pos .remove(idx)
when .remove(idx)
countup .remove(idx)
countdn .remove(idx)
if true
if barstate.isfirst
for i = 0 to ob_num - 1
orderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
continuationorderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.right, bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
uporderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
dnorderblock .unshift(box.new (na, na, na, na, xloc = xloc.bar_time, extend = extend.none , bgcolor = use_grayscale ? color.new(css,t) : css, border_color = color.new(color.white,100)))
middlelvl .unshift(line.new(na, na, na, na, xloc = xloc.bar_time, extend = extend.none , color = color.new(css,0), style = line.style_dashed, width = 1))
// ---- Also create placeholder line/label objects for the line style ----
array.unshift(ob_top_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_bottom_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_mid_lines, line.new(na, na, na, na, xloc = xloc.bar_index))
array.unshift(ob_labels, label.new(na, na, xloc = xloc.bar_index))
// ------------------------------------------------------------------------
if barstate.islast
if ob_top.size() > 0
for i = 0 to math.min(ob_num - 1, ob_top.size() - 1)
get_orderblock = orderblock .get(i)
get_continuationorderblock = continuationorderblock.get(i)
get_uporderblock = uporderblock .get(i)
get_dnorderblock = dnorderblock .get(i)
get_middlelvl = middlelvl .get(i)
// MAIN ORDERBLOCK
box.set_top (get_orderblock, ob_top .get(i) )
box.set_bottom(get_orderblock, ob_bottom .get(i) )
box.set_left (get_orderblock, ob_left .get(i) )
box.set_right (get_orderblock, time )
// ---- CONTINUATION: either boxes (default) or lines (if show_ob_lines) ----
if show_ob_lines
// Delete old continuation box (not needed)
box.delete(get_continuationorderblock)
// Draw lines instead
right_index = bar_index + line_extend_bars
// Top line
line.set_xy1(array.get(ob_top_lines, i), ob_left.get(i), ob_top.get(i))
line.set_xy2(array.get(ob_top_lines, i), right_index , ob_top.get(i))
line.set_color(array.get(ob_top_lines, i), css)
line.set_width(array.get(ob_top_lines, i), 1)
line.set_style(array.get(ob_top_lines, i), line.style_solid)
// Bottom line
line.set_xy1(array.get(ob_bottom_lines, i), ob_left.get(i), ob_bottom.get(i))
line.set_xy2(array.get(ob_bottom_lines, i), right_index , ob_bottom.get(i))
line.set_color(array.get(ob_bottom_lines, i), css)
line.set_width(array.get(ob_bottom_lines, i), 1)
line.set_style(array.get(ob_bottom_lines, i), line.style_solid)
// Middle line (optional)
if use_middle_line
line.set_xy1(array.get(ob_mid_lines, i), ob_left.get(i), ob_avg.get(i))
line.set_xy2(array.get(ob_mid_lines, i), right_index , ob_avg.get(i))
line.set_color(array.get(ob_mid_lines, i), color.new(css,70))
line.set_width(array.get(ob_mid_lines, i), 1)
line.set_style(array.get(ob_mid_lines, i), line.style_dashed)
else
line.delete(array.get(ob_mid_lines, i))
// Volume label
if use_show_metric
vol_text = str.tostring(math.round(ob_vol.get(i) / 1000, 3)) + "K"
// Calculate percentage relative to the total volume of all displayed OBs
total_vol = 0.0
for j=0 to math.min(ob_num-1, ob_vol.size()-1)
total_vol += math.abs(ob_vol.get(j))
perc = math.round(ob_vol.get(i) / total_vol * 100, 1)
vol_text += " (" + str.tostring(perc) + "%)"
label.set_x(array.get(ob_labels, i), right_index)
label.set_y(array.get(ob_labels, i), ob_avg.get(i))
label.set_text(array.get(ob_labels, i), vol_text)
label.set_color(array.get(ob_labels, i), color.new(color.white,100))
label.set_textcolor(array.get(ob_labels, i), css)
label.set_style(array.get(ob_labels, i), label.style_label_left)
label.set_size(array.get(ob_labels, i), size.small)
else
label.delete(array.get(ob_labels, i))
else
// Use original continuation box
box.set_top (get_continuationorderblock, ob_top .get(i) )
box.set_bottom(get_continuationorderblock, ob_bottom.get(i) )
box.set_left (get_continuationorderblock, time )
box.set_right (get_continuationorderblock, time + (time - time[1]) * 100)
// Delete any lines from previous line mode
line.delete(array.get(ob_top_lines, i))
line.delete(array.get(ob_bottom_lines, i))
line.delete(array.get(ob_mid_lines, i))
label.delete(array.get(ob_labels, i))
// -------------------------------------------------------------------------
// MIDDLE LEVEL (inside main box) – keep as before
if use_middle_line and not show_ob_lines // only show inside box if not using lines? can keep both, but we already drew mid line above if lines enabled.
line.set_x1(get_middlelvl, ob_left .get(i))
line.set_x2(get_middlelvl, time )
line.set_y1(get_middlelvl, ob_avg .get(i))
line.set_y2(get_middlelvl, ob_avg .get(i))
if ob_metrics_show
box.set_top (get_uporderblock , ob_top .get(i))
box.set_bottom(get_uporderblock , ob_avg .get(i))
box.set_left (get_uporderblock , ob_left .get(i))
box.set_right (get_uporderblock , ob_left .get(i))
box.set_bgcolor(get_uporderblock , css_metric_up )
box.set_top (get_dnorderblock, ob_avg .get(i))
box.set_bottom(get_dnorderblock, ob_bottom .get(i))
box.set_left (get_dnorderblock, ob_left .get(i))
box.set_right (get_dnorderblock, ob_left .get(i))
box.set_bgcolor(get_dnorderblock, css_metric_dn )
takeup = box.get_right(get_uporderblock)
takedn = box.get_right(get_dnorderblock)
box.set_right (get_dnorderblock, takedn + (time - time[1]) * countdn.get(i))
box.set_right (get_uporderblock, takeup + (time - time[1]) * countup.get(i))
if use_show_metric and not show_ob_lines // only show text inside continuation box if not using lines
box.set_text (get_continuationorderblock, str.tostring(math.round(ob_vol.get(i) / 1000,3)) + "K" + " (" + str.tostring( math.abs(math.round((ob_vol.get(i) - (i == 0 ? sum : ob_vol.get(0)) ) / (i == 0 ? sum : ob_vol.get(0)) ,3)) * 10) + "%)")
box.set_text_size (get_continuationorderblock, size.auto)
box.set_text_halign(get_continuationorderblock, text.align_left)
box.set_text_color (get_continuationorderblock, color.new(css,0))
if ob_metrics_show
if barstate.isconfirmed and when.size() > 0
for i = 0 to countup.size() - 1
if redvol.get(i) < greenvol.get(i)
switch when.get(i)
1 => countup.set(i, countup.get(i) + 1), when.set(i, 2)
2 => countup.set(i, countup.get(i) + 1), when.set(i, 3)
3 => countdn.set(i, countdn.get(i) + 1), when.set(i, 1)
if redvol.get(i) > greenvol.get(i)
switch when.get(i)
1 => countdn.set(i, countdn.get(i) + 1), when.set(i, 2)
2 => countdn.set(i, countdn.get(i) + 1), when.set(i, 3)
3 => countup.set(i, countup.get(i) + 1), when.set(i, 1)
f_line(x, y, z, css, txt, down, size, style) =>
var line id = na
var label lbl = na
id := line.new(
x
, y
, z
, y
, xloc = b.xn
, color = css
, width = 1
, style = style
)
if msline.size() == 450 // Limit to 450 to avoid issue with other drawing object
line.delete(msline.shift())
msline.push(id)
lbl := label.new(
int(math.avg(x, z))
, y
, txt
, color = invcol
, textcolor = css
, style = down ? label.style_label_down : label.style_label_up
, size = size
)
//-----------------------------------------------------------------------------{
// Market strcture pivot point
//-----------------------------------------------------------------------------{
switch
b.c > b.o => boolean.set(green_candle, true)
b.c < b.o => boolean.set(red_candle , true)
_v = request.security("", ob_timeframe, volume)
switch
boolean.get(green_candle) => obv.up_bottom.push(obsec(low)), obv.up_top.push(obsec(high)), obv.up_vol.push(math.abs(_v)), obv.up_current_time.push(time), obv.up_last_time.push(time[1]), obv.up_n.push(bar_index), obv.metric_up.push(upvol), obv.metric_dn.push(math.abs(dnvol))
boolean.get(red_candle) => obv.dn_bottom.push(obsec(low)), obv.dn_top.push(obsec(high)), obv.dn_vol.push(math.abs(_v)), obv.dn_current_time.push(time), obv.dn_last_time.push(time[1]), obv.dn_n.push(bar_index), obv.metric_up.push(upvol), obv.metric_dn.push(math.abs(dnvol))
=> na
ph_i = ta.pivothigh(b.h , internal_l_lookback , internal_r_lookback)
pl_i = ta.pivotlow (b.l , internal_l_lookback , internal_r_lookback)
ph_s = ta.pivothigh(b.h , swing_l_lookback , swing_r_lookback )
pl_s = ta.pivotlow (b.l , swing_l_lookback , swing_r_lookback )
switch
ph_i => pp.i_hpoint.clear(), pp.i_nBull.clear(), pp.i_hpoint.push(b.h[internal_r_lookback]), pp.i_nBull.push(b.n[internal_r_lookback]), pp.up_ms_logs.push(b.h[internal_r_lookback])
pl_i => pp.i_lpoint.clear(), pp.i_nBear.clear(), pp.i_lpoint.push(b.l[internal_r_lookback]), pp.i_nBear.push(b.n[internal_r_lookback]), pp.dn_ms_logs.push(b.l[internal_r_lookback])
ph_s => pp.s_hpoint.clear(), pp.s_nBull.clear(), pp.s_hpoint.push(b.h[swing_r_lookback]) , pp.s_nBull.push(b.n[swing_r_lookback] )
pl_s => pp.s_lpoint.clear(), pp.s_nBear.clear(), pp.s_lpoint.push(b.l[swing_r_lookback]) , pp.s_nBear.push(b.n[swing_r_lookback] )
=> na
//-----------------------------------------------------------------------------{
// strcture set
//-----------------------------------------------------------------------------{
method structure(bin zz) =>
var color css = na
var int count = 0
var int trend = 0
var int itrend = 0
var bool ob_bear = false
bool bear_ob = false
bool bull_ob = false
bool swing_up = false
bool swing_dn = false
if true
//-----------------------------------------------------------------------------{
//Internal structure bullish
//-----------------------------------------------------------------------------{
if zz.dn_ms_logs.size() > 1 and zz.i_hpoint.size() > 0 and zz.i_nBull.size() > 0
if ta.crossover(b.c, zz.i_hpoint.last())
bool choch = na
string txt = na
if itrend < 0
choch := true
switch
choch and not (zz.dn_ms_logs.last() > zz.dn_ms_logs.get(zz.dn_ms_logs.indexof(zz.dn_ms_logs.last()) - 1)) => txt := "CHoCH"
choch and (zz.dn_ms_logs.last() > zz.dn_ms_logs.get(zz.dn_ms_logs.indexof(zz.dn_ms_logs.last()) - 1)) => txt := "CHoCH+"
not choch => txt := "BOS"
itrend := 1
switch txt
"CHoCH" => css := i_ms_up_choch
"BOS" => css := i_ms_up_bos
"CHoCH+" => css := i_ms_up_choch
=> css
if ((txt == "BOS" and boolean.get(i_bos) == true) or (txt == "CHoCH" and boolean.get(i_choch) == true) or (txt =="CHoCH+" and boolean.get(i_pp_choch) == true))
f_line(zz.i_nBull.last(), zz.i_hpoint.last(), b.n, i_ms_up_bos, txt, true, size.small, line.style_dashed)
switch
ob_filter == "None" => bull_ob := true
ob_filter == "BOS" and txt == "BOS" => bull_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bull_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bull_ob := true
=> bull_ob := false
zz.i_nBull.clear ()
zz.i_hpoint.clear()
zz.i_bulltxt.push(txt)
//-----------------------------------------------------------------------------{
//Internal structure bearish
//-----------------------------------------------------------------------------{
if zz.up_ms_logs.size() > 1 and zz.i_lpoint.size() > 0 and zz.i_nBear.size() > 0
if ta.crossunder(b.c, zz.i_lpoint.last())
bool choch = na
string txt = na
if itrend > 0
choch := true
switch
choch and not (zz.up_ms_logs.last() < zz.up_ms_logs.get(zz.up_ms_logs.indexof(zz.up_ms_logs.last()) - 1)) => txt := "CHoCH"
choch and (zz.up_ms_logs.last() < zz.up_ms_logs.get(zz.up_ms_logs.indexof(zz.up_ms_logs.last()) - 1)) => txt := "CHoCH+"
not choch => txt := "BOS"
itrend := -1
switch txt
"CHoCH" => css := i_ms_dn_choch
"BOS" => css := i_ms_dn_bos
"CHoCH+" => css := i_ms_dn_choch
=> css
if ((txt == "BOS" and boolean.get(i_bos) == true) or (txt == "CHoCH" and boolean.get(i_choch) == true) or (txt =="CHoCH+" and boolean.get(i_pp_choch) == true))
f_line(zz.i_nBear.last(), zz.i_lpoint.last(), b.n, i_ms_dn_bos, txt, false, size.small, line.style_dashed)
switch
ob_filter == "None" => bear_ob := true
ob_filter == "BOS" and txt == "BOS" => bear_ob := true
ob_filter == "CHoCH" and txt == "CHoCH" => bear_ob := true
ob_filter == "CHoCH+" and txt == "CHoCH+" => bear_ob := true
=> bear_ob := false
zz.i_nBear.clear ()
zz.i_lpoint.clear()
zz.i_beartxt.push(txt)
//-----------------------------------------------------------------------------{
//Swing structure bullish
//-----------------------------------------------------------------------------{
if zz.s_hpoint.size() > 0
if ta.crossover(b.c, zz.s_hpoint.last())
bool choch = na
string txt = na
if trend < 0
choch := true
txt := choch ? "CHoCH" : "BOS"
trend := 1
css := i_ms_up_bos
if ((txt == "BOS" and boolean.get(s_bos) == true) or (txt == "CHoCH" and boolean.get(s_choch) == true))
f_line(zz.s_nBull.last(), zz.s_hpoint.last(), b.n, i_ms_up_bos, txt, true, size.small, line.style_solid)
zz.s_nBull.clear ()
zz.s_hpoint.clear()
if ob_swings
swing_up := true
//-----------------------------------------------------------------------------{
//Swing structure bearish
//-----------------------------------------------------------------------------{
if zz.s_lpoint.size() > 0
if ta.crossunder(b.c, zz.s_lpoint.last())
bool choch = na
string txt = na
if trend > 0
choch := true
txt := choch ? "CHoCH" : "BOS"
trend := -1
css := i_ms_dn_bos
if ((txt == "BOS" and boolean.get(s_bos) == true) or (txt == "CHoCH" and boolean.get(s_choch) == true))
f_line(zz.s_nBear.last(), zz.s_lpoint.last(), b.n, i_ms_dn_bos, txt, false, size.small, line.style_solid)
zz.s_nBear.clear ()
zz.s_lpoint.clear()
if ob_swings
swing_dn := true
[css, bear_ob, bull_ob, swing_up, swing_dn, trend]
[css, bear_ob_check, bull_ob_check, swing_up_check, swing_dn_check, trend] = pp.structure()
if show_mtb
upphl (trend)
dnphl (trend)
midphl( )
p_css = css
b_css = css
w_css = css
p_css := plotcandle_bool ? css : na
b_css := barcolor_bool ? css : na
w_css := plotcandle_bool ? color.rgb(120, 123, 134, 50) : na
hl () => [high, low ]
ohlc() => [close[1], open[1], high, low, high[2], low[2]]
[pdh, pdl] = request.security(syminfo.tickerid , 'D' , hl() , lookahead = barmerge.lookahead_on)
[pwh, pwl] = request.security(syminfo.tickerid , 'W' , hl() , lookahead = barmerge.lookahead_on)
[pmh, pml] = request.security(syminfo.tickerid , 'M' , hl() , lookahead = barmerge.lookahead_on)
[pyh, pyl] = request.security(syminfo.tickerid , '12M', hl() , lookahead = barmerge.lookahead_on)
f_fvg(fvg_tf, fvg_extend, bear_fvg_css, bull_fvg_css, fvg_src) =>
[c1, o1, h0, l0, h2, l2] = request.security(syminfo.tickerid, fvg_tf, ohlc())
bull_fvg = false
bear_fvg = false
delta = (c1 - o1) / o1 * 100
change_tf = timeframe.change(fvg_tf)
threshold = 0
bull_fvg := l0 > h2 and c1 > h2 and delta > threshold and change_tf
bear_fvg := h0 < l2 and c1 < l2 and -delta > threshold and change_tf
if bull_fvg
fvg.bull_max.unshift(
box.new(
b.n - 1
, l0
, b.n + fvg_extend
, math.avg(l0, h2)
, border_color = bull_fvg_css
, bgcolor = bull_fvg_css)
)
fvg.bull_min.unshift(
box.new(
b.n - 1
, math.avg(l0, h2)
, b.n + fvg_extend
, h2
, border_color = bull_fvg_css
, bgcolor = bull_fvg_css)
)
if bear_fvg
fvg.bear_max.unshift(
box.new(
b.n - 1
, h0
, b.n + fvg_extend
, math.avg(h0, l2)
, border_color = bear_fvg_css
, bgcolor = bear_fvg_css)
)
fvg.bear_min.unshift(
box.new(
b.n - 1
, l2
, b.n + fvg_extend
, math.avg(h0, l2)
, border_color = bear_fvg_css
, bgcolor = bear_fvg_css)
)
for bx in fvg.bull_min
if (fvg_src == "Wick" ? low : close) < box.get_bottom(bx)
bx.delete()
fvg.bull_max.get(array.indexof(fvg.bull_min, bx)).delete()
for bx in fvg.bear_min
if (fvg_src == "Wick" ? high : close) > box.get_top(bx)
bx.delete()
fvg.bear_max.get(array.indexof(fvg.bear_min, bx)).delete()
if fvg_enable
f_fvg(fvg_tf , fvg_extend, fvg_dncss, fvg_upcss, fvg_src)
if lvl_daily
mtfphl(pdh , pdl , 'D' , css_d, s_d)
if lvl_weekly
mtfphl(pmh , pml , 'W' , css_w, s_w)
if lvl_monthly
mtfphl(pwh , pwl, 'M' , css_m, s_m)
if lvl_yearly
mtfphl(pyh , pyl , '12M', css_y, s_y)
if ob_show
volblock(bull_ob_check , true , ob_mitigation , obv.dn_top.last() , obv.dn_bottom.last() , obv.dn_current_time.last() , time , ob_bull_css , obv.metric_up.last() , obv.metric_dn.last() , obv.dn_n.last() , obv.up_vol.last() , obv.dn_vol.last())
volblock(bear_ob_check , false , ob_mitigation , obv.up_top.last() , obv.up_bottom.last() , obv.up_current_time.last() , time , ob_bear_css , obv.metric_up.last() , obv.metric_dn.last() , obv.up_n.last() , obv.up_vol.last() , obv.dn_vol.last())
if ob_swings
volblock(swing_up_check , true , ob_mitigation , obv.dn_top.last() , obv.dn_bottom.last() , obv.dn_current_time.last() , time , css_swing_up , obv.metric_up.last() , obv.metric_dn.last() , obv.dn_n.last() , obv.up_vol.last() , obv.dn_vol.last())
volblock(swing_dn_check , false , ob_mitigation , obv.up_top.last() , obv.up_bottom.last() , obv.up_current_time.last() , time , css_swing_dn , obv.metric_up.last() , obv.metric_dn.last() , obv.up_n.last() , obv.up_vol.last() , obv.dn_vol.last())
// ---- Fixed price levels ----
if show_fixed_lines
prices_str = fixed_prices
prices_array = str.split(prices_str, ",")
for price_str in prices_array
price = str.tonumber(price_str)
if not na(price)
line.new(
x1 = bar_index - line_extend_bars, y1 = price,
x2 = bar_index + line_extend_bars, y2 = price,
xloc = xloc.bar_index, color = fixed_line_color, width = 1, style = line.style_solid)
// ----------------------------
// ---- Plot candles (Heikin‑Ashi or normal) - FIXED: moved to global scope ----
plot_open = show_heikin_ashi ? ha_open : open
plot_high = show_heikin_ashi ? ha_high : high
plot_low = show_heikin_ashi ? ha_low : low
plot_close = show_heikin_ashi ? ha_close : close
plot_color = show_heikin_ashi ? color.rgb(33,150,243) : p_css
plot_wick = show_heikin_ashi ? color.gray : w_css
plot_border = show_heikin_ashi ? color.rgb(33,150,243) : p_css
plotcandle(plot_open, plot_high, plot_low, plot_close,
color = plot_color, wickcolor = plot_wick, bordercolor = plot_border,
title = "Price Action Candles", // fixed constant title
editable = false)
barcolor(b_css, editable = false)
开源脚本
秉承TradingView的精神,该脚本的作者将其开源,以便交易者可以查看和验证其功能。向作者致敬!您可以免费使用该脚本,但请记住,重新发布代码须遵守我们的网站规则。
免责声明
这些信息和出版物并非旨在提供,也不构成TradingView提供或认可的任何形式的财务、投资、交易或其他类型的建议或推荐。请阅读使用条款了解更多信息。
开源脚本
秉承TradingView的精神,该脚本的作者将其开源,以便交易者可以查看和验证其功能。向作者致敬!您可以免费使用该脚本,但请记住,重新发布代码须遵守我们的网站规则。
免责声明
这些信息和出版物并非旨在提供,也不构成TradingView提供或认可的任何形式的财务、投资、交易或其他类型的建议或推荐。请阅读使用条款了解更多信息。