Индикатор

MNQ MTF Dashboard [RSI/ADX/Stoch/ATR]A compact multi-timeframe table that reads four core indicators across five timeframes at once, so you can gauge trend, momentum, direction, and volatility without flipping charts.
For the chart's current symbol, it displays 5m, 15m, 1H, 4H, and 1D in a single color-coded panel:
ADX (14) — trend strength. Green above 25 (trending), orange 20–25 (borderline), gray below 20 (no trend/chop). ADX measures strength only, not direction.
RSI (14) — directional bias. Green above 50 (bullish lean), red below (bearish lean).
Stochastic (14,3,3) — momentum. Colored by %K vs %D and level; shows whether short-term momentum is rising or fading, and when it's stretched.
ATR (14) — volatility, in points. Read each timeframe relative to its own norm; useful for stop-sizing and spotting volatility expansion.
Bias — a quick synthesis cell combining RSI and Stochastic into Bull / Bear / Mixed. Индикатор

2.0- Killzones EUR/GBP - Manuel De Jesus Leiva// This source code is subject to the terms of the Mozilla Public License 2.0 at mozilla.org
// © tradeforopp
//@version=6
indicator("Killzones EUR/GBP - Manuel De Jesus Leiva", "Killzones EUR/GBP - Manuel De Jesus Leiva", true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500)
// ---------------------------------------- Constant Functions --------------------------------------------------
get_line_type(_style) =>
switch _style
"Solid" => line.style_solid
"Dotted" => line.style_dotted
"Dashed" => line.style_dashed
get_size(x) =>
switch x
"Auto" => size.auto
"Tiny" => size.tiny
"Small" => size.small
"Normal" => size.normal
"Large" => size.large
"Huge" => size.huge
get_table_pos(pos) =>
switch pos
"Bottom Center" => position.bottom_center
"Bottom Left" => position.bottom_left
"Bottom Right" => position.bottom_right
"Middle Center" => position.middle_center
"Middle Left" => position.middle_left
"Middle Right" => position.middle_right
"Top Center" => position.top_center
"Top Left" => position.top_left
"Top Right" => position.top_right
// ---------------------------------------- Constant Functions --------------------------------------------------
// ---------------------------------------- Inputs --------------------------------------------------
var g_SETTINGS = "Settings"
max_days = input.int(1000, "Session Drawing Limit", 1, tooltip = "Only this many drawings will be kept on the chart, for each selected drawing type (killzone boxes, pivot lines, open lines, etc.). Set to 200 for ~6 months of sessions.", group = g_SETTINGS)
tf_limit = input.timeframe("60", "Timeframe Limit", tooltip = "Drawings will not appear on timeframes greater than or equal to this", group = g_SETTINGS)
gmt_tz = input.string('America/New_York', "Timezone", options = , tooltip = "Note GMT is not adjusted to reflect Daylight Saving Time changes", group = g_SETTINGS)
lbl_size = get_size(input.string('Normal', "Label Size", options = , tooltip = "The size of all labels", group = g_SETTINGS))
txt_color = input.color(color.black, "Text Color", tooltip = "The color of all label and table text", group = g_SETTINGS)
use_cutoff = input.bool(false, "Drawing Cutoff Time", inline = "CO", tooltip = "When enabled, all pivots and open price lines will stop extending at this time", group = g_SETTINGS)
cutoff = input.session("1800-1801", "", inline = "CO", group = g_SETTINGS)
var tf_limit_is_equal_or_more_chart_tf = timeframe.in_seconds('') <= timeframe.in_seconds(tf_limit)
var g_KZ = "Killzones"
// General Killzone Settings
show_kz = input.bool(true, "Show Killzone Boxes", inline = "KZ", group = g_KZ)
show_kz_text = input.bool(false, "Display Text", inline = "KZ", group = g_KZ)
box_transparency = input.int(70, "Box Transparency", 0, 100, group = g_KZ)
text_transparency = input.int(50, "Text Transparency", 0, 100, group = g_KZ)
// ==================== KILLZONE 01 ====================
use_kz01 = input.bool(true, "00:00-01:00", inline = "KZ01", group = g_KZ)
kz01_txt = input.string("00-01", "", inline = "KZ01", group = g_KZ)
kz01 = input.session("0000-0100", "", inline = "KZ01", group = g_KZ)
kz01_color = input.color(color.blue, "", inline = "KZ01", group = g_KZ)
// ==================== KILLZONE 02 ====================
use_kz02 = input.bool(true, "01:00-02:00", inline = "KZ02", group = g_KZ)
kz02_txt = input.string("01-02", "", inline = "KZ02", group = g_KZ)
kz02 = input.session("0100-0200", "", inline = "KZ02", group = g_KZ)
kz02_color = input.color(color.blue, "", inline = "KZ02", group = g_KZ)
// ==================== KILLZONE 03 ====================
use_kz03 = input.bool(true, "02:00-03:00", inline = "KZ03", group = g_KZ)
kz03_txt = input.string("02-03", "", inline = "KZ03", group = g_KZ)
kz03 = input.session("0200-0300", "", inline = "KZ03", group = g_KZ)
kz03_color = input.color(color.blue, "", inline = "KZ03", group = g_KZ)
// ==================== KILLZONE 04 ====================
use_kz04 = input.bool(true, "03:00-04:00", inline = "KZ04", group = g_KZ)
kz04_txt = input.string("03-04", "", inline = "KZ04", group = g_KZ)
kz04 = input.session("0300-0400", "", inline = "KZ04", group = g_KZ)
kz04_color = input.color(color.blue, "", inline = "KZ04", group = g_KZ)
// ==================== KILLZONE 05 ====================
use_kz05 = input.bool(true, "04:00-05:00", inline = "KZ05", group = g_KZ)
kz05_txt = input.string("04-05", "", inline = "KZ05", group = g_KZ)
kz05 = input.session("0400-0500", "", inline = "KZ05", group = g_KZ)
kz05_color = input.color(color.blue, "", inline = "KZ05", group = g_KZ)
// ==================== KILLZONE 06 ====================
use_kz06 = input.bool(true, "05:00-06:00", inline = "KZ06", group = g_KZ)
kz06_txt = input.string("05-06", "", inline = "KZ06", group = g_KZ)
kz06 = input.session("0500-0600", "", inline = "KZ06", group = g_KZ)
kz06_color = input.color(color.blue, "", inline = "KZ06", group = g_KZ)
// ==================== KILLZONE 07 ====================
use_kz07 = input.bool(true, "06:00-07:00", inline = "KZ07", group = g_KZ)
kz07_txt = input.string("06-07", "", inline = "KZ07", group = g_KZ)
kz07 = input.session("0600-0700", "", inline = "KZ07", group = g_KZ)
kz07_color = input.color(color.blue, "", inline = "KZ07", group = g_KZ)
// ==================== KILLZONE 08 ====================
use_kz08 = input.bool(true, "07:00-08:00", inline = "KZ08", group = g_KZ)
kz08_txt = input.string("07-08", "", inline = "KZ08", group = g_KZ)
kz08 = input.session("0700-0800", "", inline = "KZ08", group = g_KZ)
kz08_color = input.color(color.blue, "", inline = "KZ08", group = g_KZ)
// ==================== KILLZONE 09 ====================
use_kz09 = input.bool(true, "08:00-09:00", inline = "KZ09", group = g_KZ)
kz09_txt = input.string("08-09", "", inline = "KZ09", group = g_KZ)
kz09 = input.session("0800-0900", "", inline = "KZ09", group = g_KZ)
kz09_color = input.color(color.blue, "", inline = "KZ09", group = g_KZ)
// ==================== KILLZONE 10 ====================
use_kz10 = input.bool(true, "09:00-10:00", inline = "KZ10", group = g_KZ)
kz10_txt = input.string("09-10", "", inline = "KZ10", group = g_KZ)
kz10 = input.session("0900-1000", "", inline = "KZ10", group = g_KZ)
kz10_color = input.color(color.blue, "", inline = "KZ10", group = g_KZ)
// ==================== KILLZONE 11 ====================
use_kz11 = input.bool(true, "10:00-11:00", inline = "KZ11", group = g_KZ)
kz11_txt = input.string("10-11", "", inline = "KZ11", group = g_KZ)
kz11 = input.session("1000-1100", "", inline = "KZ11", group = g_KZ)
kz11_color = input.color(color.blue, "", inline = "KZ11", group = g_KZ)
// ==================== KILLZONE 12 ====================
use_kz12 = input.bool(true, "11:00-12:00", inline = "KZ12", group = g_KZ)
kz12_txt = input.string("11-12", "", inline = "KZ12", group = g_KZ)
kz12 = input.session("1100-1200", "", inline = "KZ12", group = g_KZ)
kz12_color = input.color(color.blue, "", inline = "KZ12", group = g_KZ)
// ==================== KILLZONE 13 ====================
use_kz13 = input.bool(true, "12:00-13:00", inline = "KZ13", group = g_KZ)
kz13_txt = input.string("12-13", "", inline = "KZ13", group = g_KZ)
kz13 = input.session("1200-1300", "", inline = "KZ13", group = g_KZ)
kz13_color = input.color(color.blue, "", inline = "KZ13", group = g_KZ)
// ==================== KILLZONE 14 ====================
use_kz14 = input.bool(true, "13:00-14:00", inline = "KZ14", group = g_KZ)
kz14_txt = input.string("13-14", "", inline = "KZ14", group = g_KZ)
kz14 = input.session("1300-1400", "", inline = "KZ14", group = g_KZ)
kz14_color = input.color(color.blue, "", inline = "KZ14", group = g_KZ)
// ==================== KILLZONE 15 ====================
use_kz15 = input.bool(true, "14:00-15:00", inline = "KZ15", group = g_KZ)
kz15_txt = input.string("14-15", "", inline = "KZ15", group = g_KZ)
kz15 = input.session("1400-1500", "", inline = "KZ15", group = g_KZ)
kz15_color = input.color(color.blue, "", inline = "KZ15", group = g_KZ)
// ==================== KILLZONE 16 ====================
use_kz16 = input.bool(true, "15:00-16:00", inline = "KZ16", group = g_KZ)
kz16_txt = input.string("15-16", "", inline = "KZ16", group = g_KZ)
kz16 = input.session("1500-1600", "", inline = "KZ16", group = g_KZ)
kz16_color = input.color(color.blue, "", inline = "KZ16", group = g_KZ)
// ==================== KILLZONE 17 ====================
use_kz17 = input.bool(true, "16:00-17:00", inline = "KZ17", group = g_KZ)
kz17_txt = input.string("16-17", "", inline = "KZ17", group = g_KZ)
kz17 = input.session("1600-1700", "", inline = "KZ17", group = g_KZ)
kz17_color = input.color(color.blue, "", inline = "KZ17", group = g_KZ)
// ==================== KILLZONE 18 ====================
use_kz18 = input.bool(true, "17:00-18:00", inline = "KZ18", group = g_KZ)
kz18_txt = input.string("17-18", "", inline = "KZ18", group = g_KZ)
kz18 = input.session("1700-1800", "", inline = "KZ18", group = g_KZ)
kz18_color = input.color(color.blue, "", inline = "KZ18", group = g_KZ)
// ==================== KILLZONE 19 ====================
use_kz19 = input.bool(true, "18:00-19:00", inline = "KZ19", group = g_KZ)
kz19_txt = input.string("18-19", "", inline = "KZ19", group = g_KZ)
kz19 = input.session("1800-1900", "", inline = "KZ19", group = g_KZ)
kz19_color = input.color(color.blue, "", inline = "KZ19", group = g_KZ)
// ==================== KILLZONE 20 ====================
use_kz20 = input.bool(true, "19:00-20:00", inline = "KZ20", group = g_KZ)
kz20_txt = input.string("19-20", "", inline = "KZ20", group = g_KZ)
kz20 = input.session("1900-2000", "", inline = "KZ20", group = g_KZ)
kz20_color = input.color(color.blue, "", inline = "KZ20", group = g_KZ)
// ==================== KILLZONE 21 ====================
use_kz21 = input.bool(true, "20:00-21:00", inline = "KZ21", group = g_KZ)
kz21_txt = input.string("20-21", "", inline = "KZ21", group = g_KZ)
kz21 = input.session("2000-2100", "", inline = "KZ21", group = g_KZ)
kz21_color = input.color(color.blue, "", inline = "KZ21", group = g_KZ)
// ==================== KILLZONE 22 ====================
use_kz22 = input.bool(true, "21:00-22:00", inline = "KZ22", group = g_KZ)
kz22_txt = input.string("21-22", "", inline = "KZ22", group = g_KZ)
kz22 = input.session("2100-2200", "", inline = "KZ22", group = g_KZ)
kz22_color = input.color(color.blue, "", inline = "KZ22", group = g_KZ)
// ==================== KILLZONE 23 ====================
use_kz23 = input.bool(true, "22:00-23:00", inline = "KZ23", group = g_KZ)
kz23_txt = input.string("22-23", "", inline = "KZ23", group = g_KZ)
kz23 = input.session("2200-2300", "", inline = "KZ23", group = g_KZ)
kz23_color = input.color(color.blue, "", inline = "KZ23", group = g_KZ)
// ==================== KILLZONE 24 ====================
use_kz24 = input.bool(true, "23:00-00:00", inline = "KZ24", group = g_KZ)
kz24_txt = input.string("23-00", "", inline = "KZ24", group = g_KZ)
kz24 = input.session("2300-0000", "", inline = "KZ24", group = g_KZ)
kz24_color = input.color(color.blue, "", inline = "KZ24", group = g_KZ)
// ==================== KILLZONE 25 ====================
use_kz25 = input.bool(true, "17:00-21:00", inline = "KZ25", group = g_KZ)
kz25_txt = input.string("H4", "", inline = "KZ25", group = g_KZ)
kz25 = input.session("1700-2100", "", inline = "KZ25", group = g_KZ)
kz25_color = input.color(color.orange, "", inline = "KZ25", group = g_KZ)
// ==================== KILLZONE 26 ====================
use_kz26 = input.bool(true, "21:00-01:00", inline = "KZ26", group = g_KZ)
kz26_txt = input.string("H4", "", inline = "KZ26", group = g_KZ)
kz26 = input.session("2100-0100", "", inline = "KZ26", group = g_KZ)
kz26_color = input.color(color.aqua, "", inline = "KZ26", group = g_KZ)
// ==================== KILLZONE 27 ====================
use_kz27 = input.bool(true, "01:00-05:00", inline = "KZ27", group = g_KZ)
kz27_txt = input.string("H4", "", inline = "KZ27", group = g_KZ)
kz27 = input.session("0100-0500", "", inline = "KZ27", group = g_KZ)
kz27_color = input.color(color.green, "", inline = "KZ27", group = g_KZ)
// ==================== KILLZONE 28 ====================
use_kz28 = input.bool(true, "05:00-09:00", inline = "KZ28", group = g_KZ)
kz28_txt = input.string("H4", "", inline = "KZ28", group = g_KZ)
kz28 = input.session("0500-0900", "", inline = "KZ28", group = g_KZ)
kz28_color = input.color(color.yellow, "", inline = "KZ28", group = g_KZ)
// ==================== KILLZONE 29 ====================
use_kz29 = input.bool(true, "09:00-13:00", inline = "KZ29", group = g_KZ)
kz29_txt = input.string("H4", "", inline = "KZ29", group = g_KZ)
kz29 = input.session("0900-1300", "", inline = "KZ29", group = g_KZ)
kz29_color = input.color(color.red, "", inline = "KZ29", group = g_KZ)
// ==================== KILLZONE 30 ====================
use_kz30 = input.bool(true, "13:00-17:00", inline = "KZ30", group = g_KZ)
kz30_txt = input.string("H4", "", inline = "KZ30", group = g_KZ)
kz30 = input.session("1300-1700", "", inline = "KZ30", group = g_KZ)
kz30_color = input.color(color.purple, "", inline = "KZ30", group = g_KZ)
// ==================== KILLZONE 31 ====================
use_kz31 = input.bool(false, "05:00-17:00", inline = "KZ31", group = g_KZ)
kz31_txt = input.string("H12", "", inline = "KZ31", group = g_KZ)
kz31 = input.session("0500-1700", "", inline = "KZ31", group = g_KZ)
kz31_color = input.color(color.purple, "", inline = "KZ31", group = g_KZ)
// ==================== KILLZONE 32 ====================
use_kz32 = input.bool(false, "17:00-05:00", inline = "KZ32", group = g_KZ)
kz32_txt = input.string("H12", "", inline = "KZ32", group = g_KZ)
kz32 = input.session("1700-0500", "", inline = "KZ32", group = g_KZ)
kz32_color = input.color(color.purple, "", inline = "KZ32", group = g_KZ)
// ==================== KILLZONE 33 ====================
use_kz33 = input.bool(false, "17:00-01:00", inline = "KZ33", group = g_KZ)
kz33_txt = input.string("H8", "", inline = "KZ33", group = g_KZ)
kz33 = input.session("1700-0100", "", inline = "KZ33", group = g_KZ)
kz33_color = input.color(color.purple, "", inline = "KZ33", group = g_KZ)
// ==================== KILLZONE 34 ====================
use_kz34 = input.bool(false, "01:00-09:00", inline = "KZ34", group = g_KZ)
kz34_txt = input.string("H8", "", inline = "KZ34", group = g_KZ)
kz34 = input.session("0100-0900", "", inline = "KZ34", group = g_KZ)
kz34_color = input.color(color.purple, "", inline = "KZ34", group = g_KZ)
// ==================== KILLZONE 35 ====================
use_kz35 = input.bool(false, "09:00-17:00", inline = "KZ35", group = g_KZ)
kz35_txt = input.string("H8", "", inline = "KZ35", group = g_KZ)
kz35 = input.session("0900-1700", "", inline = "KZ35", group = g_KZ)
kz35_color = input.color(color.purple, "", inline = "KZ35", group = g_KZ)
// ==================== KILLZONE 36 ====================
use_kz36 = input.bool(false, "17:00-23:00", inline = "KZ36", group = g_KZ)
kz36_txt = input.string("H6", "", inline = "KZ36", group = g_KZ)
kz36 = input.session("1700-2300", "", inline = "KZ36", group = g_KZ)
kz36_color = input.color(color.purple, "", inline = "KZ36", group = g_KZ)
// ==================== KILLZONE 37 ====================
use_kz37 = input.bool(false, "23:00-05:00", inline = "KZ37", group = g_KZ)
kz37_txt = input.string("H6", "", inline = "KZ37", group = g_KZ)
kz37 = input.session("2300-0500", "", inline = "KZ37", group = g_KZ)
kz37_color = input.color(color.purple, "", inline = "KZ37", group = g_KZ)
// ==================== KILLZONE 38 ====================
use_kz38 = input.bool(false, "05:00-11:00", inline = "KZ38", group = g_KZ)
kz38_txt = input.string("H6", "", inline = "KZ38", group = g_KZ)
kz38 = input.session("0500-1100", "", inline = "KZ38", group = g_KZ)
kz38_color = input.color(color.purple, "", inline = "KZ38", group = g_KZ)
// ==================== KILLZONE 39 ====================
use_kz39 = input.bool(false, "11:00-17:00", inline = "KZ39", group = g_KZ)
kz39_txt = input.string("H6", "", inline = "KZ39", group = g_KZ)
kz39 = input.session("1100-1700", "", inline = "KZ39", group = g_KZ)
kz39_color = input.color(color.purple, "", inline = "KZ39", group = g_KZ)
var g_LABELS = "Killzone Pivots"
show_pivots = input.bool(false, "Show Pivots", inline = "PV", group = g_LABELS)
use_alerts = input.bool(false, "Alert Broken Pivots", inline = "PV", tooltip = "The desired killzones must be enabled at the time that an alert is created, along with the show pivots option, in order for alerts to work", group = g_LABELS)
show_midpoints = input.bool(false, "Show Pivot Midpoints", inline = "mp", group = g_LABELS)
stop_midpoints = input.bool(false, "Stop Once Mitigated", inline = "mp", group = g_LABELS)
show_labels = input.bool(false, "Show Pivot Labels", inline = "LB", tooltip = "Show labels denoting each killzone's high and low. Optionally choose to show the price of each level. Right side will show labels on the right-hand side of the chart until they are reached", group = g_LABELS)
label_price = input.bool(false, "Display Price", inline = "LB", group = g_LABELS)
label_right = input.bool(false, "Right Side", inline = "LB", group = g_LABELS)
ext_pivots = input.string("Until Mitigated", "Extend Pivots...", options = , group = g_LABELS)
ext_which = input.string("All", "...From Which Sessions", options = , group = g_LABELS)
ash_str = input.string("AS.H", "Killzone 1 Labels", inline = "L_AS", group = g_LABELS)
asl_str = input.string("AS.L", "", inline = "L_AS", group = g_LABELS)
loh_str = input.string("LO.H", "Killzone 2 Labels", inline = "L_LO", group = g_LABELS)
lol_str = input.string("LO.L", "", inline = "L_LO", group = g_LABELS)
nah_str = input.string("NYAM.H", "Killzone 3 Labels", inline = "L_NA", group = g_LABELS)
nal_str = input.string("NYAM.L", "", inline = "L_NA", group = g_LABELS)
nlh_str = input.string("NYL.H", "Killzone 4 Labels", inline = "L_NL", group = g_LABELS)
nll_str = input.string("NYL.L", "", inline = "L_NL", group = g_LABELS)
nph_str = input.string("NYPM.H", "Killzone 5 Labels", inline = "L_NP", group = g_LABELS)
npl_str = input.string("NYPM.L", "", inline = "L_NP", group = g_LABELS)
kzp_style = get_line_type(input.string(defval = 'Solid', title = "Pivot Style", options = , inline = "KZP", group = g_LABELS))
kzp_width = input.int(1, "", inline = "KZP", group = g_LABELS)
kzm_style = get_line_type(input.string(defval = 'Dotted', title = "Midpoint Style", options = , inline = "KZM", group = g_LABELS))
kzm_width = input.int(1, "", inline = "KZM", group = g_LABELS)
var g_RNG = "Killzone Range"
show_range = input.bool(false, "Show Killzone Range", tooltip = "Show the most recent ranges of each selected killzone, from high to low", group = g_RNG)
show_range_avg = input.bool(false, "Show Average", tooltip = "Show the average range of each selected killzone", group = g_RNG)
range_avg = input.int(5, "Average Length", 0, tooltip = "This many previous sessions will be used to calculate the average. If there isn't enough data on the current chart, it will use as many sessions as possible", group = g_RNG)
range_pos = get_table_pos(input.string('Top Right', "Table Position", options = , group = g_RNG))
range_size = get_size(input.string('Normal', "Table Size", options = , group = g_RNG))
var g_DWM = "Day - Week - Month"
sep_unlimited = input.bool(false, "Unlimited", tooltip = "Unlimited will show as many of the selected lines as possible. Otherwise, the session drawing limit will be used", group = g_DWM)
alert_HL = input.bool(false, "Alert High/Low Break", tooltip = "Alert when any selected highs and lows are traded through. The desired timeframe's high/low option must be enabled at the time that an alert is created", group = g_DWM)
show_d_open = input.bool(false, "D Open", inline = "DO", group = g_DWM)
dhl = input.bool(false, "High/Low", inline = "DO", tooltip = "", group = g_DWM)
ds = input.bool(false, "Separators", inline = "DO", tooltip = "Mark where a new day begins", group = g_DWM)
d_color = input.color(color.blue, "", inline = "DO", group = g_DWM)
show_w_open = input.bool(false, "W Open", inline = "WO", group = g_DWM)
whl = input.bool(false, "High/Low", inline = "WO", tooltip = "", group = g_DWM)
ws = input.bool(false, "Separators", inline = "WO", tooltip = "Mark where a new week begins", group = g_DWM)
w_color = input.color(#089981, "", inline = "WO", group = g_DWM)
show_m_open = input.bool(false, "M Open", inline = "MO", group = g_DWM)
mhl = input.bool(false, "High/Low", inline = "MO", tooltip = "", group = g_DWM)
ms = input.bool(false, "Separators", inline = "MO", tooltip = "Mark where a new month begins", group = g_DWM)
m_color = input.color(color.red, "", inline = "MO", group = g_DWM)
htf_style = get_line_type(input.string(defval = 'Solid', title = "Style", options = , inline = "D0", group = g_DWM))
htf_width = input.int(1, "", inline = "D0", group = g_DWM)
dow_labels = input.bool(false, "Day of Week Labels", inline = "DOW", group = g_DWM)
dow_yloc = input.string('Bottom', "", options = , inline = "DOW", group = g_DWM)
dow_xloc = input.string('Midnight', "", options = , inline = "DOW", group = g_DWM)
dow_hide_wknd = input.bool(false, "Hide Weekend Labels", group = g_DWM)
var g_OPEN = "Opening Prices"
open_unlimited = input.bool(false, "Unlimited", tooltip = "Unlimited will show as many of the selected lines as possible. Otherwise, the session drawing limit will be used", group = g_OPEN)
use_h1 = input.bool(false, "", inline = "H1", group = g_OPEN)
h1_text = input.string("17:00 H12/H8/H6/H4", "", inline = "H1", group = g_OPEN)
h1 = input.session("1700-1701", "", inline = "H1", group = g_OPEN)
h1_color = input.color(color.black, "", inline = "H1", group = g_OPEN)
use_h2 = input.bool(false, "", inline = "H2", group = g_OPEN)
h2_text = input.string("05:00 H12/H6/H4", "", inline = "H2", group = g_OPEN)
h2 = input.session("0500-0501", "", inline = "H2", group = g_OPEN)
h2_color = input.color(color.black, "", inline = "H2", group = g_OPEN)
use_h3 = input.bool(false, "", inline = "H3", group = g_OPEN)
h3_text = input.string("01:00 H8/H4", "", inline = "H3", group = g_OPEN)
h3 = input.session("0100-0101", "", inline = "H3", group = g_OPEN)
h3_color = input.color(color.black, "", inline = "H3", group = g_OPEN)
use_h4 = input.bool(false, "", inline = "H4", group = g_OPEN)
h4_text = input.string("09:00 H8/H4", "", inline = "H4", group = g_OPEN)
h4 = input.session("0900-0901", "", inline = "H4", group = g_OPEN)
h4_color = input.color(color.black, "", inline = "H4", group = g_OPEN)
use_h5 = input.bool(false, "", inline = "H5", group = g_OPEN)
h5_text = input.string("23:00 H6", "", inline = "H5", group = g_OPEN)
h5 = input.session("2300-2301", "", inline = "H5", group = g_OPEN)
h5_color = input.color(color.black, "", inline = "H5", group = g_OPEN)
use_h6 = input.bool(false, "", inline = "H6", group = g_OPEN)
h6_text = input.string("11:00 H6", "", inline = "H6", group = g_OPEN)
h6 = input.session("1100-1101", "", inline = "H6", group = g_OPEN)
h6_color = input.color(color.black, "", inline = "H6", group = g_OPEN)
use_h7 = input.bool(false, "", inline = "H7", group = g_OPEN)
h7_text = input.string("21:00 H4", "", inline = "H7", group = g_OPEN)
h7 = input.session("2100-2101", "", inline = "H7", group = g_OPEN)
h7_color = input.color(color.black, "", inline = "H7", group = g_OPEN)
use_h8 = input.bool(false, "", inline = "H8", group = g_OPEN)
h8_text = input.string("13:00 H4", "", inline = "H8", group = g_OPEN)
h8 = input.session("1300-1301", "", inline = "H8", group = g_OPEN)
h8_color = input.color(color.black, "", inline = "H8", group = g_OPEN)
hz_style = get_line_type(input.string(defval = 'Dotted', title = "Style", options = , inline = "H0", group = g_OPEN))
hz_width = input.int(1, "", inline = "H0", group = g_OPEN)
var g_VERTICAL = "Timestamps"
v_unlimited = input.bool(false, "Unlimited", tooltip = "Unlimited will show as many of the selected lines as possible. Otherwise, the session drawing limit will be used", group = g_VERTICAL)
use_v1 = input.bool(false, "", inline = "V1", group = g_VERTICAL)
v1 = input.session("0000-0001", "", inline = "V1", group = g_VERTICAL)
v1_color = input.color(color.black, "", inline = "V1", group = g_VERTICAL)
use_v2 = input.bool(false, "", inline = "V2", group = g_VERTICAL)
v2 = input.session("0800-0801", "", inline = "V2", group = g_VERTICAL)
v2_color = input.color(color.black, "", inline = "V2", group = g_VERTICAL)
use_v3 = input.bool(false, "", inline = "V3", group = g_VERTICAL)
v3 = input.session("1000-1001", "", inline = "V3", group = g_VERTICAL)
v3_color = input.color(color.black, "", inline = "V3", group = g_VERTICAL)
use_v4 = input.bool(false, "", inline = "V4", group = g_VERTICAL)
v4 = input.session("1200-1201", "", inline = "V4", group = g_VERTICAL)
v4_color = input.color(color.black, "", inline = "V4", group = g_VERTICAL)
vl_style = get_line_type(input.string(defval = 'Dotted', title = "Style", options = , inline = "V0", group = g_VERTICAL))
vl_width = input.int(1, "", inline = "V0", group = g_VERTICAL)
// ---------------------------------------- Inputs --------------------------------------------------
// ---------------------------------------- Variables & Constants --------------------------------------------------
type kz
string _title
array _box
array _hi_line
array _md_line
array _lo_line
array _hi_label
array _lo_label
array _hi_valid
array _md_valid
array _lo_valid
array _range_store
float _range_current
type hz
array LN
array LB
array CO
type dwm_hl
array hi_line
array lo_line
array hi_label
array lo_label
bool hit_high = false
bool hit_low = false
type dwm_info
string tf
float o = na
float h = na
float l = na
float ph = na
float pl = na
type lines_helper
hz _hz
string h
string h_text
color h_color
initLines() =>
array res = array.new()
if use_h1
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h1, h1_text, h1_color))
if use_h2
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h2, h2_text, h2_color))
if use_h3
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h3, h3_text, h3_color))
if use_h4
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h4, h4_text, h4_color))
if use_h5
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h5, h5_text, h5_color))
if use_h6
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h6, h6_text, h6_color))
if use_h7
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h7, h7_text, h7_color))
if use_h8
res.push(lines_helper.new(hz.new(array.new_line(), array.new_label(), array.new_bool()), h8, h8_text, h8_color))
res
var array lines = initLines()
type kz_helper
kz _kz
string session
color c
string box_txt
string hi_txt
string lo_txt
initKZ() =>
array res = array.new()
if use_kz01
res.push(kz_helper.new(
kz.new(kz01_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz01, kz01_color, kz01_txt, "KZ01.H", "KZ01.L"
))
if use_kz02
res.push(kz_helper.new(
kz.new(kz02_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz02, kz02_color, kz02_txt, "KZ02.H", "KZ02.L"
))
if use_kz03
res.push(kz_helper.new(
kz.new(kz03_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz03, kz03_color, kz03_txt, "KZ03.H", "KZ03.L"
))
if use_kz04
res.push(kz_helper.new(
kz.new(kz04_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz04, kz04_color, kz04_txt, "KZ04.H", "KZ04.L"
))
if use_kz05
res.push(kz_helper.new(
kz.new(kz05_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz05, kz05_color, kz05_txt, "KZ05.H", "KZ05.L"
))
if use_kz06
res.push(kz_helper.new(
kz.new(kz06_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz06, kz06_color, kz06_txt, "KZ06.H", "KZ06.L"
))
if use_kz07
res.push(kz_helper.new(
kz.new(kz07_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz07, kz07_color, kz07_txt, "KZ07.H", "KZ07.L"
))
if use_kz08
res.push(kz_helper.new(
kz.new(kz08_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz08, kz08_color, kz08_txt, "KZ08.H", "KZ08.L"
))
if use_kz09
res.push(kz_helper.new(
kz.new(kz09_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz09, kz09_color, kz09_txt, "KZ09.H", "KZ09.L"
))
if use_kz10
res.push(kz_helper.new(
kz.new(kz10_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz10, kz10_color, kz10_txt, "KZ10.H", "KZ10.L"
))
if use_kz11
res.push(kz_helper.new(
kz.new(kz11_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz11, kz11_color, kz11_txt, "KZ11.H", "KZ11.L"
))
if use_kz12
res.push(kz_helper.new(
kz.new(kz12_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz12, kz12_color, kz12_txt, "KZ12.H", "KZ12.L"
))
if use_kz13
res.push(kz_helper.new(
kz.new(kz13_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz13, kz13_color, kz13_txt, "KZ13.H", "KZ13.L"
))
if use_kz14
res.push(kz_helper.new(
kz.new(kz14_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz14, kz14_color, kz14_txt, "KZ14.H", "KZ14.L"
))
if use_kz15
res.push(kz_helper.new(
kz.new(kz15_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz15, kz15_color, kz15_txt, "KZ15.H", "KZ15.L"
))
if use_kz16
res.push(kz_helper.new(
kz.new(kz16_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz16, kz16_color, kz16_txt, "KZ16.H", "KZ16.L"
))
if use_kz17
res.push(kz_helper.new(
kz.new(kz17_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz17, kz17_color, kz17_txt, "KZ17.H", "KZ17.L"
))
if use_kz18
res.push(kz_helper.new(
kz.new(kz18_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz18, kz18_color, kz18_txt, "KZ18.H", "KZ18.L"
))
if use_kz19
res.push(kz_helper.new(
kz.new(kz19_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz19, kz19_color, kz19_txt, "KZ19.H", "KZ19.L"
))
if use_kz20
res.push(kz_helper.new(
kz.new(kz20_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz20, kz20_color, kz20_txt, "KZ20.H", "KZ20.L"
))
if use_kz21
res.push(kz_helper.new(
kz.new(kz21_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz21, kz21_color, kz21_txt, "KZ21.H", "KZ21.L"
))
if use_kz22
res.push(kz_helper.new(
kz.new(kz22_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz22, kz22_color, kz22_txt, "KZ22.H", "KZ22.L"
))
if use_kz23
res.push(kz_helper.new(
kz.new(kz23_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz23, kz23_color, kz23_txt, "KZ23.H", "KZ23.L"
))
if use_kz24
res.push(kz_helper.new(
kz.new(kz24_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz24, kz24_color, kz24_txt, "KZ24.H", "KZ24.L"
))
if use_kz25
res.push(kz_helper.new(
kz.new(kz25_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz25, kz25_color, kz25_txt, "KZ25.H", "KZ25.L"
))
if use_kz26
res.push(kz_helper.new(
kz.new(kz26_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz26, kz26_color, kz26_txt, "KZ26.H", "KZ26.L"
))
if use_kz27
res.push(kz_helper.new(
kz.new(kz27_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz27, kz27_color, kz27_txt, "KZ27.H", "KZ27.L"
))
if use_kz28
res.push(kz_helper.new(
kz.new(kz28_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz28, kz28_color, kz28_txt, "KZ28.H", "KZ28.L"
))
if use_kz29
res.push(kz_helper.new(
kz.new(kz29_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz29, kz29_color, kz29_txt, "KZ29.H", "KZ29.L"
))
if use_kz30
res.push(kz_helper.new(
kz.new(kz30_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz30, kz30_color, kz30_txt, "KZ30.H", "KZ30.L"
))
if use_kz31
res.push(kz_helper.new(
kz.new(kz31_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz31, kz31_color, kz31_txt, "KZ31.H", "KZ31.L"
))
if use_kz32
res.push(kz_helper.new(
kz.new(kz32_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz32, kz32_color, kz32_txt, "KZ32.H", "KZ32.L"
))
if use_kz33
res.push(kz_helper.new(
kz.new(kz33_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz33, kz33_color, kz33_txt, "KZ33.H", "KZ33.L"
))
if use_kz34
res.push(kz_helper.new(
kz.new(kz34_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz34, kz34_color, kz34_txt, "KZ34.H", "KZ34.L"
))
if use_kz35
res.push(kz_helper.new(
kz.new(kz35_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz35, kz35_color, kz35_txt, "KZ35.H", "KZ35.L"
))
if use_kz36
res.push(kz_helper.new(
kz.new(kz36_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz36, kz36_color, kz36_txt, "KZ36.H", "KZ36.L"
))
if use_kz37
res.push(kz_helper.new(
kz.new(kz37_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz37, kz37_color, kz37_txt, "KZ37.H", "KZ37.L"
))
if use_kz38
res.push(kz_helper.new(
kz.new(kz38_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz38, kz38_color, kz38_txt, "KZ38.H", "KZ38.L"
))
if use_kz39
res.push(kz_helper.new(
kz.new(kz39_txt, array.new_box(), array.new_line(), array.new_line(), array.new_line(), array.new_label(), array.new_label(), array.new_bool(), array.new_bool(), array.new_bool(), array.new_float()),
kz39, kz39_color, kz39_txt, "KZ39.H", "KZ39.L"
))
res
var array _kz = initKZ()
var d_hl = dwm_hl.new(array.new_line(), array.new_line(), array.new_label(), array.new_label())
var w_hl = dwm_hl.new(array.new_line(), array.new_line(), array.new_label(), array.new_label())
var m_hl = dwm_hl.new(array.new_line(), array.new_line(), array.new_label(), array.new_label())
var d_info = dwm_info.new("D")
var w_info = dwm_info.new("W")
var m_info = dwm_info.new("M")
t_co = not na(time("", cutoff, gmt_tz))
type ts_helper
string session
array lines
color c
initTS() =>
array res = array.new()
if use_v1
res.push(ts_helper.new(v1, array.new_line(), v1_color))
if use_v2
res.push(ts_helper.new(v2, array.new_line(), v2_color))
if use_v3
res.push(ts_helper.new(v3, array.new_line(), v3_color))
if use_v4
res.push(ts_helper.new(v4, array.new_line(), v4_color))
res
var array ts_data = initTS()
var d_sep_line = array.new_line()
var w_sep_line = array.new_line()
var m_sep_line = array.new_line()
var d_line = array.new_line()
var w_line = array.new_line()
var m_line = array.new_line()
var d_label = array.new_label()
var w_label = array.new_label()
var m_label = array.new_label()
var transparent = #ffffff00
var ext_current = ext_which == 'Most Recent'
var ext_past = ext_pivots == 'Past Mitigation'
update_dwm_info(dwm_info n) =>
if timeframe.change(n.tf)
n.ph := n.h
n.pl := n.l
n.o := open
n.h := high
n.l := low
else
n.h := math.max(high, n.h)
n.l := math.min(low, n.l)
if dhl or show_d_open
update_dwm_info(d_info)
if whl or show_w_open
update_dwm_info(w_info)
if mhl or show_m_open
update_dwm_info(m_info)
// ---------------------------------------- Variables & Constants --------------------------------------------------
// ---------------------------------------- Functions --------------------------------------------------
get_box_color(color c) =>
color.new(c, box_transparency)
get_text_color(color c) =>
color.new(c, text_transparency)
// ---------------------------------------- Functions --------------------------------------------------
// ---------------------------------------- Core Logic --------------------------------------------------
dwm_sep(string tf, bool use, array arr, color col) =>
if use
if timeframe.change(tf)
arr.unshift(line.new(bar_index, high * 1.0001, bar_index, low, style = htf_style, width = htf_width, extend = extend.both, color = col))
if not sep_unlimited and arr.size() > max_days
arr.pop().delete()
dwm_open(string tf, bool use, array lns, array lbls, dwm_info n, color col) =>
if use
if lns.size() > 0
lns.get(0).set_x2(time)
lbls.get(0).set_x(time)
if timeframe.change(tf)
lns.unshift(line.new(time, n.o, time, n.o, xloc = xloc.bar_time, style = htf_style, width = htf_width, color = col))
lbls.unshift(label.new(time, n.o, tf + " OPEN", xloc = xloc.bar_time, style = label.style_label_left, color = transparent, textcolor = txt_color, size = lbl_size))
if not sep_unlimited and lns.size() > max_days
lns.pop().delete()
lbls.pop().delete()
dwm_hl(string tf, bool use, dwm_hl hl, dwm_info n, color col) =>
if use
if hl.hi_line.size() > 0
hl.hi_line.get(0).set_x2(time)
hl.lo_line.get(0).set_x2(time)
hl.hi_label.get(0).set_x(time)
hl.lo_label.get(0).set_x(time)
if timeframe.change(tf)
hl.hi_line.unshift(line.new(time, n.ph, time, n.ph, xloc = xloc.bar_time, style = htf_style, width = htf_width, color = col))
hl.lo_line.unshift(line.new(time, n.pl, time, n.pl, xloc = xloc.bar_time, style = htf_style, width = htf_width, color = col))
hl.hi_label.unshift(label.new(time, n.ph, "P" + tf + "H", xloc = xloc.bar_time, style = label.style_label_left, color = transparent, textcolor = txt_color, size = lbl_size))
hl.lo_label.unshift(label.new(time, n.pl, "P" + tf + "L", xloc = xloc.bar_time, style = label.style_label_left, color = transparent, textcolor = txt_color, size = lbl_size))
hl.hit_high := false
hl.hit_low := false
if not sep_unlimited and hl.hi_line.size() > max_days
hl.hi_line.pop().delete()
hl.lo_line.pop().delete()
hl.hi_label.pop().delete()
hl.lo_label.pop().delete()
if hl.hi_line.size() > 0 and alert_HL
if not hl.hit_high and high > hl.hi_line.get(0).get_y1()
hl.hit_high := true
alert(str.format("Hit P{0}H", tf))
if not hl.hit_low and low < hl.lo_line.get(0).get_y1()
hl.hit_low := true
alert(str.format("Hit P{0}L", tf))
dwm() =>
if tf_limit_is_equal_or_more_chart_tf
// DWM - Separators
dwm_sep("D", ds, d_sep_line, d_color)
dwm_sep("W", ws, w_sep_line, w_color)
dwm_sep("M", ms, m_sep_line, m_color)
// DWM - Open Lines
dwm_open("D", show_d_open, d_line, d_label, d_info, d_color)
dwm_open("W", show_w_open, w_line, w_label, w_info, w_color)
dwm_open("M", show_m_open, m_line, m_label, m_info, m_color)
// DWM - Highs and Lows
dwm_hl("D", dhl, d_hl, d_info, d_color)
dwm_hl("W", whl, w_hl, w_info, w_color)
dwm_hl("M", mhl, m_hl, m_info, m_color)
method vline(ts_helper this) =>
bool t = not na(time("", this.session, gmt_tz))
bool t_prev = not na(time("", this.session, gmt_tz, bars_back = 1))
array arr = this.lines
color col = this.c
if t and not t_prev
arr.unshift(line.new(bar_index, high * 1.0001, bar_index, low, style = vl_style, width = vl_width, extend = extend.both, color = col))
if not v_unlimited
if arr.size() > max_days
arr.pop().delete()
vlines() =>
if tf_limit_is_equal_or_more_chart_tf
for in ts_data
vline(value)
method hz_line(lines_helper this) =>
bool t = not na(time("", this.h, gmt_tz))
bool t_prev = not na(time("", this.h, gmt_tz, bars_back = 1))
hz hz = this._hz
string txt = this.h_text
color col = this.h_color
if t and not t_prev
hz.LN.unshift(line.new(bar_index, open, bar_index, open, style = hz_style, width = hz_width, color = col))
hz.LB.unshift(label.new(bar_index, open, txt, style = label.style_label_left, color = transparent, textcolor = txt_color, size = lbl_size))
array.unshift(hz.CO, false)
if not open_unlimited and hz.LN.size() > max_days
hz.LN.pop().delete()
hz.LB.pop().delete()
hz.CO.pop()
if not t and hz.CO.size() > 0
if not hz.CO.get(0)
hz.LN.get(0).set_x2(bar_index)
hz.LB.get(0).set_x(bar_index)
if (use_cutoff ? t_co : false)
hz.CO.set(0, true)
hz_lines() =>
if tf_limit_is_equal_or_more_chart_tf
for in lines
hz_line(value)
del_kz(kz k) =>
if k._box.size() > max_days
k._box.pop().delete()
if k._hi_line.size() > max_days
k._hi_line.pop().delete()
k._lo_line.pop().delete()
k._hi_valid.pop()
k._lo_valid.pop()
if show_midpoints
k._md_line.pop().delete()
k._md_valid.pop()
if k._hi_label.size() > max_days
k._hi_label.pop().delete()
k._lo_label.pop().delete()
update_price_string(label L, float P) =>
S = L.get_text()
pre = str.substring(S, 0, str.pos(S, " "))
str.trim(pre)
L.set_text(str.format("{0} ({1})", pre, P))
adjust_in_kz(kz kz, bool t) =>
if t
kzBox0 = kz._box.get(0)
kzBox0.set_right(time)
newTop = math.max(kzBox0.get_top(), high)
kzBox0.set_top(newTop)
newBottom = math.min(kzBox0.get_bottom(), low)
kzBox0.set_bottom(newBottom)
kz._range_current := newTop - newBottom
if show_pivots and kz._hi_line.size() > 0
kzHiLine0 = kz._hi_line.get(0)
kzHiLine0.set_x2(time)
if high > kzHiLine0.get_y1()
kzHiLine0.set_xy1(time, high)
kzHiLine0.set_xy2(time, high)
kzLoLine0 = kz._lo_line.get(0)
kzLoLine0.set_x2(time)
if low < kzLoLine0.get_y1()
kzLoLine0.set_xy1(time, low)
kzLoLine0.set_xy2(time, low)
if show_midpoints
kzMidLine0 = kz._md_line.get(0)
kzMidLine0.set_x2(time)
kzMidLine0.set_xy1(time, math.avg(kzHiLine0.get_y2(), kzLoLine0.get_y2()))
kzMidLine0.set_xy2(time, math.avg(kzHiLine0.get_y2(), kzLoLine0.get_y2()))
if show_labels and kz._hi_label.size() > 0
if label_right
kz._hi_label.get(0).set_x(time)
kz._lo_label.get(0).set_x(time)
if high > kz._hi_label.get(0).get_y()
kz._hi_label.get(0).set_xy(time, high)
if label_price
update_price_string(kz._hi_label.get(0), high)
if low < kz._lo_label.get(0).get_y()
kz._lo_label.get(0).set_xy(time, low)
if label_price
update_price_string(kz._lo_label.get(0), low)
adjust_out_kz(kz kz, bool t, bool t_prev) =>
boxCount = kz._box.size()
if not t and boxCount > 0
if t_prev
array.unshift(kz._range_store, kz._range_current)
if kz._range_store.size() > range_avg
kz._range_store.pop()
if show_pivots and boxCount > 0
for i = 0 to boxCount - 1 by 1
if not ext_current or i == 0
kzHiValid = kz._hi_valid.get(i)
if ext_past or kzHiValid
kz._hi_line.get(i).set_x2(time)
if show_labels and label_right
kz._hi_label.get(i).set_x(time)
if kzHiValid and high > kz._hi_line.get(i).get_y1()
if use_alerts and i == 0
alert('Broke ' + kz._title + ' High', alert.freq_once_per_bar)
kz._hi_valid.set(i, false)
if show_labels and label_right
kz._hi_label.get(i).set_style(label.style_label_down)
else if use_cutoff ? t_co : false
kz._hi_valid.set(i, false)
kzLoValid = kz._lo_valid.get(i)
if ext_past or kzLoValid
kz._lo_line.get(i).set_x2(time)
if show_labels and label_right
kz._lo_label.get(i).set_x(time)
if kzLoValid and low < kz._lo_line.get(i).get_y1()
if use_alerts and i == 0
alert("Broke " + kz._title + " Low", alert.freq_once_per_bar)
kz._lo_valid.set(i, false)
if show_labels and label_right
kz._lo_label.get(i).set_style(label.style_label_up)
else if use_cutoff ? t_co : false
kz._lo_valid.set(i, false)
if show_midpoints and not t
if stop_midpoints ? kz._md_valid.get(i) : true
kz._md_line.get(i).set_x2(time)
if kz._md_valid.get(i) and low <= kz._md_line.get(i).get_y1() and high >= kz._md_line.get(i).get_y1()
kz._md_valid.set(i, false)
else
break
method manage_kz(kz_helper this) =>
kz kz = this._kz
c = this.c
string box_txt = this.box_txt
string hi_txt = this.hi_txt
string lo_txt = this.lo_txt
if tf_limit_is_equal_or_more_chart_tf
t = not na(time("", this.session, gmt_tz))
t_prev = not na(time("", this.session, gmt_tz, bars_back = 1))
if t and not t_prev
_c = get_box_color(c)
_t = get_text_color(c)
kz._box.unshift(box.new(time, high, time, low, xloc = xloc.bar_time, border_color = show_kz ? _c : na, bgcolor = show_kz ? _c : na, text = show_kz and show_kz_text ? box_txt : na, text_color = _t))
if show_pivots
kz._hi_line.unshift(line.new(time, high, time, high, xloc = xloc.bar_time, style = kzp_style, color = c, width = kzp_width))
kz._lo_line.unshift(line.new(time, low, time, low, xloc = xloc.bar_time, style = kzp_style, color = c, width = kzp_width))
if show_midpoints
kz._md_line.unshift(line.new(time, math.avg(high, low), time, math.avg(high, low), xloc = xloc.bar_time, style = kzm_style, color = c, width = kzm_width))
array.unshift(kz._md_valid, true)
array.unshift(kz._hi_valid, true)
array.unshift(kz._lo_valid, true)
if show_labels
_hi_txt = label_price ? str.format('{0} ({1})', hi_txt, high) : hi_txt
_lo_txt = label_price ? str.format('{0} ({1})', lo_txt, low) : lo_txt
if label_right
kz._hi_label.unshift(label.new(time, high, _hi_txt, xloc = xloc.bar_time, color = transparent, textcolor = txt_color, style = label.style_label_left, size = lbl_size))
kz._lo_label.unshift(label.new(time, low, _lo_txt, xloc = xloc.bar_time, color = transparent, textcolor = txt_color, style = label.style_label_left, size = lbl_size))
else
kz._hi_label.unshift(label.new(time, high, _hi_txt, xloc = xloc.bar_time, color = transparent, textcolor = txt_color, style = label.style_label_down, size = lbl_size))
kz._lo_label.unshift(label.new(time, low, _lo_txt, xloc = xloc.bar_time, color = transparent, textcolor = txt_color, style = label.style_label_up, size = lbl_size))
del_kz(kz)
adjust_in_kz(kz, t)
adjust_out_kz(kz, t, t_prev)
for in _kz
manage_kz(value)
dwm()
vlines()
hz_lines()
new_dow_time = dow_xloc == 'Midday' ? time - timeframe.in_seconds("D") / 2 * 1000 : time
new_day = dayofweek(new_dow_time, gmt_tz) != dayofweek(new_dow_time, gmt_tz)
var dow_top = dow_yloc == 'Top'
var saturday = "SATURDAY"
var sunday = "SUNDAY"
var monday = "MONDAY"
var tuesday = "TUESDAY"
var wednesday = "WEDNESDAY"
var thursday = "THURSDAY"
var friday = "FRIDAY"
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 1 and new_day and not dow_hide_wknd, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = sunday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 2 and new_day, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = monday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 3 and new_day, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = tuesday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 4 and new_day, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = wednesday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 5 and new_day, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = thursday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 6 and new_day, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = friday)
plotchar(dow_labels and timeframe.isintraday and dayofweek(new_dow_time, gmt_tz) == 7 and new_day and not dow_hide_wknd, location = dow_top ? location.top : location.bottom, char = '', textcolor = txt_color, text = saturday)
get_min_days_stored() =>
store = array.new_int()
for in _kz
int tmpStoreSize = value._kz._range_store.size()
if tmpStoreSize > 0
store.push(tmpStoreSize)
store.min()
method set_table(table tbl, kz kz, int row, string txt, bool t, color col) =>
table.cell(tbl, 0, row, txt, text_size = range_size, bgcolor = get_box_color(col), text_color = txt_color)
table.cell(tbl, 1, row, str.tostring(kz._range_current), text_size = range_size, bgcolor = t ? get_box_color(col) : na, text_color = txt_color)
if show_range_avg
table.cell(tbl, 2, row, str.tostring(kz._range_store.avg()), text_size = range_size, text_color = txt_color)
if show_range and barstate.islast
var tbl = table.new(range_pos, 10, 10, chart.bg_color, chart.fg_color, 2, chart.fg_color, 1)
table.cell(tbl, 0, 0, "Killzone", text_size = range_size, text_color = txt_color)
table.cell(tbl, 1, 0, "Range", text_size = range_size, text_color = txt_color)
if show_range_avg
table.cell(tbl, 2, 0, "Avg (" + str.tostring(get_min_days_stored()) + ")", text_size = range_size, text_color = txt_color)
for in _kz
set_table(tbl, value._kz, index + 1, value.box_txt, not na(time("", value.session, gmt_tz)), value.c)
// ---------------------------------------- Core Logic --------------------------------------------------
Индикатор

Индикатор

Индикатор

Индикатор

Индикатор

Индикатор

Индикатор

Индикатор

Trend Angle Momentum [MarkitTick]💡 This tool measures market structure not just as a sequence of highs and lows, but as a rate of directional change. It detects confirmed swing pivots and then calculates the geometric angle of the trendline connecting each pivot to the one before it, translating pure price action into a single, intuitive metric: degrees of trend steepness. Instead of asking traders to infer momentum from candle shape or oscillator divergence, it hands them a number — the actual angle of ascent or descent between structural turning points — along with an optional smoothed reading of how that angle is evolving over time.
✨ Originality and Utility
Most swing-detection tools stop at marking the high or low. This script goes a step further by quantifying the relationship between consecutive swings using trigonometry. Each swing-to-swing move is converted into a percentage price change, which is then run through an arctangent function to produce a true geometric angle in degrees, independent of the instrument's absolute price scale. A move on a $2 stock and a move on a $2,000 stock that share the same percentage steepness will report the same angle, making the readings comparable across symbols and timeframes in a way that raw price-based slope calculations cannot achieve.
The utility here is twofold. First, the angle itself acts as a quantified momentum proxy: a shallow angle after a strong prior swing signals decelerating momentum well before a lagging oscillator would confirm it, while a steepening angle on successive swings signals acceleration. Second, an optional Angle Momentum layer tracks a rolling average of the last several swing angles, smoothing out single-swing noise and revealing whether the broader structural rhythm of the market is strengthening or weakening. This combination — geometric normalization plus rolling angle smoothing — gives traders a structural momentum read that is not available from stock pivot tools or generic slope indicators alone.
🔬 Methodology and Concepts
• Confirmed Pivot Detection
The script identifies swing highs and swing lows using a symmetric fractal method: a bar is only confirmed as a pivot high if it is higher than a defined number of bars to its left and right, and likewise for a pivot low. The "Left Bars" and "Right Bars" inputs control how many bars on each side must confirm the extreme. Because the right-side bars must fully close before a pivot can be validated, every pivot marked on the chart is confirmed historical structure, not a live, moving estimate — the marker is deliberately plotted with a backward offset equal to the right-bar count so that its horizontal position matches where the actual swing extreme occurred, not where it was confirmed.
• Percent-to-Angle Conversion
Once two consecutive confirmed pivots of the same type (high-to-high or low-to-low) are available, the script calculates the percentage price change between them. This percentage is then optionally normalized by the number of bars separating the two pivots (via the "Normalize Angle by Bars" input), which converts the reading from "how much did price move" into "how much did price move per bar," a more useful measure of steepness when swings vary widely in duration. The resulting rate is passed through an arctangent function and converted from radians to degrees, producing a bounded, intuitive angle: values approaching plus or minus ninety degrees represent extremely steep percentage moves, while values near zero represent flat, sideways structure.
• Angle Momentum (Optional Smoothing Layer)
When enabled, the script maintains a running array of the most recent swing angles (separately for highs and lows) and reports their simple average over a user-defined lookback length. This produces a second-order reading: rather than looking at a single swing's angle in isolation, it shows whether the sequence of recent swing angles is, on average, steep or shallow, positive or negative — a way of gauging whether structural momentum is building or fading across several swings rather than just the most recent one.
• Live Dashboard
A compact on-chart table continuously summarizes the last confirmed high pivot price, the last confirmed low pivot price, the most recent high-swing angle, the most recent low-swing angle, and whether Angle Momentum smoothing is currently active, giving traders a persistent numerical snapshot without needing to hover over chart objects.
🎨 Visual Guide
Diagonal trend lines connecting consecutive swing highs (default red/green by angle sign) and consecutive swing lows are drawn directly between the two pivot points, visually representing the geometric slope being measured.
A small numeric label at the midpoint of each swing line displays the calculated angle in degrees, colored green for a positive (upward) angle and red for a negative (downward) angle by default.
When Angle Momentum is enabled, an additional label appears at the most recent pivot showing the smoothed "Mom" value in a distinct color (orange for highs, blue for lows by default), separated visually from the raw single-swing angle label.
Cross-style markers plot at each confirmed pivot high and pivot low directly on price, offset backward to align with the actual bar where the extreme occurred.
The dashboard table (position configurable) shows the symbol, timeframe, last high and low pivot prices, the latest angle readings for each, and the current on/off state of Angle Momentum.
📖 How to Use
Treat the angle label on each swing line as a normalized momentum reading for that specific leg of price action: steep angles indicate strong directional conviction, shallow angles indicate a weakening or consolidating move.
Compare the angle of the most recent swing to the angle of the swing before it. A sequence of progressively shallower high-to-high angles during an uptrend can indicate fading bullish momentum even while price is still making new highs, a structural early warning that pure price action alone may not show.
When Angle Momentum is enabled, use the smoothed "Mom" reading as a broader confirmation layer: a rising average angle across several swings supports the idea that momentum is genuinely building, rather than reacting to a single outlier swing.
Divergences between price structure and angle behavior — for example, higher swing highs paired with a declining angle momentum reading — can be used as a discretionary caution signal ahead of a potential trend deceleration.
The two alert conditions ("High Pivot Formed" and "Low Pivot Formed") can be used to build automated or semi-automated workflows that trigger only once a swing point is fully confirmed, rather than on every bar.
⚠️ Confirmation Lag Notice
All pivots and their associated angle calculations are confirmed structure. Because a pivot cannot be validated until the required number of bars on its right side have closed, every marker, line, and label is necessarily plotted a number of bars after the actual high or low occurred, equal to the "Right Bars" setting. The plotted markers are intentionally offset backward to align visually with the true location of the swing extreme — this does not mean the indicator is predicting or anticipating pivots in real time. Traders should treat swing confirmations as lagging structural events by design, not as leading signals.
⚙️ Inputs and Settings
Left Bars / Right Bars: Define the symmetric lookback and lookahead window used to validate a swing high or low. Larger values filter out minor fluctuations and confirm only more significant structural turning points, at the cost of a longer confirmation delay. Smaller values confirm pivots faster but are more sensitive to short-term noise.
Show High Swing Lines / Show Low Swing Lines: Independently toggle the diagonal trend lines connecting consecutive high or low pivots.
Show Swing Point Dots: Toggles the cross markers plotted directly at each confirmed pivot price.
Normalize Angle by Bars: When enabled, divides the percentage move between two pivots by the number of bars separating them before calculating the angle, producing a "steepness per bar" measure rather than a raw total-move angle. Useful for comparing swings of different durations on a more equal footing.
Use Angle Momentum: Enables the rolling average smoothing layer over the last several swing angles, plotted as an additional label at each new pivot.
Angle Momentum Length: Sets how many recent swing angles are averaged together for the smoothed momentum reading. Shorter lengths react faster to recent swings; longer lengths produce a smoother, slower-changing average.
Dashboard Position / Show Dashboard: Controls visibility and screen placement of the summary table.
High Pivot Action / Low Pivot Action: Custom text tags embedded into the JSON alert payload for each pivot type, useful for routing alerts to external automation systems that key off a specific action string.
Color inputs: Independently control the color of swing lines, angle text, pivot cross markers, momentum labels, and dashboard theming to match personal charting preferences.
🔍 Deconstruction of the Underlying Scientific and Academic Framework
The core of this indicator rests on classical trigonometric slope analysis rather than any single named technical analysis school. Converting a price move into an angle is mathematically equivalent to computing the arctangent of a rate of change, the same operation used broadly in engineering and physics to express a gradient as an angular measure rather than a raw ratio. Expressing the swing-to-swing move as a percentage change before applying the arctangent function normalizes the calculation across instruments of different absolute price levels, addressing a well-known limitation of naive "price-per-bar" slope measures, which are not comparable between a low-priced and high-priced instrument, or between two different timeframes without adjustment. The optional bar-normalization step draws on the same logic used in rate-of-change and momentum oscillators broadly, where a raw price delta is scaled by the time or bar interval over which it occurred to produce a comparable velocity-style reading rather than a simple magnitude.
The pivot detection mechanism itself is a fractal/symmetric extremum test, a widely used method in swing-structure analysis (related in spirit to Bill Williams' fractal indicator and to classical Dow Theory's emphasis on confirmed swing highs and lows as the building blocks of trend structure) that requires a candidate bar to dominate a defined number of bars on both sides before being accepted as a genuine local extremum. This symmetric confirmation requirement is a standard technique for filtering transient noise out of swing-point identification, at the deliberate cost of confirmation lag, a well-documented trade-off in any lookback-based extremum detection method. The Angle Momentum layer applies a simple moving average — one of the most foundational smoothing techniques in time-series analysis — to the sequence of discrete angle readings themselves rather than to price, effectively treating "swing angle" as its own derived data series and smoothing it the same way a moving average would smooth a price or oscillator series, in order to separate signal (the underlying trend in momentum) from noise (single-swing outliers).
⚠️ Disclaimer
All provided scripts and indicators are strictly for educational exploration and must not be interpreted as financial advice or a recommendation to execute trades. We expressly disclaim all liability for any financial losses or damages that may result, directly or indirectly, from the reliance on or application of these tools. Market participation carries inherent risk where past performance never guarantees future returns, leaving all investment decisions and due diligence solely at your own discretion. Индикатор

Индикатор

Perfect Trading Entry Exit FinderA trading entry/exit finder that searches retained structural swings for positive terminal-to-terminal opportunities, anchors each qualifying entry and exit to the actual retained terminal extremes, and preserves the corresponding causal confirmation for direct timing and opportunity comparison. Results are search and review outputs, not a guarantee of profitability or future performance.
Name:
Perfect Trading Entry Exit Finder
Searchable Name:
Perfect Trading Entry Exit Finder
Technical Name:
Retained Terminal-to-Terminal Perfect Entry Exit and Causal Confirmation Finder
Short title:
Perfect Entry Exit
Summary
Perfect Trading Entry Exit Finder is an experimental finder for locating complete retained terminal-to-terminal opportunities while preserving the corresponding causal signal timing for comparison.
The retrospective Perfect terminals are the completed ideal entry and exit endpoints defined by the Perfect search, but those exact terminal entries or exits are only causally executable at those same bars when the corresponding causal confirmation actually becomes available there.
A BUY can confirm after the retained trough has already occurred.
A SELL can confirm after the retained peak has already occurred.
Multiple signal attempts can develop before a completed structural swing establishes the retained signal identity.
A signal can appear very close to the terminal or only after part of the move has already occurred.
An earlier signal can be superseded before the structural swing finishes.
A retained signal can also survive structurally while the move to the next opposite retained terminal still produces a non-positive result.
A strong terminal-to-terminal move can begin before the corresponding causal confirmation becomes available.
Perfect Trading Entry Exit Finder attempts to expose this difference directly.
Within this script, Perfect has a specific meaning.
A Perfect Opportunity is a completed retained opposite-terminal relationship whose directional terminal-to-terminal result is positive.
The first retained terminal becomes the:
PERFECT BUY
or PERFECT SELL
The next retained opposite terminal becomes the corresponding:
PERFECT EXIT
The interval between those retained terminals becomes:
PERFECT HOLD
The Perfect Entry, Hold, and Exit remain attached to the retained terminal structure.
The corresponding causal confirmation remains separately preserved.
That allows the finder to expose both:
the complete retained terminal-to-terminal opportunity
and
the portion represented from the actual causal confirmation
without redefining one as the other.
The finder can therefore expose:
Perfect Buy and Perfect Sell terminals
Perfect Exit terminals
Perfect Hold paths
complete terminal-to-terminal opportunity
corresponding causal confirmation
confirmation timing difference
Causal Result
Opportunity Capture
Confirmation Loss
false/non-perfect signal context
completed structural relationships
and the current unfinished search state
The primary search is based on completed retained structural relationships.
An additional preview mode can expose the currently implied unfinished result at the chart edge.
Because that newest structural state is incomplete, preview-only output can change as additional bars arrive.
How it works
Perfect Trading Entry Exit Finder combines a causal signal foundation with a completed terminal-to-terminal search.
The causal signal foundation preserves what could actually become available through the forward signal process.
The Perfect search evaluates the completed retained structural opportunity.
Those two reference systems remain separate.
That separation is deliberately engineered into the finder: the completed terminal result can be reviewed alongside the causal confirmation without allowing the later result to replace what was actually available through the causal process.
The completed terminal opportunity shows the full retained swing after structural finalization.
The causal confirmation shows where the corresponding signal became available through the causal process.
The finder brings those references together for direct review without treating one as the other.
Terminal opportunity and causal confirmation
A completed terminal opportunity and the signal timing actually available through causal confirmation can differ substantially.
Once a swing has reached structural finalization, its retained terminal can be identified precisely within the completed structure.
At that terminal itself, however, the signal that ultimately corresponds to the completed opportunity may not yet have confirmed.
Price can move before confirmation becomes available.
An earlier signal can fail or be superseded.
The opposite terminal can later reveal that a retained signal did not produce a positive completed opportunity.
The finder keeps these relationships visible.
The Perfect terminal remains the Perfect terminal.
The causal confirmation remains the causal confirmation.
This allows direct comparison between:
where the complete retained opportunity began
and
where the corresponding causal signal became available
without moving either reference point to make them appear equivalent.
Perfect search structure
The Perfect search evaluates structurally finalized retained opposite-terminal relationships.
For a BUY-side opportunity, the completed structure runs from a retained trough toward a retained opposite peak.
For a SELL-side opportunity, the completed structure runs from a retained peak toward a retained opposite trough.
The finder evaluates the complete retained swing rather than an isolated signal bar.
The first retained terminal supplies the Perfect Entry reference.
The later opposite retained terminal supplies the Perfect Exit reference.
A positive directional result qualifies the completed relationship as a Perfect Opportunity.
The terminal relationship defines the Perfect search.
Causal signal foundation
The Perfect results remain connected to an underlying causal signal process.
This matters because identifying the finalized Perfect terminal retrospectively does not make that exact terminal entry or exit causally executable when the terminal bar originally occurred.
The associated causal confirmation remains separately preserved.
That provides the causal comparison reference for the completed Perfect opportunity.
Structural resolution
Structural resolution determines when a retained swing reaches structural finalization and becomes available to the completed Perfect search.
The script provides selectable structural approaches for reviewing this relationship.
These approaches can produce differences in finalization timing and retained structural presentation.
They do not change the central definition of a Perfect Opportunity:
a structurally finalized retained entry terminal followed by its retained opposite terminal with a positive directional result.
Earliest Terminal
Earliest Terminal provides an alternative structural view emphasizing earlier retained terminal context.
Original Grouping
Original Grouping provides the script's primary grouped structural view.
Conditional Accelerated
Conditional Accelerated provides an alternative earlier-finalization structural view when its conditions are satisfied.
These modes affect structural finalization and retained terminal identity while preserving the same Perfect Opportunity definition.
Retained signal identity
More than one causal signal attempt can occur during a structural swing.
The completed finder result does not treat all of those attempts as equivalent.
A retained signal identity provides the connection between the causal signal process and the structurally finalized terminal result.
Other signal attempts can remain visible as superseded or false/non-perfect context.
Retained terminal association
The finder preserves two conceptually different references:
Terminal reference
the retained structural extreme belonging to the structurally finalized opportunity.
Causal reference
the corresponding signal confirmation that was actually available through the causal signal process.
These references describe different parts of the same completed opportunity.
Neither replaces the other.
Retained terminal chain
Structurally finalized retained terminals provide the sequence used by the Perfect search.
A newest retained terminal by itself does not yet provide a complete terminal-to-terminal opportunity.
A later opposite endpoint is needed before the completed pair can be evaluated.
This prevents the normal retrospective search from treating unfinished structure as though its later endpoint were already known.
Eligible terminal pair
A structurally finalized retained opposite-terminal relationship provides the potential Perfect Entry and Perfect Exit.
The directional terminal-to-terminal result determines whether that completed relationship qualifies as a Perfect Opportunity.
The Perfect endpoints remain the retained structural terminals.
The search does not redefine them using the causal confirmation or an arbitrary interior price.
Perfect Opportunity
A Perfect Opportunity is a qualifying structurally finalized retained opposite-terminal relationship with a positive directional terminal-to-terminal result.
For a BUY-side opportunity, the retained trough is followed by a higher retained opposite terminal.
For a SELL-side opportunity, the retained peak is followed by a lower retained opposite terminal.
A zero or negative completed directional result does not qualify as Perfect.
Perfect is therefore a search definition applied to structurally finalized retained structure.
It is separate from whether the causal confirmation captured all, some, or little of that move.
Perfect Entry
The Perfect Entry is the first retained terminal of a qualifying completed opportunity.
For a BUY-side opportunity, this becomes the Perfect Buy .
For a SELL-side opportunity, this becomes the Perfect Sell .
The marker remains anchored to the retained terminal used by the completed search.
It is not moved forward to the causal confirmation.
Perfect Exit
The Perfect Exit is the retained opposite terminal that completes the qualifying opportunity.
The finalized structural endpoint defines the exit.
The finder does not replace it with an arbitrary interior price simply because that price would have produced a larger temporary result.
This preserves a consistent terminal-to-terminal definition.
Perfect Hold
PERFECT HOLD spans the complete qualifying retained opportunity.
For a Perfect BUY, it represents the retained trough-to-opposite-peak movement.
For a Perfect SELL, it represents the retained peak-to-opposite-trough movement.
The displayed hold therefore represents the complete Perfect opportunity between the two retained endpoints.
Shared Perfect Exit and next Perfect Entry
One retained terminal can conceptually complete one opportunity and begin another.
For example, a retained peak can complete a BUY-side Perfect opportunity and also become the starting terminal of a later SELL-side opportunity.
Likewise, a retained trough can complete a SELL-side opportunity and begin a later BUY-side opportunity.
The chart can present these shared terminal relationships without changing the underlying Perfect definitions.
Causal confirmation comparison
The finder can display the causal confirmation associated with a Perfect terminal opportunity.
The causal reference shows where the corresponding surviving signal became available.
The Perfect reference remains at the structurally finalized retained terminal.
This produces a direct comparison between:
Perfect terminal opportunity
and
causal signal availability
without treating the Perfect terminal as though it were known causally at that point.
Terminal-to-confirmation delay
The finder measures the timing difference between the retained Perfect Entry terminal and its corresponding causal confirmation.
A same-bar relationship has no bar delay.
A later confirmation represents a later causal availability point.
The purpose of this measurement is to expose the timing gap between the completed Perfect benchmark and the signal timing actually represented by the causal process.
Perfect result
Perfect Result is the directional percentage result between the retained Perfect Entry and retained Perfect Exit of a qualifying opportunity.
It represents the full completed terminal-to-terminal opportunity identified by the finder.
Causal result
Causal Result uses the corresponding causal confirmation as the entry reference while preserving the same completed exit context.
This allows the causal result and Perfect result to be compared within the same completed opportunity.
The two measurements answer different questions and are not interchangeable.
Opportunity Capture
Opportunity Capture describes how much of the completed Perfect opportunity is represented by the corresponding causal result.
It provides a normalized comparison between:
the complete retained opportunity
and
the result represented from causal confirmation.
Its purpose is interpretation, not to redefine either reference.
Confirmation Loss
Confirmation Loss describes the difference between the Perfect Result and corresponding Causal Result.
It shows how much of the completed terminal-to-terminal movement was not represented from the causal confirmation reference.
Perfect Result, Causal Result, Opportunity Capture, and Confirmation Loss therefore provide different views of the same completed opportunity.
False / non-perfect context
The finder can expose causal signal activity that did not become part of a qualifying Perfect Opportunity.
This can include:
1. signal attempts that were later superseded
2. structurally finalized retained relationships whose directional result was not positive
These categories help distinguish the wider causal signal stream from the subset of completed relationships classified as Perfect.
Superseded attempts
Several causal signal attempts can occur while the underlying structure is still developing.
Not every attempt becomes the retained identity associated with the structurally finalized opportunity.
Non-retained attempts can remain visible as superseded context.
This allows users to see that the eventual Perfect terminal association does not imply every earlier causal signal was correct.
Retained non-positive pairs
Structural retention alone does not automatically create a Perfect Opportunity.
A structurally finalized retained relationship must still satisfy the Perfect Opportunity definition.
If the completed directional terminal-to-terminal result is zero or negative, it does not qualify as a Perfect Opportunity and remains non-perfect context.
Unmatched newest retained terminal
The newest retained terminal remains incomplete until a later opposite terminal provides the second endpoint needed for structural finalization of the terminal-to-terminal relationship.
The normal retrospective search therefore does not prematurely classify that newest terminal as Perfect or non-perfect.
The optional preview can temporarily expose what the unfinished structure currently implies.
That preview remains separate from structurally finalized retained history.
Finder modes
The script contains two search modes:
Retrospective
Repainting Preview
They use the same conceptual Perfect Entry/Exit definition.
Their difference is whether the newest unfinished chart-edge structure is temporarily included.
Retrospective
Retrospective uses structurally finalized retained relationships.
Its Perfect results are based on swings whose terminal relationship has already reached structural finalization.
This is the primary review mode.
Repainting Preview
Repainting Preview temporarily extends the same search concept to the unfinished chart-right structure.
It shows what the newest result currently looks like before structural finalization has occurred.
Because the latest structural state is unfinished, preview-only output can change as future bars arrive.
The preview therefore repaints by design.
It should not be interpreted as a permanent causal Perfect signal.
Permanent completed history and preview state
The finder keeps structurally finalized retained history separate from temporary preview completion.
Finalized retrospective results belong to the completed search.
Preview-only results belong to the unfinished chart-edge state.
This allows the finder to expose both:
structurally finalized Perfect Entry/Exit structure
and
the currently implied unfinished structure
without treating them as equivalent.
Perfect Entry / Exit display
The primary display can show:
Perfect Buy
Perfect Sell
Perfect Exit
Perfect Hold
terminal-to-terminal result
causal confirmation comparison
and supporting opportunity context
These visuals represent the underlying search result.
Display settings do not redefine what qualifies as a finalized Perfect Opportunity.
False-context display
False/non-perfect context can be displayed separately from Perfect results.
This allows the chart to show the broader causal signal activity around finalized opportunities without changing which retained terminal relationships qualify as Perfect.
Structural context
The finder retains structural context around the signals and finalized opportunities being reviewed.
This context can help distinguish finalized, unresolved, retained, or superseded relationships.
The Perfect Opportunity itself remains defined by the structurally finalized retained terminal relationship and its directional result.
Search reconstruction
The finder reconstructs the historical search context needed to display causal signal relationships, structurally finalized opportunities, and the current unfinished state.
The purpose is to preserve the distinction between causal signal timing and finalized terminal identity across loaded chart history.
Perfect Opportunity Rate
The finder calculates a Perfect Opportunity Rate describing how often eligible finalized retained terminal relationships satisfy the Perfect Opportunity definition.
Perfect Opportunity Rate is not a trading win rate.
It describes the completed search classification.
It does not establish the result of an executed strategy using causal entries, transaction costs, slippage, sizing, or external risk rules.
Search statistics
The finder provides summary statistics describing the finalized Perfect search, causal comparison, false/non-perfect context, and current search state.
These statistics are designed to help interpret the search result.
Status pages
The script includes a compact status interface for reviewing the current Perfect search, causal comparison, timing, and interpretation context.
The status display supports chart review without requiring every measurement to be placed directly on the chart.
Alerts and execution
Perfect Trading Entry Exit Finder is not an execution engine.
Retrospective Perfect terminal results identify the completed ideal entry and exit endpoints. Those exact terminal entries or exits are only causally executable at those same bars when the corresponding causal confirmation actually becomes available there.
The Perfect structure and causal confirmation are retained for search, review, and comparison.
When causal confirmation occurs later, the executable causal entry or exit occurs later; the retrospective Perfect terminal remains the completed ideal endpoint rather than an entry or exit that was available at that earlier terminal bar.
The optional preview also remains a search preview rather than an execution-ready Perfect signal source.
Finder behavior
Perfect Trading Entry Exit Finder combines causal signal identity with finalized terminal-to-terminal opportunity searching.
Its broad workflow is:
causal signal activity develops
retained structural relationships reach finalization
a completed terminal-to-terminal relationship becomes available for review
positive finalized relationships can qualify as Perfect Opportunities
Perfect Entry, Hold, and Exit remain attached to the retained terminal structure
and the corresponding causal confirmation remains separately available for comparison
The optional preview can temporarily extend the same search concept to the unfinished newest structure.
Features
Perfect terminal-to-terminal Entry/Exit finder
Perfect Buy identification
Perfect Sell identification
Perfect Exit identification
Perfect Hold paths
retained trough-to-peak opportunity review
retained peak-to-trough opportunity review
positive finalized opportunity classification
actual retained terminal anchoring
causal confirmation preservation
direct Perfect-versus-causal comparison
confirmation timing comparison
Perfect Result
Causal Result
Opportunity Capture
Confirmation Loss
false/non-perfect context
superseded signal context
unresolved newest-terminal handling
selectable structural views
finalized historical search mode
Repainting Preview
false-context visualization
Perfect Hold visualization
causal comparison markers
supporting search statistics
Perfect Opportunity Rate
compact review/status interface
review-focused standalone finder
completed ideal Perfect entry/exit endpoints compared directly with whether those exact endpoints were causally executable at the time
Strengths
Perfect Entry/Exit Search — directly finds qualifying finalized terminal-to-terminal opportunities rather than stopping at the original causal signal.
Terminal-to-Terminal Structure — Perfect Entry and Perfect Exit remain tied to retained structural endpoints.
Complete Swing Representation — Perfect Hold represents the full retained opportunity between those endpoints.
Causal Identity Preservation — keeps the corresponding causal confirmation connected to the finalized opportunity for comparison.
Direct Perfect-versus-Causal Comparison — shows both the complete retained opportunity and the result represented from causal confirmation.
Opportunity Capture Measurement — quantifies how much of the completed Perfect opportunity is represented by the causal result.
Confirmation Loss Measurement — measures the difference between Perfect and causal results.
No Arbitrary Exit Substitution — does not replace the retained opposite terminal with an arbitrary interior best price.
False-Context Separation — keeps superseded and non-positive finalized relationships distinct from qualifying Perfect Opportunities.
Unfinished-Terminal Discipline — normal retrospective results are not finalized until the necessary opposite structural endpoint exists.
Structural Choice — selectable structural views can be compared while preserving the same central Perfect concept.
Finalized-History Separation — finalized search results remain distinct from temporary chart-edge preview output.
Preview Capability — the currently implied unfinished Perfect structure can be inspected while remaining explicitly identified as repainting.
Search Diagnostics — timing, capture, confirmation loss, false context, and opportunity context remain measurable.
Weaknesses
Opposite-Terminal Requirement — the complete Perfect Entry/Exit opportunity is not known until the later retained opposite endpoint structurally finalizes the relationship.
Terminal Hindsight — the finalized retained terminal and causal confirmation are different reference systems and can occur at different times and prices.
Confirmation Delay — part of the complete terminal-to-terminal movement can occur before causal confirmation becomes available.
Superseded Signals — multiple causal attempts can occur before structural finalization establishes the retained relationship.
Non-Positive Retained Relationships — structural retention alone does not guarantee a positive Perfect Opportunity.
Perfect Definition Scope — Perfect refers specifically to the positive finalized terminal-to-terminal search definition.
No Interior Exit Optimization — the retained opposite terminal remains the Perfect Exit even if another temporary price would have produced a larger result.
Structural Dependence — different structural views can affect finalization timing and retained terminal presentation.
Causal-Signal Dependence — the comparison remains connected to an underlying causal signal process.
Preview Repainting — unfinished preview output can move, disappear, or change before structural finalization.
Perfect-Terminal Executability Is Conditional — a finalized Perfect terminal is the completed ideal entry or exit endpoint, but it is executable at that exact terminal bar only when the corresponding causal confirmation actually becomes available there.
Perfect Opportunity Rate Is Not Win Rate — it measures search classification rather than executed strategy performance.
No Execution Engine — the standalone finder does not turn Perfect search output into automated trading decisions.
No Full Strategy Return Calculation — it does not establish complete returns after sizing, transaction costs, slippage, and external trading rules.
Who it’s for
This tool is best suited for:
advanced TradingView users
users investigating the Perfect Entry/Exit problem
users searching for complete retained terminal-to-terminal opportunities
users comparing causal signal timing with structurally finalized terminal structure
users studying how much of a move occurs before confirmation
users examining trough-to-peak and peak-to-trough opportunities
users comparing Perfect Result and Causal Result
users studying Opportunity Capture
users studying Confirmation Loss
users examining false and superseded signal activity
users studying structural finalization and retained terminal relationships
users who want entry and exit markers anchored to actual retained endpoints
users who do not want arbitrary interior prices substituted for Perfect Exit
users who want the complete hold path between retained terminals
users comparing finalized structural results with an unfinished preview
users developing or evaluating separate causal methods against an explicit Perfect benchmark
Who it’s not for
This tool is not best suited for:
users expecting Perfect terminal markers to be live causal signals
users expecting final retained extremes to be known at the exact moment they occur
users expecting every causal signal to survive structural finalization
users expecting every finalized retained terminal relationship to qualify as Perfect
users expecting Perfect Opportunity Rate to represent an executed trading win rate
users expecting unfinished preview results to remain fixed
users expecting preview-only terminal markers never to repaint
users looking for broker execution from retrospective Perfect markers
users looking for automated position management from Perfect results
users looking for a complete trading strategy
users expecting causal confirmation and Perfect terminal timing to always coincide
users expecting the finder to remove confirmation delay
users expecting a guarantee of profitability or future performance
Known limitations
The finder is better at:
finding finalized retained terminal-to-terminal opportunity structure
identifying positive finalized terminal relationships
preserving complete Perfect Entry/Hold/Exit geometry
anchoring results to retained structural extremes
linking Perfect opportunity identity with causal signal confirmation
comparing complete opportunity with causal availability
measuring confirmation delay
measuring Opportunity Capture
measuring Confirmation Loss
exposing superseded and non-perfect context
comparing finalized and unfinished search structure
and reviewing how causal signals relate to complete terminal swings
than it is at:
identifying the final retained terminal causally before structural finalization
eliminating delayed confirmation
eliminating false or superseded signals
guaranteeing that every retained relationship produces a positive result
turning Perfect terminal markers into operational entries
determining whether the newest unfinished preview terminal will remain final
or determining a complete future trading result
A Perfect Opportunity is defined by the structurally finalized retained terminal relationship.
The causal confirmation can represent all, some, or very little of the complete terminal-to-terminal movement.
That difference is part of what the finder exposes.
A large Perfect Result can therefore coexist with a much smaller Causal Result.
Opportunity Capture and Confirmation Loss describe that difference.
Likewise, a retained signal can remain structurally relevant while its finalized opposite-terminal result still fails the Perfect qualification.
The term Perfect therefore belongs to the finalized search definition.
It does not imply that the finalized terminal was causally available as a Perfect signal when it originally occurred.
The optional Repainting Preview introduces an additional limitation.
It evaluates unfinished chart-right structure before structural finalization.
Future bars can therefore change preview-only output.
Finalized retrospective results remain separate from that temporary preview.
Perfect result scope
The Perfect result represents the complete retained terminal-to-terminal directional opportunity of a qualifying structurally finalized relationship.
It can include:
Perfect Buy or Perfect Sell
Perfect Exit
Perfect Hold
retained entry and exit terminals
terminal-to-terminal result
corresponding causal confirmation
Causal Result
Opportunity Capture
Confirmation Loss
and false/non-perfect context
These measurements preserve the complete opportunity and causal signal result as separate but directly comparable references.
Final note
Perfect Trading Entry Exit Finder is an experimental finder for the entry-and-exit problem that remains when the complete terminal opportunity and the causal signal available during that opportunity are not the same thing.
Its central capability is the retained terminal-to-terminal search.
Positive structurally finalized terminal relationships can become Perfect Opportunities.
The corresponding:
Perfect Entry
Perfect Hold
and Perfect Exit
remain attached to the retained terminal structure.
The causal confirmation remains separately preserved.
This allows the finder to expose both:
the complete retained entry-to-exit opportunity
and
the result represented from the corresponding causal confirmation
inside the same finalized swing.
False, superseded, or non-perfect signal context remains separately visible.
The optional preview can extend the same search concept to unfinished chart-edge structure, with its repainting behavior kept separate from structurally finalized results.
Perfect Trading Entry Exit Finder therefore remains centered on the problem it attempts to address:
locating the complete retained terminal entry, hold, and opposite-terminal exit opportunity while preserving the causal signal that was actually available for comparison.
Profitability is not guaranteed.
Future performance is not guaranteed.
The finder reports the Perfect opportunity, its terminal structure, corresponding causal result, timing difference, capture, confirmation loss, and false/non-perfect context. The retrospective Perfect terminals are the completed ideal entry and exit endpoints, but those exact terminal entries or exits were only causally executable at the time when the corresponding causal confirmation actually occurred at those same terminal bars; otherwise the executable causal timing occurred elsewhere. Индикатор

Индикатор

ATK/DEF HTF Analysis Combo# ATK/DEF HTF Analysis Combo
ATK/DEF HTF Analysis Combo is a Higher Timeframe market environment analysis framework designed to observe the broader market structure from a use-selected timeframe.
Instead of focusing only on the active chart, this indicator uses a selected Higher Timeframe as the primary reference environment and organizes several independent market measurements into one structured dashboard.
The objective is to provide a clearer view of the **broader market direction, trend condition, momentum condition, and volatility environment** through a Higher Timeframe perspective.
The indicator is designed as an analytical observation framework rather than a conventional single-condition indicator.
## 📊 Higher Timeframe Market Environment
The core of this indicator is the use of a use-selected Higher Timeframe.
The selected timeframe becomes the reference environment for the dashboard.
For example, the user may select:
* 4 Hour
* Daily
* Weekly
* Other supported timeframes
The indicator retrieves market data from the selected timeframe and evaluates the corresponding market conditions.
This allows the active chart to be viewed in the contet of a broader market environment.
A smaller chart can contain many local price movements while the selected Higher Timeframe may still maintin a different overall structure.
The purpose of this framework is to make that broader structure visible in a compact analytical format.
# 🧭 Four-Dimensional HTF Observation
ATK/DEF HTF Analysis Combo separates the Higher Timeframe environment into four primary dimensions:
**Direction**
**Trend**
**Momentum**
**Volatility**
These dimensions are intentionally treated as independent analytical modules.
They do not represent the same characteristic of the market.
A market can have a clear directional position while momentum is neutral.
A market can have strong trend strength while volatility is relatively low.
A market can experience elevated volatility while directional structure remains mixed.
By keeping these measurements separated, the dashboard preserves more information about the current Higher Timeframe environment.
# 🎯 1. HTF Direction
The HTF Direction module evaluates the directional position of the selected Higher Timeframe.
The calculation examines the relationship between:
* HTF closing price
* EMA 8
* EMA 21
* EMA 50
* +DI
* -DI
The relationship between price and multiple EMA levels provides the primary structural classification.
The +DI and -DI relationship is then used as an additional directional component.
The module classifies the environment into several states:
* 📈 Strong Bull
* 📈 Bullish
* ↗️ Mixed Bull
* ➡️ Neutral
* ↘️ Mixed Bear
* 📉 Bearish
* 📉 Strong Bear
A Direction Score ranging from **-100 to +100** is also displayed.
The score provides a normalized representation of the directional classification used by the module.
The Direction module is not intended to represent the entire market by itself.
It describes the directional position of the selected Higher Timeframe environment.
# 📈 2. HTF Trend
The HTF Trend module examines the strength and orientation of the broader trend structure.
It uses:
* ADX
* +DI
* -DI
ADX is used to describe the strength of the prevailing directional structure, while +DI and -DI provide the directional component.
The module produces several classifications:
* 🔥 Strong Bull
* 🔥 Strong Bear
* ⚡ Bull Trend
* ⚡ Bear Trend
* 🌊 Weak Trend
* 🌀 Range
This creates a distinction between **directional position** and **trend strength**.
The Direction module asks where the market is positioned.
The Trend module asks how strongly the directional structure is expressed.
These two measurements can therefore display different conditions at the same time.
# ⚡ 3. HTF Momentum
The HTF Momentum module uses RSI to describe the current momentum condition of the selected Higher Timeframe.
The RSI range is divided into multiple states:
* 🚀 Strong Up
* ⬆️ Accelerating
* ↗️ Bullish
* ➡️ Neutral
* ↘️ Bearish
* ⬇️ Declining
* 💀 Strong Down
The purpose of this classification is to provide a more detailed view of momentum rather than reducing the entire momentum condition to a simple positive or negative value.
The module also generates a normalized Momentum Score between **-100 and +100**.
Momentum is kept independent from Direction and Trend because these characteristics can behave differently within the same market environment.
# 📊 4. HTF Volatility
The HTF Volatility module measures the relative movement environment of the selected Higher Timeframe using ATR.
The current ATR is compared with its 50-period average:
**ATR Ratio = Current ATR / Average ATR**
The result is classified into:
* 🔥 Extreme
* ⚠️ High
* 📊 Elevated
* 📊 Normal
* 😴 Low
Volatility is treated as a **non-directional measurement**.
A high volatility condition does not represent an upward or downward direction.
It describes the relative magnitude of market movement compared with its reference volatility.
This distinction allows the dashboard to separate market activity from market direction.
# 🏆 5. HTF Composite Environment
The final HTF score combines the independent Direction, Trend and Momentum scores.
The three directional components are averaged into a normalized composite value.
The Volatility condition can then modify the composite value when volatility reaches elevated levels.
The resulting score is constrained to a range between:
**-100 and +100**
The composite value is intended to provide a compact representation of the broader Higher Timeframe environment.
It does not replace the individual modules.
Instead, it provides another layer of information that can be compared with the separate Direction, Trend, Momentum and Volatility readings.
# 🔬 Independent Module Architecture
One of the main characteristics of ATK/DEF HTF Analysis Combo is its independent-module architecture.
Rather than forcing all market conditions into one calculation from the beginning, each component is evaluated separately.
### Direction
Describes the broader directional position.
### Trend
Describes the strength and orientation of the trend structure.
### Momentum
Describes the current momentum condition.
### Volatility
Describes the relative magnitude of market movement.
### Composite Environment
Provides a consolidated numerical representation of the directional modules.
This architecture allows different market characteristics to remain visible instead of hing them inside one simplified reading.
# 🌐 Broader Market Observation
The indicator is designed specifically around the idea of observing the market from a broader timeframe perspective.
The active chart represents the immediate chart environment.
The selected Higher Timeframe represents the broader reference environment.
The dashboard connects these two perspectives by displaying the selected HTF conditions directly on the active chart.
This makes it possible to examine how the current chart exists within a larger market structure.
The indicator therefore focuses on **environmental context rather than individual candle interpretation**.
# 📐 Why Higher Timeframe Data Is Used
Higher Timeframe data can provide a different structural perspective from the active chart.
For example, price movement that appears highly directional on a smaller chart may exist inside a much broader range when viewed from a larger timeframe.
Likewise, a period of relatively quiet movement on the ative chart may occur while the broader timeframe maintns a clearly defined directional structure.
The purpose of the HTF framework is to expose this broader context through a consistent set of measurements.
# 🧩 Multiple Conditions Can Coexist
The dashboard does not require every module to produce the same classification.
Different combinations are possible.
For example:
**Bullish Direction + Weak Trend + Neutral Momentum + Low Volatility**
or:
**Mixed Direction + Strong Trend + Strong Momentum + High Volatility**
or:
**Neutral Direction + Range + Neutral Momentum + Normal Volatility**
These combinations represent different market environments.
The dashboard preserves these differences instead of reducing every situation to one basic directional label.
# 📊 Scoring System
Direction, Trend and Momentum use normalized scores to represent their respective classifications.
Directional scores use a range from **-100 to +100**.
The Volatility module uses a separate non-directional scale because volatility does not inherently describe upward or downward movement.
The Composite HTF Score is normalized to remain within **-100 to +100**.
The scores are mathematical representations of the conditions defined in the script.
They should be interpreted as descriptive measurements of the selected Higher Timeframe environment.
# ⚙️ User-Defined Timeframe Configuration
The Higher Timeframe reference is **user-configurable**.
Users are expected to select and configure the timeframe that they want the dashboard to analyze.
The indicator does not assume that one particular timeframe is appropriate for every chart or every market.
Changing the HTF Resolution changes the reference environment used by the dashboard.
This is an important part of the design because the indicator is intended to allow the user to determine which broader market context should be observed.
The selected timeframe should therefore be considered part of the user's analytical configuration.
# 🖥️ Dashboard
The dashboard provides a compact display of the five main analytical sections:
### 🎯 HTF Direction
Directional classification and normalized Direction Score.
### 📈 HTF Trend
Trend classification and ADX measurement.
### ⚡ HTF Momentum
Momentum classification and RSI measurement.
### 📊 HTF Volatility
Volatility classification and ATR Ratio.
### 🏆 HTF Final Environment
Composite Higher Timeframe score and overall environmental classification.
The selected HTF resolution is also displayed in the dashboard header.
The table can be positioned on either side of the chart according to the user's preference.
# 📏 Technical Components
The indicator uses established market-data calculations including:
* EMA 8
* EMA 21
* EMA 50
* RSI 14
* ADX 14
* +DI
* -DI
* ATR 14
* 50-period average ATR
These components are processed independently before being combined into the Higher Timeframe dashboard.
The purpose of the framework is not to introduce a single new mathematical measurement, but to organize several market characteristics into a unified Higher Timeframe observation structure.
# 🧠 Analytical Concept
ATK/DEF HTF Analysis Combo approaches market observation from a broader environmental perspective.
Instead of asking only whether price is moving upward or downward, the framework separates the market into several observable characteristics:
**Where is the market positioned?**
**How strong is the directional structure?**
**What is the current momentum condition?**
**How active is the market environment?**
**How do these independent conditions relate to one another?**
This creates a multi-dimensional representation of the selected Higher Timeframe environment.
---
# ⚠️ Important Information
ATK/DEF HTF Analysis Combo is an analytical and visualization tool based on market data and mattical calculations.
The classifications and scores are generated from the relationships defined in the scrt between price, EMA, DMI, ADX, RSI and ATR.
The displayed values represent the calculated state of the selected Higher Timeframe environment at the time of observation.
They are not intended to represent certainty, probability, guarantees, or future market outcomes.
The indicator does not provide finl advice, tre instructions, or recommendations, or capital-management guidance.
The purpose of this script is to provide a structured framework for observing the broader market environment through use-selected Higher Timeframe data.
User are responsible for selecting and configuring the timeframe parameters appropriate to their own analytical requirements.
Индикатор

Индикатор

MSH - Demand & Supply Zones Pro### Overview
The Demand and Supply Zones Pro indicator automatically identifies, plots, and tracks institutional market structure imbalance zones on your chart. Based on core Extended Market Structure (EMS) price action principles, it highlights areas where institutional supply or demand imbalances cause rapid price movements.
### Features & Methodology
1. Zone Identification Logic:
The indicator evaluates individual candlestick body-to-range ratios to classify candle types into:
- Base Candles: Consolidation or low-volatility bars where body size is ≤ 50% of total candle range.
- Leg-In / Leg-Out Candles: High-momentum, strong-body expansion candles.
2. Pattern Classifications (RBR, DBR, RBD, DBD):
- Demand Zones: Rally-Base-Rally (RBR) and Drop-Base-Rally (DBR).
- Supply Zones: Rally-Base-Drop (RBD) and Drop-Base-Drop (DBD).
3. Dynamic Zone Tracking & Boundaries:
- Proximal Line: Plotted at the top/bottom boundary of the base body for entry reference.
- Distal Line: Plotted at the extreme high/low wick of the base for stop-loss and risk reference.
- Dynamic Extensions & Violation Cleanup: Active zones extend automatically to current price action and are automatically removed once invalidating price breaks occur.
4. Trend & Moving Average Overlays:
- Includes integrated Rapid (EMA 7) and Fast (EMA 21) Exponential Moving Averages to quickly assess short-term momentum and trend alignment alongside zone levels.
### How to Use
- Looking for Demand Trades (Long): Seek long setups when price revisits active Green/Demand zones, especially when aligned with short-term EMA momentum.
- Looking for Supply Trades (Short): Seek short setups when price approaches active Red/Supply zones.
- Risk Management: Use the Distal boundary of the zone as a structural stop-loss level.
### Settings & Customization
- Candle Rules: Adjust body percentage thresholds for Base, Leg-In, and Leg-Out candles to match different asset classes (Equities, Forex, Crypto, Futures).
- Display Limits: Set maximum active zones displayed concurrently to maintain chart clarity.
- Visuals: Fully customizable zone fill, border colors, and label options. Индикатор

Индикатор

Индикатор

Индикатор

Adaptive Pivot Zones MTF█ OVERVIEW
Adaptive Pivot Zones MTF is a multi-timeframe trend analysis indicator based on dynamic zones calculated using pivot highs and pivot lows.
Instead of representing the trend with a single line, the indicator creates three levels inside the range between the last confirmed pivot low and pivot high. These levels form a dynamic zone whose position and width adapt to the current market structure.
The core of the indicator is the relationship between price and this zone. Depending on the selected mode, a trend change occurs either after price breaks the middle line of the zone or only after price exits the entire zone. This allows the user to choose between earlier direction changes or stronger confirmation of the move.
The indicator is designed as an MTF system. In addition to the current timeframe, two higher timeframes are analyzed. These can be selected automatically in proportion to the current TF or set manually. This helps assess whether a trend change on the lower timeframe is aligned with the broader market direction.
The central element of the signal system is Multi-Timeframe Agreement. Each trend change can be evaluated based on the agreement of 1, 2, or all 3 monitored timeframes. The higher the agreement, the stronger the directional confirmation the user receives.
The indicator also integrates automatic Entry, Stop Loss, and three Take Profit levels based on ATR or a fixed risk percentage. This combines market direction analysis with a visual representation of the potential Risk/Reward setup.
The result is a complete trend analysis system that combines dynamic pivot zones, multi-timeframe analysis, trend agreement confirmation, and automatic position management levels.
█ CONCEPTS
Pivot Zone
Pivot Zone is the foundation of the entire indicator and is used to determine the current market state.
The indicator uses confirmed pivot highs and pivot lows to define the current price range. Then, three levels are calculated inside this range according to the set values of Pivot Level 1, Pivot Level 2, and Pivot Level 3.
These levels form a dynamic zone that can be treated as an equilibrium area between the most recent significant market extremes.
Pivot Zone answers the question:
Where is the current market decision zone located?
Pivot Length
Pivot Length determines how many bars are required on each side to confirm a pivot high or pivot low.
A lower value results in more frequent pivot detection and faster adaptation of the zones to price changes, but it also increases the number of less significant pivots.
A higher value requires a more developed structure to confirm a pivot, so zones appear less often but represent more meaningful market points.
Smoothing Length additionally allows the calculated pivot levels to be smoothed.
Pivot Levels
The three pivot levels define the exact position of the zone inside the range between pivot low and pivot high.
Pivot Level 1 defines the first level of the zone, Pivot Level 2 its middle line, and Pivot Level 3 the third level.
The default setting of 0.3 / 0.5 / 0.7 creates three levels placed symmetrically around the middle of the range, but the user can adjust them freely.
The middle line has a special role because it can be used as the primary level that determines a trend change.
Trend State
Trend State determines whether the market is currently in a bullish or bearish state.
In Full Band mode, price must break above the upper boundary of the entire zone to become bullish, or below the lower boundary to become bearish.
In Mid Line mode, the direction changes as soon as price crosses the middle line of the zone.
This allows the sensitivity of the indicator to be adjusted to the analysis style:
• Mid Line → earlier direction changes
• Full Band → stronger confirmation of a zone breakout
An optional Neutral mode also allows a neutral state to be displayed when price is exactly at the decision level.
Multi-Timeframe Analysis
Adaptive Pivot Zones MTF analyzes not only the current timeframe but also two higher timeframes.
TF1 and TF2 can be selected automatically based on the current timeframe. The system proportionally chooses higher intervals so that the MTF structure can be applied across different market scales without the need to manually set each TF.
Alternatively, the user can switch MTF Mode to Manual and define Higher TF 1 and Higher TF 2 independently.
Multi-Timeframe Analysis answers the question:
Is the direction on my timeframe aligned with the higher market context?
TF1 & TF2 Zones
In addition to the current zone, the indicator can also display pivot channels from TF1 and TF2.
Each higher timeframe has its own zone with an upper and lower boundary and an optional middle line. The channel color changes according to the current trend of that timeframe.
Higher timeframe zones can be used as additional context, showing where the current price is located relative to the broader structure.
MTF Agreement
MTF Agreement determines the number of timeframes that confirm the same trend direction.
The system analyzes three timeframes:
• Current TF → current timeframe
• TF1 → first higher timeframe
• TF2 → second higher timeframe
As a result, a trend change can receive confirmation from 1, 2, or 3 timeframes.
For example:
• 1 TF → change visible only on the current timeframe
• 2 TF → current timeframe + one of the higher timeframes confirms the same direction
• 3 TF → all three timeframes indicate the same direction
It is the number of agreeing timeframes that decides which signals can be displayed.
█ FEATURES
Current TF Settings
• Pivot Length – number of bars required on each side to confirm a pivot high / pivot low.
• Smoothing Length – length of the SMA that smooths the pivot lines. A value of 1 means no smoothing.
• Pivot Level 1 / 2 / 3 – coefficients (0.0–1.0) that determine the position of the three lines inside the pivot low – pivot high range.
• Paint Bars (Mid Pivot) – colors the candles according to the current trend state of the current timeframe.
Current TF Style
• Line Width – thickness of the current timeframe pivot lines.
• Line Transparency – transparency of the pivot lines.
• Gradient Transparency – transparency of the gradient fill between the lines.
Current TF Colors
• Bullish Color / Bearish Color – colors of the lines and fill in bullish / bearish state.
• Use Neutral Color – enables a third, neutral state.
• Neutral Color – color used in the neutral state.
MTF Settings
• MTF Mode – Automatic (automatic selection of TF1 and TF2) or Manual.
• Higher TF 1 / Higher TF 2 – manual selection of higher timeframes (active only in Manual mode).
TF1 Style & Colors
• Show TF1 (lines + channel) – displays the upper and lower TF1 channel lines together with the fill.
• Show TF1 Mid Line – additionally shows the middle line of the TF1 zone.
• TF1 Line Width / Transparency / Gradient Transparency – appearance settings for the TF1 channel.
• TF1 Bullish / Bearish Color – colors of the TF1 channel depending on the trend.
TF2 Style & Colors
• Show TF2 (lines + channel) – displays the upper and lower TF2 channel lines together with the fill.
• Show TF2 Mid Line – additionally shows the middle line of the TF2 zone.
• TF2 Line Width / Transparency / Gradient Transparency – appearance settings for the TF2 channel.
• TF2 Bullish / Bearish Color – colors of the TF2 channel depending on the trend.
Signals
• Trend Change Based On – Mid Line (earlier signals) or Full Band (stronger confirmation).
• Show Buy/Sell Labels – displays signal labels on the chart.
• Show Label When 1 / 2 / 3 TF Agree – controls at what number of agreeing timeframes the signal is shown.
• Buy / Sell Label Color and Label Size – appearance of the signal labels.
TP/SL
• Show TP/SL Levels – draws Entry, Stop Loss, and Take Profit levels on an active signal that meets the TF agreement criteria.
• SL = ATR – when enabled, the SL distance is calculated based on ATR. When disabled, a fixed percentage is used.
• ATR Period for TP/SL – ATR period used for calculations.
• ATR Multiplier for SL / SL % from Entry – parameters that define the Stop Loss distance.
• RR for TP1 / TP2 / TP3 – Risk:Reward ratios for the three Take Profit levels.
• Show SL / TP1 / TP2 / TP3 Level – individual enabling of each level.
MTF Table
• Show Multi-Timeframe Table – displays a table with the trend state on the selected timeframes.
• Table Position / Text Size – position and text size of the table.
• Bull / Bear / Background / Header Colors – table color scheme.
• Show + TF (for each row) – individual enabling and selection of timeframes displayed in the table (5m, 15m, 30m, 1h, 2h, 4h, 8h, 12h, 1D, 1W, 2W).
█ APPLICATIONS
Trend direction analysis with MTF context
The indicator allows you to assess whether a direction change on the current timeframe is supported by higher timeframes. Signals with 2 or 3 TF confirmation have significantly higher informational value than signals visible only on the current chart.
Filtering signals from other indicators
It can be used as a classic trend indicator to filter signals from other indicators.
Risk and potential reward management
Automatic Entry, SL, and three TP levels allow you to immediately see the Risk/Reward setup on every trend change that meets the agreement criteria. This makes it easier to quickly decide on position size and targets.
█ NOTES
• Full Band mode generates fewer signals but with stronger confirmation of a full zone breakout. Mid Line mode reacts faster.
• The Multi-Timeframe table shows the current trend state on the selected timeframes and serves as quick context, not as an independent signal system.
• The indicator works best when combined with market structure analysis, key support/resistance levels, and other indicators such as momentum or volume. Индикатор

OptiPine: High-Performance Caching and Data PipelinesOptiPine is a high performance architecture library for Pine Script™, built for algorithms that push beyond ordinary indicator workloads. It turns caching, sparse updates, reusable storage and workload-aware data structures into practical APIs that stay small at the call site.
In a small indicator, optimization is often optional. In a rendering engine, machine learning library, simulation, dashboard or object system, it can determine whether a feature runs at all. The problem is rarely one slow formula. It is the thousands of unnecessary operations around it: recalculating unchanged results, shifting rolling arrays, scanning large collections for a few changes, and moving stored objects when one disappears.
OptiPine attacks that layer with techniques used in projects such as Pine3D and NeuraLib . The idea is simple: do less work, move less data, and let the representation follow the workload.
Compared with conventional Pine implementations of the same task, OptiPine's optimized paths commonly ran 15% to 40% faster . Sparse updates and indexed lookups exceeded 90% when the alternative scanned or searched the full collection.
Most users can stay entirely within the high-level API. A Memo cache with several dependencies looks like this:
// Pseudocode: trendRegime and volatilityRegime are floats;
// rebuildModel() is a pure calculation.
var op.FloatMemo model = op.floatMemo()
if model.staleOn(trendRegime, volatilityRegime)
model.store(rebuildModel(trendRegime, volatilityRegime))
float result = model.get()
// Output: rebuildModel() runs once, then only when either regime changes.
Memo owns the previous dependencies, first-run state, validity and cached result. The caller only declares what the result depends on.
----------------------------------------------------------------------------------------------------------------
🔷 DO NOT CALCULATE THE SAME THING TWICE
The fastest expensive calculation is the one that never needed to run. Models, simulations and generated geometry often remain valid across many script executions.
Memo is the direct choice when the cached result is an int, float, bool, string or color. staleOn() checks up to four floats, two integers, one Boolean and one string; store() saves a rebuilt value, and get() returns it.
Many models respond to regimes rather than every tiny change in raw data. Round the inputs into meaningful regimes, pass them to staleOn() , and the model runs only when a regime changes.
In practice: Memo is useful for scenario models, parameter sweeps, numerical solvers and other expensive pure calculations that reduce to one primitive result. If its dependencies repeat on nine out of ten executions, it avoids roughly 90% of those model runs.
For collections or a variable dependency list, use Memo's explicit begin() , dependencies.watch*() and miss() lifecycle.
Keep guarded work pure: Stateful ta.* and similar history-dependent calls must remain outside Memo and Watch guards. Compute them every bar, then pass their results into the guarded calculation.
🔸 WATCH: CHANGE DETECTION WITHOUT RESULT STORAGE
Watch is the lighter choice when the caller already owns the result. Several consumers can observe the same producer independently by giving each its own Watch. changed() returns true on the first observation and whenever one scalar, primitive array or OptiPine row ring changes. Row rings expose an internal revision, so checking them is O(1).
For a single source, the dependency check should take less attention than the calculation it protects. Here another component supplies one caller-owned feature array:
// Pseudocode: getFeatureSnapshot() supplies an array.
array features = getFeatureSnapshot()
var op.Watch featureWatch = op.watch()
var float modelScore = na
if featureWatch.changed(features)
modelScore := evaluateModel(features)
// Output: modelScore is rebuilt only when the features array changes.
Because OptiPine does not own features , it compares the array with a retained snapshot and rewrites that snapshot only after a change. Supported row rings use their internal revision instead. The call stays the same, and this compare-first array pattern measured roughly 35% to 60% faster than rewriting the snapshot every time.
The array comparison is still O(N), so use it when the avoided calculation costs more than the comparison. If the producer already provides one reliable change flag, use the flag directly.
For several dependencies, use an explicit pass. begin() starts the comparison, the typed watch*() methods add dependencies, and finish() returns true if the completed set changed. A Watch remembers dependencies; it does not store the result.
// Pseudocode dependencies: int length, float multiplier,
// and array features.
var op.Watch settingsWatch = op.watch()
var float result = na
settingsWatch.begin()
settingsWatch.watchInt(length)
settingsWatch.watchFloat(multiplier)
settingsWatch.watchFloats(features)
bool dependenciesChanged = settingsWatch.finish()
if dependenciesChanged
result := rebuild(length, multiplier, features)
// Output: result is rebuilt when any observed dependency changes.
Construct the Watch once with var , then run begin() and finish() on every comparison pass. For one dependency, changed(source) is the shorter path.
In practice: Watch fits module boundaries: a model can observe a feature array, a renderer can observe a managed ring, or a cache can observe several mixed settings without duplicating the producer's change logic.
CadenceGate limits how often work may run. due() is periodic; dueWhenChanged() also requires a producer revision and remembers changes until the cadence opens. Use it for intentionally delayed work such as periodic model fitting, not results that must update immediately.
----------------------------------------------------------------------------------------------------------------
🔷 ROLLING HISTORY WITHOUT SHIFTING IT
Rolling histories often perform work that adds nothing to the result. If an array keeps the latest 200 events, removing the oldest one and shifting the other 199 entries is unnecessary.
FloatRowRing and IntRowRing keep fixed-width rows in reusable storage. Once full, the next row overwrites the oldest physical slot while reads remain chronological.
var op.FloatRowRing history = op.floatRowRing(200, 3)
float atr14 = ta.atr(14)
if barstate.isconfirmed
history.push(array.from(close, volume, atr14))
float oldestPrice = history.at(0, 0)
float latestPrice = history.newestAt(0, 0)
// Output: after a confirmed push, these are the oldest and newest retained closes.
A push costs O(width), or O(1) through pushValue() for a width-one ring. Rings also provide chronological windows and gathered rows. When several producers can mutate a ring, a separate consumer can detect its revision with Watch.changed(ring) in O(1).
In practice: Row rings fit pivots, completed trades, sampled features and other fixed event histories.
Performance: A full ring overwrites one row instead of shifting every retained row. Its chronological output uses at most two native contiguous copies, which measured 90% faster than rebuilding a 512-cell, width-four output row by row.
Use RingCursor when several caller-owned arrays need the same circular layout. Ordinary series history such as close should remain native Pine.
----------------------------------------------------------------------------------------------------------------
🔷 KEEP DYNAMIC OBJECTS STABLE
Dynamic objects become surprisingly expensive when identity is tied to array position. If one object is removed from several parallel arrays, every later entry shifts, every synchronized payload array needs the same removal, and every external reference to those positions becomes fragile.
StablePool is not the zone storage itself. It keeps one association: an object ID supplied by the script points to a reusable array slot. The ID answers "which zone is this?" while the slot answers "where is this zone's data stored?"
The example has three different logical zones named A, B and C. Their IDs, 1001, 1002 and 1003, are arbitrary unique values chosen for readability. Real IDs may come from a pivot bar, timestamp, order number or incrementing counter.
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
// This example never has more than two active zones.
var array prices = array.new(2, na)
if barstate.isfirst
// A receives slot 0. B receives slot 1.
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
prices.set(slotA, 100.0)
prices.set(slotB, 200.0)
// Zone A no longer exists. Its slot becomes available.
zonePool.release(ZONE_A_ID)
// C is a new zone with a new identity, but it can reuse A's old slot.
= zonePool.acquire(ZONE_C_ID)
prices.set(slotC, 300.0)
// Output: B keeps slot 1. C has ID 1003 but reuses A's released slot 0.
// prices is .
Why C needs a new ID: C is a different zone, even though it occupies the same array position A once used. Reusing 1001 would describe A returning, not a new zone C. IDs preserve object identity; slots are only reusable storage addresses.
Several fields, one slot: In production, the same slot usually addresses every field belonging to the object. Continuing the A, B and C lifecycle with four parallel arrays:
const int ZONE_A_ID = 1001
const int ZONE_B_ID = 1002
const int ZONE_C_ID = 1003
var op.StablePool zonePool = op.stablePool()
var array zonePrices = array.new()
var array zoneTimes = array.new()
var array zoneStrengths = array.new()
var array zoneColors = array.new()
if barstate.isfirst
= zonePool.acquire(ZONE_A_ID)
= zonePool.acquire(ZONE_B_ID)
// Grow every payload array to cover the allocated slots.
int required = zonePool.slotCount()
op.ensureSizeFloat(zonePrices, required, na)
op.ensureSizeInt(zoneTimes, required, na)
op.ensureSizeFloat(zoneStrengths, required, na)
op.ensureSizeColor(zoneColors, required, na)
zonePrices.set(slotA, 100.0)
zoneTimes.set(slotA, 10)
zoneStrengths.set(slotA, 0.40)
zoneColors.set(slotA, color.blue)
zonePrices.set(slotB, 200.0)
zoneTimes.set(slotB, 20)
zoneStrengths.set(slotB, 0.80)
zoneColors.set(slotB, color.red)
zonePool.release(ZONE_A_ID)
= zonePool.acquire(ZONE_C_ID)
// C reuses A's slot, so every field at that slot must be overwritten.
zonePrices.set(slotC, 300.0)
zoneTimes.set(slotC, 30)
zoneStrengths.set(slotC, 0.60)
zoneColors.set(slotC, color.lime)
// Output: B keeps slot 1 in every array. C owns slot 0 in every array.
// Nothing is removed or shifted.
acquire(id) returns the slot and whether the ID was newly added. Calling it again for an active ID returns the same slot. release(id) frees the slot, but does not erase its array data, so every field must be overwritten when that slot is reused.
The example preallocates two values because it has at most two active zones. A dynamic script can grow its payload arrays with ensureSize*() whenever acquire() reports a new ID. zonePool.slots() returns the currently active slots as a read-only view.
In practice: One zone slot can index its price, time, color, strength and line across several arrays. In the complete example later, the pivot bar and event type form each zone ID. Releasing one zone frees its slot without shifting other zones or breaking saved positions.
Performance: StablePool is independent of payload layout: its slots can index parallel arrays or one array of UDTs. acquire() , release() and find() are O(1), and releasing an object never shifts caller-owned payloads.
For a few fixed objects, manual indices are simpler. StablePool becomes useful when IDs appear and disappear over time, several payload arrays share the same slots, or other parts of the script retain those positions.
SlotCache is the frame-based alternative. Call begin() , acquire every active key, then call finish() ; previously active keys that were not touched are retired automatically.
----------------------------------------------------------------------------------------------------------------
🔷 UPDATE ONLY WHAT CHANGED
Large state does not imply large change. A dashboard may contain 10,000 cells while only a few change on one bar, or a large object system may need to refresh only a handful of entries.
A conventional dirty-flag array must be cleared and scanned in full. DirtySet stores only the changed indices, removes duplicate marks and begins a new cycle without clearing the entire universe. It is a work list, not payload storage or an ID-to-slot map.
Here StablePool resolves zoneId , the arrays store zone data, and DirtySet schedules the slots that need rebuilding. The event values are pseudocode:
int MAX_ZONES = 50000
var op.StablePool zones = op.stablePool()
var op.DirtySet dirtySlots = op.dirtySet(MAX_ZONES)
var array tops = array.new()
var array bottoms = array.new()
var array midpoints = array.new()
// Start this bar's sparse-work cycle.
dirtySlots.begin()
if zoneGeometryChanged
// StablePool converts the logical ID into a reusable physical slot.
= zones.acquire(zoneId)
if created
op.ensureSizeFloat(tops, zoneSlot + 1, na)
op.ensureSizeFloat(bottoms, zoneSlot + 1, na)
op.ensureSizeFloat(midpoints, zoneSlot + 1, na)
tops.set(zoneSlot, newTop)
bottoms.set(zoneSlot, newBottom)
dirtySlots.mark(zoneSlot)
if zoneStyleChanged
int styleSlot = zones.find(zoneId)
if styleSlot >= 0
dirtySlots.mark(styleSlot) // A second mark of the same slot is ignored.
// Process only the distinct physical slots marked during this bar.
for dirtySlot in dirtySlots.values()
float midpoint = (tops.get(dirtySlot) + bottoms.get(dirtySlot)) * 0.5
midpoints.set(dirtySlot, midpoint)
redrawZone(zones.keyAt(dirtySlot), midpoint)
// Output: one zone is rebuilt once even if geometry and style both mark it.
Repeated marks are deduplicated, and unmarked zones are never visited. Work scales with the number of changed slots, not the size of the collection. If the natural address is already a dense index, mark it directly without StablePool.
In practice: Several producers can mark work, then one consumer updates each affected cell, drawing or record once. With 1% of entries changed, this measured 93% faster than clearing and scanning the full universe.
----------------------------------------------------------------------------------------------------------------
🔷 KEYED LOOKUP WITHOUT GUESSWORK
Keyed lookup appears throughout object systems, caches and grouped data, but no structure fits every key set. Distribution, rebuild frequency and query volume change the best choice. OptiPine sees the completed keys at build() , then selects the lookup shape that fits them.
🔸 TYPED STORES: ONE VALUE PER KEY
A typed store maps each integer key to one primitive value. build() pairs entries at matching positions in the key and value arrays. Consecutive IDs allow direct addressing:
var op.IntFloatStore scores = op.intFloatStore()
if barstate.isfirst
// Four entries are shown for readability; both arrays may be much larger.
scores.build(
array.from(410, 411, 412, 413),
array.from(0.80, 0.30, 0.95, 0.50))
float selected = scores.get(412)
// Output: integer key 412 resolves to float value 0.95.
Lookup is one-way: get(412) returns 0.95 , but values may repeat, so get(0.95) has no general meaning.
What automatic mode chooses:
Consecutive ascending keys: Direct arithmetic indexing.
Compact key ranges: A dense lookup table.
Other unordered keys: A native map when within Pine's map limit.
Ascending sparse keys: Binary search, or a map within that limit when expectedQueries justifies its build cost.
Linear lookup remains available for unusual workloads that rebuild far more often than they query. Automatic mode only selects it for non-empty stores when linearMaxEntries is deliberately configured.
The same API avoids hashing when direct addressing fits, uses a map when it pays, and remains usable beyond Pine's map capacity. Automatic mode is the normal default. Use op.indexConfigDynamic() when future query volume is unknown and the store may need to promote itself later.
build(keys, values, expectedQueries) accepts two same-length arrays. The optional hint tells OptiPine how many lookups to expect before the next build. Stores support int, float, bool, string and color values. Use one IntIndex for several payload fields, or IntBuckets when a key owns several integers.
In practice: Batch-build IDs to scores, states or metadata, then query them without committing to a representation. Direct integer addressing measured 21% faster than a map, while a map measured 91% faster than repeated linear lookup with 32 entries.
🔸 INTBUCKETS: ONE KEY TO MANY INTEGER VALUES
A Store returns one value for each key. IntBuckets returns a group of integers, usually object IDs or physical slots. Repeating a key adds another member instead of replacing the previous one.
var op.IntBuckets cellMembers = op.intBuckets()
var array matches = array.new()
if barstate.isfirst
// Six (cell, object slot) pairs. Cell 7 appears three times.
array cellKeys = array.from(7, 2, 7, 5, 2, 7)
array objectSlots = array.from(101, 205, 412, 990, 777, 888)
cellMembers.buildFromPairs(cellKeys, objectSlots)
// Read cell 7's group from flat storage. matches is only demo output.
= cellMembers.rangeByKey(7)
if count > 0
for position = start to start + count - 1
matches.push(cellMembers.valueAt(position))
// Output: matches contains , the object slots assigned to cell 7.
What happens: Each key is paired with the slot at the same array position. Cell 7 appears three times, so its group contains 101, 412 and 888. rangeByKey() returns where that group starts and how many values it contains. A missing key returns a count of 0.
Lifecycle: buildFromPairs() replaces all previous groups. Use buildBegin() , add() and buildFinish() only when pairs arrive one at a time.
In practice: A price cell can own several zone slots, a graph node can own several neighbors, or a category can own several record IDs. One query visits only that group.
Why use it: A native map stores one value per key, and Pine does not allow an array directly as that value. Giving one key several values therefore requires a small wrapper UDT containing an array. IntBuckets provides that relationship directly, packing every group into shared contiguous storage. It suits batch rebuilds followed by repeated traversal, while the wrapper approach is more convenient when individual groups change constantly. In the tested 64-key traversal workload, IntBuckets averaged 19% faster across four runs.
----------------------------------------------------------------------------------------------------------------
🔷 REUSE STATE INSTEAD OF REBUILDING IT
IntDoubleBuffer and FloatDoubleBuffer retain current and previous arrays. swap() exchanges their references in O(1), preserves the old result and clears the new current buffer for reuse. That clear still costs O(N).
This is useful when one pass must remain readable while the next is built. In this small search, node n has children 2n and 2n + 1 . Each pass reads the active level and writes the next one:
var op.IntDoubleBuffer searchFrontier = op.intDoubleBuffer()
if barstate.isfirst
searchFrontier.current.push(1)
for depth = 1 to 3
= searchFrontier.swap()
for nodeId in activeFrontier
nextFrontier.push(nodeId * 2)
nextFrontier.push(nodeId * 2 + 1)
// Output: current contains .
// previous contains .
What happens: swap() makes the completed level available as activeFrontier and returns the other retained array, already empty, as nextFrontier . No level is copied and no replacement array is created. The same pattern supports graph searches, flood fills, iterative clustering and simulations. Use swapSized() when every pass needs a fixed-size output.
A var array can also be reused. The ensureSize*() , resize*() and refill*() families modify existing storage, while sameExact*() compares primitive arrays without Pine's float-comparison rounding.
Revision handles caller-owned state that OptiPine cannot observe. The producer calls bump() after a change; each consumer compares its own saved token with changedSince() instead of keeping a snapshot.
----------------------------------------------------------------------------------------------------------------
🔷 WEIGHTED SELECTION FOR STATIC AND DYNAMIC SYSTEMS
Weighted selection chooses entries in proportion to their weights. It is useful in simulations, randomized search and priority sampling.
WeightedSampler is the high-level interface. Set weights, then supply a fraction to select a slot. The sampler does not generate randomness; use math.random() or a repeatable fraction sequence:
var op.WeightedSampler sampler = op.weightedSampler(512)
if barstate.isfirst
sampler.setWeight(10, 0.25)
sampler.setWeight(11, 0.80)
sampler.setWeight(12, 0.10)
float fraction = 0.50
int selected = sampler.sample(fraction)
// Output: selected is 11 for the supplied fraction of 0.50.
The default cumulative prefix suits stable weights. Pass op.weightConfigSparseUpdates() and the sampler can move to an update-friendly Fenwick tree as the workload changes. sample() stays the same. Use WeightedIndex for circular ranges or explicit policy control.
In practice: Each slot can represent a candidate model, simulation outcome or work item. Update its weight when its score changes, then sample repeatedly through the same interface.
----------------------------------------------------------------------------------------------------------------
🔷 THREE LEVELS OF CONTROL
OptiPine is layered so high-level code describes the problem rather than the mechanism. Start with Tier 1 and move deeper only when the workload requires more control:
Tier 1, Quick: Ready-to-use APIs with automatic defaults, including Watch, Memo, CadenceGate, typed stores, StablePool, DirtySet, row rings, double buffers and WeightedSampler.
Tier 2, Composable: Explicit lifecycles, configuration and representation policies through IntIndex, IntBuckets, SlotCache, RingCursor and Revision.
Tier 3, Expert: Physical addressing, unchecked operations and scoped raw mutation for measured hot paths. Ordinary read-only views are not Tier 3.
Editor warnings: Methods such as get() , set() , push() and clear() intentionally match Pine's collection vocabulary. Any shadowing-method warning is cosmetic; the receiver's type determines which method runs.
----------------------------------------------------------------------------------------------------------------
🔷 COMPLETE, COPY-PASTE EXAMPLES
The fragments above isolate one idea at a time. These two copy-paste indicators combine them in practical workflows, using native Pine where it is simpler and OptiPine where it removes real work.
🔸 Complete example 1: high-level cached stress model
What it does: The indicator plots a probability-weighted downside estimate for the current trend and volatility regime, while exposing both regime values in the Data Window.
The EMA and ATR calculations run normally on every bar. Their rounded regimes change less often, so Memo recalculates the 401-scenario model only when one of those regimes changes and serves the cached result between changes.
//@version=6
indicator("OptiPine - Cached Regime Stress", overlay = false)
import Alien_Algorithms/OptiPine/1 as op
// Test 401 possible moves, giving more weight to common moves.
// This function is pure: its result depends only on its inputs.
estimateDownside(float trendInAtr, float atrPercent) =>
float result = na
if not na(trendInAtr) and not na(atrPercent) and atrPercent > 0
float weightedDownside = 0.0
float totalWeight = 0.0
for scenario = -200 to 200
float standardShock = scenario / 40.0
float weight = math.exp(-0.5 * standardShock * standardShock)
float projectedMove = (trendInAtr + standardShock) * atrPercent
float downside = math.max(-projectedMove, 0.0)
weightedDownside += downside * weight
totalWeight += weight
result := totalWeight > 0 ? weightedDownside / totalWeight : na
result
// Stateful Pine calculations stay outside the Memo guard.
float ema20 = ta.ema(close, 20)
float ema50 = ta.ema(close, 50)
float atr14 = ta.atr(14)
float trendInAtr = atr14 > 0 ? (ema20 - ema50) / atr14 : na
float atrPercent = close > 0 ? atr14 / close * 100.0 : na
// Quantization makes the dependencies describe a regime, not every tick.
float trendRegime = math.round(
math.max(-3.0, math.min(3.0, trendInAtr)) * 10.0) / 10.0
float volatilityRegime = math.round(atrPercent * 4.0) / 4.0
var op.FloatMemo downsideStress = op.floatMemo()
if downsideStress.staleOn(trendRegime, volatilityRegime)
downsideStress.store(
estimateDownside(trendRegime, volatilityRegime))
float stress = downsideStress.get()
plot(stress, "Expected downside (%)", color.orange, linewidth = 2)
plot(trendRegime, "Trend regime (ATR units)", display = display.data_window)
plot(volatilityRegime, "Volatility regime (%)", display = display.data_window)
🔸 Complete example 2: advanced zone-cluster engine
What it does: The indicator draws recent pivot levels, thickens those near the current price, plots the strongest price cluster and reports its key statistics in the Data Window.
StablePool preserves drawing slots, the ring tracks retirement order, DirtySet queues redraws, IntBuckets forms price clusters and IntFloatStore looks up their strength.
Relevant benchmarks: These are component results, not a total for this 32-zone indicator. In larger matching workloads, DirtySet saved 93% at 1% dirty and IntBuckets averaged 19% with 64 keys. For typed lookup, direct addressing saved 21% over a map on compact keys, while a map saved 91% over linear search at 32 entries. Automatic mode selects the representation.
StablePool and the ring manage recycling. The script still scans live zones for proximity changes, then DirtySet avoids unnecessary drawing updates.
//@version=6
indicator("OptiPine - Zone Cluster Engine", overlay = true, max_lines_count = 100)
import Alien_Algorithms/OptiPine/1 as op
int pivotLength = input.int(5, "Pivot length", minval = 1)
int maxZones = input.int(32, "Maximum zones", minval = 4, maxval = 100)
int bucketTicks = input.int(25, "Cluster size in ticks", minval = 1)
float bucketSize = syminfo.mintick * bucketTicks
// Stateful Pine calculations remain outside every conditional rebuild.
float pivotHigh = ta.pivothigh(high, pivotLength, pivotLength)
float pivotLow = ta.pivotlow(low, pivotLength, pivotLength)
float pivotStrength = math.max(nz(volume , 1.0), 1.0)
float highlightDistance = ta.atr(14)
var op.StablePool zones = op.stablePool()
var op.IntRowRing zoneOrder = op.intRowRing(maxZones, 1)
var op.DirtySet dirtyZones = op.dirtySet(maxZones)
var array zonePrices = array.new(maxZones, na)
var array zoneStrengths = array.new(maxZones, 0.0)
var array zoneTimes = array.new(maxZones, na)
var array resistance = array.new(maxZones, false)
var array highlighted = array.new(maxZones, false)
var array zoneLines = array.new(maxZones)
var op.IntBuckets zonesByBucket = op.intBuckets()
var op.IntFloatStore strengthByBucket = op.intFloatStore()
// Retained build storage is resized and overwritten, never cleared and repopulated.
var array bucketKeyByPosition = array.new()
var array aggregateKeys = array.new()
var array aggregateStrengths = array.new()
var int strongestBucketKey = na
var float strongestBucketStrength = na
var int strongestZoneCount = 0
dirtyZones.begin()
bool topologyChanged = barstate.isfirst
// Logical pivot IDs receive stable, reusable physical drawing slots.
for event = 0 to 1
float level = event == 0 ? pivotHigh : pivotLow
if barstate.isconfirmed and not na(level)
int pivotBar = bar_index - pivotLength
int pivotTime = time
int zoneId = pivotBar * 2 + event
int slot = zones.find(zoneId)
// Only a new logical pivot enters the retirement queue.
if slot < 0
if zoneOrder.rowCount() == maxZones
int oldestId = zoneOrder.at(0, 0)
zones.release(oldestId)
= zones.acquire(zoneId)
slot := newSlot
zoneOrder.pushValue(zoneId)
zonePrices.set(slot, level)
zoneStrengths.set(slot, pivotStrength)
zoneTimes.set(slot, pivotTime)
resistance.set(slot, event == 0)
highlighted.set(slot, false)
dirtyZones.mark(slot)
topologyChanged := true
// Proximity can mark a newly created slot again; DirtySet still stores it once.
for slot in zones.slots()
bool isHighlighted = math.abs(close - zonePrices.get(slot)) <= highlightDistance
if isHighlighted != highlighted.get(slot)
highlighted.set(slot, isHighlighted)
dirtyZones.mark(slot)
// Only changed drawings cross the line API boundary.
for slot in dirtyZones.values()
float level = zonePrices.get(slot)
color baseColor = resistance.get(slot) ? color.red : color.lime
line zoneLine = zoneLines.get(slot)
if na(zoneLine)
zoneLine := line.new(zoneTimes.get(slot), level, time, level,
xloc = xloc.bar_time)
zoneLines.set(slot, zoneLine)
line.set_xy1(zoneLine, zoneTimes.get(slot), level)
line.set_xy2(zoneLine, time, level)
line.set_extend(zoneLine, extend.right)
line.set_width(zoneLine, highlighted.get(slot) ? 3 : 1)
line.set_color(zoneLine,
color.new(baseColor, highlighted.get(slot) ? 0 : 55))
// Rebuild grouped lookup only after the explicit creation event.
if topologyChanged
array liveSlots = zones.slots()
int liveCount = liveSlots.size()
op.resizeInt(bucketKeyByPosition, liveCount, 0)
if liveCount > 0
for position = 0 to liveCount - 1
int slot = liveSlots.get(position)
int bucketKey = int(math.round(zonePrices.get(slot) / bucketSize))
bucketKeyByPosition.set(position, bucketKey)
// Repeated bucket keys accumulate several physical zone slots.
zonesByBucket.buildFromPairs(bucketKeyByPosition, liveSlots)
int bucketCount = zonesByBucket.bucketCount()
op.resizeInt(aggregateKeys, bucketCount, 0)
op.resizeFloat(aggregateStrengths, bucketCount, 0.0)
strongestBucketKey := na
strongestBucketStrength := na
strongestZoneCount := 0
if bucketCount > 0
for bucketSlot = 0 to bucketCount - 1
int bucketKey = zonesByBucket.keyAt(bucketSlot)
= zonesByBucket.rangeBySlot(bucketSlot)
float totalStrength = 0.0
if count > 0
for position = start to start + count - 1
int zoneSlot = zonesByBucket.valueAt(position)
totalStrength += zoneStrengths.get(zoneSlot)
aggregateKeys.set(bucketSlot, bucketKey)
aggregateStrengths.set(bucketSlot, totalStrength)
if na(strongestBucketStrength) or totalStrength > strongestBucketStrength
strongestBucketKey := bucketKey
strongestBucketStrength := totalStrength
strongestZoneCount := count
strengthByBucket.build(aggregateKeys, aggregateStrengths)
// Query the current price cluster directly and display the strongest cluster.
int currentBucketKey = int(math.round(close / bucketSize))
float nearbyStrength = strengthByBucket.get(currentBucketKey, 0.0)
float strongestClusterPrice = na(strongestBucketKey) ?
na : strongestBucketKey * bucketSize
plot(strongestClusterPrice, "Strongest zone cluster", color.orange,
linewidth = 2, style = plot.style_stepline)
plot(nearbyStrength, "Strength near current price", display = display.data_window)
plot(strongestBucketStrength, "Strongest cluster strength",
display = display.data_window)
plot(strongestZoneCount, "Zones in strongest cluster",
display = display.data_window)
plot(dirtyZones.size(), "Drawings updated", display = display.data_window)
----------------------------------------------------------------------------------------------------------------
🔷 API REFERENCE
This is a compact index of the main public entry points.
🔸 Watch and Memo: changed(source) handles one scalar, primitive array or row ring. For several dependencies, use begin() , watch*() and finish() . Typed Memos add staleOn() , store() , get() and invalidate() .
🔸 Revision and Cadence: revision() exposes bump() , current() and changedSince() for manual change tracking. cadenceGate() provides due() and change-aware dueWhenChanged() scheduling.
🔸 Row Rings: floatRowRing() and intRowRing() provide push() , width-one pushValue() , at() , setAt() , newestAt() , chronological() and gather() .
🔸 RingCursor: Circular addressing for caller-owned arrays. Use reserve() to advance, physical() and logical() to translate positions, and newest() or oldest() to locate retained rows.
🔸 StablePool: acquire() and release() manage stable key-to-slot assignments. Lookup and traversal use find() , contains() , keyAt() , slots() and size() . Recycled slots retain their caller-owned payload until overwritten.
🔸 SlotCache: Frame-based stable allocation follows begin() , acquire() , finish() . active() , retired() and size() expose its state.
🔸 DirtySet: begin() starts a cycle; mark() , markMany() and markRange() add entries. Read the distinct work list with values() and size() .
🔸 Typed Stores: intIntStore() , intFloatStore() , intBoolStore() , intStringStore() and intColorStore() map integer keys to primitive values. Build with build() , then use get() , set() , contains() or getMany() .
🔸 IntIndex: A shared integer key-to-slot directory for custom payloads and explicit lookup policy. Build with buildBegin() , add() or addMany() and buildFinish() ; query with find() , keyAt() and findMany() . IndexConfig controls representation and duplicate policy.
🔸 IntBuckets: A one-key-to-many-integers index. Build directly with buildFromPairs() , or incrementally with buildBegin() , add() or addMany() and buildFinish() . Read groups with rangeByKey() and valueAt() .
🔸 Double Buffers: intDoubleBuffer() and floatDoubleBuffer() retain current and previous arrays. swap() exchanges them; swapSized() also sizes and refills the new current buffer.
🔸 Weighted Sampling: weightedSampler() provides weight updates, sample() , sampleMany() , probability() and total() . It maps caller-supplied fractions; it does not generate randomness. weightedIndex() adds circular ranges and explicit policy control.
🔸 Storage Utilities: ensureSize*() , resize*() , refill*() and sameExact*() handle primitive arrays. Other helpers cover flat/matrix conversion, transposition and bulk ring reads.
----------------------------------------------------------------------------------------------------------------
🔷 WHY OPTIPINE EXISTS
Pine's limits are real, but standard architecture often reaches them long before the idea itself has to. Repeating unchanged calculations, shifting rolling storage, scanning mostly untouched collections and rebuilding state all consume the same execution budget the feature needs to exist.
OptiPine reclaims that budget. Expensive models can run only when their inputs change. Large dashboards can refresh only what moved. Dynamic object systems can grow and recycle storage without reorganizing everything around them. The APIs stay approachable, while the architecture underneath is built for workloads that would normally force a Pine project to scale back.
At large scale, optimization is no longer simply about feature speed. It is the factor that dictates whether an ambitious idea can ship at all.
----------------------------------------------------------------------------------------------------------------
This work is licensed under (CC BY-NC-SA 4.0) , meaning usage is free for non-commercial purposes given that Alien_Algorithms is credited in the description for the underlying software. For commercial use licensing, contact Alien_Algorithms
The publication diagram has been rendered natively by Pine3D .
Библиотека

Индикатор
