← К каталогу
Исходный код · ID 4555

indicator luxy boss mode - support & resistance for tradingview.pine

Вы автор или правообладатель?

Сообщите нам, если считаете, что материал размещён без разрешения.

Получить полный код

Поиск по коду файла

indicator("Luxy BOSS MODE — Support & Resistance", shorttitle="Luxy Support Resistance", overlay=true, max_bars_back=1000, max_boxes_count=500, max_lines_count=500, max_labels_count=500)
import orenluxy/LuxyEnergyIndex/1 as LEI

type SRZone
    int    id
    string source
    float  top
    float  bottom
    float  mid
    int    bar_created
    string htf_source
    bool   is_support
    bool   is_fresh
    int    touches
    int    violations
    int    flip_count
    float  creation_volume
    float  touch_vol_avg
    float  width_atr
    int    last_touch_bar
    int    confluence_count
    float  score
    string grade
    string regime_at_creation
    bool   is_naked_poc
    string catalyst_label
    int    box_id
    int    label_id

    int    bounce_count        // times price bounced at this zone
    int    break_count         // times price broke through this zone
    float  avg_dist_at_touch_atr  // running avg distance from zone mid at touch moment (ATR units)
    string momentum_signal     // real-time: "⬆ BOUNCE" | "⚠ BREAK" | "—"

GROUP_MODE    = "🎯 Trading Mode ═══════════════════════════════════"
GROUP_REGIME  = "🧭 Regime Classifier ══════════════════════════════"
GROUP_ZONES   = "🎯 Zone Detection ═════════════════════════════════"
GROUP_SWING   = "📐 Swing Pivots ════════════════════════════════════"
GROUP_OB      = "🧱 Order Blocks ════════════════════════════════════"
GROUP_VOLPROF = "📊 Volume POC ══════════════════════════════════════"
GROUP_FVG     = "💨 Fair Value Gaps ═════════════════════════════════"
GROUP_LIQ     = "💧 Liquidity Pools ═════════════════════════════════"
GROUP_AVWAP   = "⚓ Anchored VWAP ════════════════════════════════════"
GROUP_SCORE   = "⚖️ Scoring ═════════════════════════════════════════"
GROUP_ASSET   = "🌍 Asset-Class Adaptation ══════════════════════════"
GROUP_WARN    = "🚨 Warnings ════════════════════════════════════════"
GROUP_VISUAL  = "🎨 Visual Settings ═════════════════════════════════"
GROUP_DASH    = "📊 Dashboard ═══════════════════════════════════════"
GROUP_ALERTS  = "🔔 Alerts ══════════════════════════════════════════"

i_trading_mode = input.string("Swing", "Trading Mode", options=["Scalp", "Intraday", "Swing"], group=GROUP_MODE, tooltip="Auto-adjusts zone width, lookback, and sensitivity.\n\nScalp: 5-15m tight zones\nIntraday: 30m-4H balanced\nSwing: 4H-D wide zones")

i_regime_adx_len  = input.int(14, "ADX Length", minval=5, maxval=30, group=GROUP_REGIME, tooltip="ADX period for trend strength")
i_regime_bb_len   = input.int(20, "BB Length", minval=10, maxval=50, group=GROUP_REGIME, tooltip="BB period for squeeze detection")
i_regime_ema_len  = input.int(50, "EMA Slope Length", minval=20, maxval=100, group=GROUP_REGIME, tooltip="EMA period for slope calculation")

i_enable_swing   = input.bool(true,  "📐 Swing Pivots",  group=GROUP_ZONES, inline="det1", tooltip="Detect zones from swing pivots")
i_enable_ob      = input.bool(true,  "🧱 Order Blocks",  group=GROUP_ZONES, inline="det1", tooltip="📐 Swing Pivots — classic swing highs/lows (current TF + Daily + Weekly)\n🧱 Order Blocks — last candle before a strong impulsive move, confirmed by volume")
i_enable_vp      = input.bool(true,  "📊 Volume POC",    group=GROUP_ZONES, inline="det2", tooltip="Detect high-volume cluster zones")
i_enable_fvg     = input.bool(true,  "💨 FVG",           group=GROUP_ZONES, inline="det2", tooltip="📊 Volume POC — highest-volume price cluster (where most contracts traded)\n💨 FVG — Fair Value Gap: 3-candle imbalance where price left unfilled range")
i_enable_liq     = input.bool(true,  "💧 Liquidity",     group=GROUP_ZONES, inline="det3", tooltip="Detect equal-high/low liquidity pools")
i_enable_avwap   = input.bool(true,  "⚓ AVWAP",         group=GROUP_ZONES, inline="det3", tooltip="💧 Liquidity Pools — equal highs/lows where stop orders cluster (sweep targets)\n⚓ AVWAP — Anchored VWAP from gap+volume catalyst events (institutional fair value)")
i_max_zones      = input.int(30, "Max Zones", minval=5, maxval=60, group=GROUP_ZONES, inline="det4", tooltip="Max zones in memory (lowest-score evicted)")

i_swing_left     = input.int(5,  "Left Bars",  minval=2, maxval=20, group=GROUP_SWING, inline="sw1", tooltip="Left bars for pivot confirmation")
i_swing_right    = input.int(5,  "Right Bars", minval=2, maxval=20, group=GROUP_SWING, inline="sw1", tooltip="Left Bars — how many bars to the left must be lower (for high) or higher (for low)\nRight Bars — how many bars to the right confirm the pivot (no-repaint: zones register with delay equal to this value)\n\nDefault 5/5 = solid detection with minimal lag")
i_swing_zone_pct = input.float(0.5, "Zone Width (ATR)", minval=0.05, maxval=1.5, step=0.05, group=GROUP_SWING, tooltip="Zone box height as a fraction of ATR.\n\n0.25 = zone spans 25% of ATR\n0.5  = wider zones, good for daily charts (default)\n0.1  = tight zones, good for intraday\n\nATR = Average True Range (daily volatility)")

i_ob_lookback    = input.int(60, "OB Lookback", minval=5, maxval=100, group=GROUP_OB, tooltip="Bars to scan for order block formations")
i_ob_min_move    = input.float(1.5, "Min Move %", minval=0.5, maxval=10.0, step=0.25, group=GROUP_OB, tooltip="Minimum impulsive move % to qualify OB")
i_ob_vol_mult    = input.float(1.5, "Volume Mult", minval=1.0, maxval=5.0, step=0.1, group=GROUP_OB, tooltip="Min volume ratio vs average at formation")

i_vp_lookback    = input.int(100, "POC Lookback", minval=20, maxval=200, group=GROUP_VOLPROF, tooltip="Bars for volume-at-price histogram")
i_vp_num_bins    = input.int(20, "Price Bins",   minval=10, maxval=40, group=GROUP_VOLPROF, tooltip="Price resolution for histogram")

i_fvg_min_size   = input.float(0.3, "Min FVG Size %", minval=0.1, maxval=3.0, step=0.1, group=GROUP_FVG, tooltip="Minimum gap size as percent of price")
i_fvg_show_filled = input.bool(false, "Keep Filled FVGs", group=GROUP_FVG, tooltip="Keep showing FVG zones after price fills them")

i_liq_length     = input.int(20, "Swing Length", minval=3, maxval=30, group=GROUP_LIQ, tooltip="Bars to look back for equal high/low clusters")
i_liq_tolerance  = input.float(0.1, "Touch Tolerance %", minval=0.05, maxval=0.5, step=0.05, group=GROUP_LIQ, tooltip="Price tolerance (%) to consider two highs/lows as equal")

i_avwap_auto     = input.bool(true, "Auto-Anchor on Gap+Volume", group=GROUP_AVWAP, tooltip="Auto-anchor VWAP when gap > 3% and volume > 3x average")
i_avwap_gap_pct  = input.float(3.0, "Auto-Anchor Gap %", minval=1.0, maxval=20.0, step=0.5, group=GROUP_AVWAP, tooltip="Minimum gap % to trigger auto-anchor")
i_avwap_vol_mult = input.float(3.0, "Auto-Anchor Vol Mult", minval=1.5, maxval=10.0, step=0.5, group=GROUP_AVWAP, tooltip="Min volume multiplier to trigger auto-anchor")
i_avwap_date     = input.time(0, "Custom Catalyst Date", group=GROUP_AVWAP, confirm=false, tooltip="Set a specific catalyst date (earnings, FDA, halving, FOMC)\nLeave at 0 to disable manual anchor")

i_w_touch_vol    = input.int(18, "Touch Volume Weight",    minval=0, maxval=30, group=GROUP_SCORE, tooltip="Per-touch volume conviction (unique vs competitors)")
i_w_confluence   = input.int(18, "Confluence Weight",      minval=0, maxval=30, group=GROUP_SCORE, tooltip="Multi-source zone overlap score")
i_w_htf          = input.int(16, "HTF Weight",             minval=0, maxval=30, group=GROUP_SCORE, tooltip="Higher timeframe origin bonus")
i_w_touch        = input.int(12, "Touch Count Weight",     minval=0, maxval=30, group=GROUP_SCORE, tooltip="Number of price reactions")
i_w_create_vol   = input.int(12, "Creation Volume Weight", minval=0, maxval=30, group=GROUP_SCORE, tooltip="Volume at zone creation")
i_w_regime       = input.int(12, "Regime Fit Weight",      minval=0, maxval=30, group=GROUP_SCORE, tooltip="Alignment with market regime direction")
i_w_fresh        = input.int(8,  "Freshness Weight",       minval=0, maxval=20, group=GROUP_SCORE, tooltip="Untouched or recently touched bonus")

i_asset_override = input.string("Auto", "Asset Class", options=["Auto", "Stock", "Crypto", "Forex", "Index", "Futures"], group=GROUP_ASSET, tooltip="Auto-detects asset class.\n\nCrypto: 24/7, percent bands\nForex: pip widths, session zones\nIndex: volume-agnostic fallback\nFutures: rollover-aware")
i_sub5_override  = input.bool(false, "Force Sub-$5 Mode", group=GROUP_ASSET, tooltip="Forces percent-band zone widths for low-price securities")

i_warn_approach  = input.bool(true, "Zone Approach",    group=GROUP_WARN, inline="w1", tooltip="Alert when within 0.5 ATR of Grade A/B zone")
i_warn_cluster   = input.bool(true, "Zone Cluster",     group=GROUP_WARN, inline="w1", tooltip="Zone Approach — fires when price is within the ATR distance you set below of any Grade A or B zone\nZone Cluster — fires when 3 or more zones overlap within 1 ATR of current price (clustered S/R = strong area)")
i_warn_sweep     = input.bool(true, "Sweep Imminent",   group=GROUP_WARN, inline="w2", tooltip="Alert when approaching liquidity pool")
i_warn_fvg       = input.bool(true, "FVG Filling",      group=GROUP_WARN, inline="w2", tooltip="Sweep Imminent — price is within 0.3 ATR of a Liquidity Pool zone AND volume is accelerating (likely stop hunt)\nFVG Filling — price has entered an unfilled Fair Value Gap zone for the first time")
i_warn_trap      = input.bool(true, "Trap Suspected",   group=GROUP_WARN, inline="w3", tooltip="Alert when close-through is low-volume (likely fake)")
i_warn_regime    = input.bool(true, "Regime Shift",     group=GROUP_WARN, inline="w3", tooltip="Trap Suspected — price closed through a zone on below-average volume (fake breakout pattern — often reverses)\nRegime Shift — market regime just changed direction AND a Grade-A zone is nearby (high-significance event)")
i_warn_atr_dist  = input.float(0.5, "Approach Distance (ATR)", minval=0.1, maxval=2.0, step=0.1, group=GROUP_WARN, tooltip="ATR distance to trigger ZONE_APPROACH warning")

i_top_n_zones    = input.int(10, "Show Top N Zones", minval=3, maxval=20, group=GROUP_VISUAL, inline="vis1", tooltip="Show only N highest-scoring zones")
i_adaptive_hide  = input.bool(true, "Adaptive Hide",  group=GROUP_VISUAL, inline="vis1", tooltip="Show Top N — render only the N highest-scored zones (sorted by score 0-100). Reduces clutter. Default 10.\nAdaptive Hide — automatically hides zones more than 2.5 ATR from current price. Keeps chart clean as price moves.")
i_show_grade_d   = input.bool(false, "Show Grade D",  group=GROUP_VISUAL, inline="vis2", tooltip="Include low-quality zones in rendering")
i_zone_transp    = input.int(70, "Transparency",      minval=30, maxval=90, group=GROUP_VISUAL, inline="vis2", tooltip="Show Grade D — include very weak zones (score <50). OFF by default — they add clutter.\nTransparency — box fill opacity. 30 = nearly opaque (vivid), 90 = almost invisible. Default 70.")
i_show_simple    = input.bool(true, "Simple S/R Lines on Chart", group=GROUP_VISUAL, tooltip="Show clear horizontal lines for nearest Support and Resistance directly on chart.\n\nGreen line = nearest SUPPORT below price\nRed line = nearest RESISTANCE above price\nLabel shows price + ATR distance\n\nFor quick reading — no detailed analysis needed.")

i_show_dash      = input.bool(true, "Show Dashboard", group=GROUP_DASH)
i_show_legend    = input.bool(true, "Show Legend",    group=GROUP_DASH, tooltip="Show a small legend explaining zone symbols and colors")
i_dash_pos       = input.string("Top Right", "Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group=GROUP_DASH, inline="dash")
i_dash_size      = input.string("Normal", "Size", options=["Auto", "Tiny", "Small", "Normal", "Large", "Huge"], group=GROUP_DASH, inline="dash", tooltip="Position — where the dashboard table appears on the chart (9 options)\nSize — text size inside the dashboard. Normal = default readable. Large if chart is large, Tiny if you want minimal footprint.")

i_alert_json     = input.bool(false, "Structured JSON Alerts", group=GROUP_ALERTS, tooltip="Include JSON payload in alerts for webhook automation\n\n{brand, ticker, tf, price, atr, regime, warning, zone{id,source,score,grade,confluence}}")

GROUP_TRADE = "💰 Trade Plan ══════════════════════════════════════"
i_enable_trade_plan = input.bool(true, "Enable Trade Plan", group=GROUP_TRADE, tooltip="Automatically calculates Entry, Stop Loss, Target, and Position Size for the best setup near price.\n\nOnly shows trades that meet the minimum R:R requirement.\n\nLONG = price approaching support from above\nSHORT = price approaching resistance from below")
i_account_size      = input.float(10000, "Account Size ($)", minval=100, maxval=10000000, step=1000, group=GROUP_TRADE, tooltip="Total account value in dollars.\n\nUsed to calculate how many shares to buy based on your risk per trade.\n\nExample: $10,000 account × 1% risk = $100 risk per trade")
i_risk_pct          = input.float(1.0, "Risk per Trade (%)", minval=0.1, maxval=5.0, step=0.1, group=GROUP_TRADE, tooltip="How much of your account to risk on each trade.\n\n1.0% = standard professional\n0.5% = conservative\n2.0% = aggressive\n\nFormula: (Account × Risk%) / (Entry - Stop) = Shares")
i_min_rr            = input.float(2.0, "Min R:R to Show", minval=1.0, maxval=5.0, step=0.5, group=GROUP_TRADE, tooltip="Minimum Risk:Reward ratio to display a trade plan.\n\nOnly setups where Target gives this R:R or better will be shown.\n\n2.0 = target must be 2× the stop distance away\n1.5 = looser filter, more setups shown")

var array<SRZone> g_zones       = array.new<SRZone>()
var array<int>    g_zone_id_arr = array.from(0)
var string        g_warning  = "—"
var int           g_warn_bar = 0
var float         g_avwap_anchor_price = na
var float         g_avwap_cum_pv = 0.0
var float         g_avwap_cum_v  = 0.0
var int           g_avwap_anchor_bar = -1
var string        g_avwap_label = ""
var int           g_hits_total  = 0
var int           g_tests_total = 0

var float g_lb_vol   = 0.0   // sum of touch_vol_avg on BOUNCE events
var float g_lb_htf   = 0.0   // sum of htf_n on BOUNCE events
var float g_lb_conf  = 0.0   // sum of confluence_n on BOUNCE events
var float g_lb_fresh = 0.0   // sum of fresh_n on BOUNCE events
var float g_lbr_vol  = 0.0   // sum of touch_vol_avg on BREAK events
var float g_lbr_htf  = 0.0
var float g_lbr_conf = 0.0
var float g_lbr_fresh = 0.0
var int   g_n_bounce = 0     // total bounce events observed
var int   g_n_break  = 0     // total break events observed

var string g_tp_dir    = "—"
var float  g_tp_entry  = na
var float  g_tp_stop   = na
var float  g_tp_target = na
var float  g_tp_rr     = na
var int    g_tp_shares = 0
var float  g_tp_score  = 0.0
var line   tp_entry_ln = na
var line   tp_stop_ln  = na
var line   tp_tgt_ln   = na
var label  tp_plan_lbl = na

get_table_pos(p) =>
    p == "Top Left" ? position.top_left : p == "Top Center" ? position.top_center : p == "Top Right" ? position.top_right : p == "Middle Left" ? position.middle_left : p == "Middle Center" ? position.middle_center : p == "Middle Right" ? position.middle_right : p == "Bottom Left" ? position.bottom_left : p == "Bottom Center" ? position.bottom_center : position.bottom_right

get_text_size(s) =>
    s == "Auto" ? size.auto : s == "Tiny" ? size.tiny : s == "Small" ? size.small : s == "Normal" ? size.normal : s == "Large" ? size.large : size.huge

get_regime_color(r) =>
    r == "STRONG_BULL" ? color.new(#26a69a, 20) : r == "WEAK_BULL" ? color.new(#66bb6a, 30) : r == "SQUEEZE" ? color.new(#ab47bc, 20) : r == "STRONG_BEAR" ? color.new(#ef5350, 20) : r == "WEAK_BEAR" ? color.new(#ff7043, 30) : color.new(color.gray, 30)

get_heat_color(score, is_sup, transp) =>
    if is_sup
        score >= 80 ? color.new(#00e5ff, transp) : score >= 65 ? color.new(#00c853, transp) : score >= 50 ? color.new(#40c4ff, transp) : score >= 35 ? color.new(#80deea, transp) : color.new(#546e7a, transp)
    else
        score >= 80 ? color.new(#ff1744, transp) : score >= 65 ? color.new(#ff5722, transp) : score >= 50 ? color.new(#ff9800, transp) : score >= 35 ? color.new(#ef9a9a, transp) : color.new(#78909c, transp)

get_border_color_grade(grade) =>
    grade == "A" ? color.new(#ffffff, 5) : grade == "B" ? color.new(#e0e0e0, 35) : color.new(color.gray, 60)

get_border_w(htf) => htf == "W" or htf == "M" ? 3 : htf == "D" ? 2 : 1
get_grade(s) => s >= 80 ? "A" : s >= 65 ? "B" : s >= 50 ? "C" : "D"
get_src_emoji(src) => src == "SWING" ? "📐" : src == "OB" ? "🧱" : src == "POC" ? "📊" : src == "FVG" ? "💨" : src == "LIQ" ? "💧" : "⚓"

calc_adaptive_w(base_w, b_sum, br_sum, n_b, n_br) =>
    float aw = base_w
    if n_b >= 10 and n_br >= 10
        b_avg  = b_sum  / n_b
        br_avg = br_sum / n_br
        ratio  = b_avg / math.max(br_avg, 0.0001)

        aw    := math.max(base_w * 0.4, math.min(base_w * 2.5, base_w * math.pow(ratio, 0.6)))
    aw

classify_momentum(z_is_sup, dist_atr, z_touches, lei_val, vol_r, spd) =>
    string sig = "—"
    if dist_atr <= 1.2 and z_touches > 0

        approaching = z_is_sup ? (close < close[2]) : (close > close[2])
        fast_hi  = spd > 0.40 and vol_r > 1.50
        slow_low = spd < 0.20 and vol_r < 0.80
        if not na(lei_val)

            if lei_val >= 70
                sig := "⬆ BOUNCE"    // Exhaustion zone: move depleted, bounce expected
            else if lei_val <= 30

                sig := approaching ? "⚠ BREAK" : "⬆ BOUNCE"
            else if slow_low          // Neutral LEI — fallback to speed/volume
                sig := "⬆ BOUNCE"
            else if fast_hi
                sig := approaching ? "⚠ BREAK" : "—"
        else
            if slow_low
                sig := "⬆ BOUNCE"
            else if fast_hi
                sig := approaching ? "⚠ BREAK" : "—"
    sig

detect_asset_class() =>
    if i_asset_override != "Auto"
        i_asset_override
    else
        t = syminfo.type
        t == "crypto" ? "Crypto" : t == "forex" or t == "spread" ? "Forex" : t == "index" ? "Index" : t == "futures" or t == "economic" ? "Futures" : "Stock"

var string g_asset   = "Stock"
var bool   g_sub5    = false
var bool   g_crypto  = false
var bool   g_forex   = false
var bool   g_index   = false
var string g_session = "RTH"

if barstate.isfirst
    g_asset  := detect_asset_class()
    g_sub5   := i_sub5_override or (g_asset == "Stock" and close < 1.0)
    g_crypto := g_asset == "Crypto"
    g_forex  := g_asset == "Forex"
    g_index  := g_asset == "Index"

if g_asset == "Stock" or g_asset == "Futures"
    session_regular = not na(time("1", "0930-1600:23456", "America/New_York"))
    session_pre     = not na(time("1", "0400-0930:23456", "America/New_York"))
    g_session := session_regular ? "RTH" : session_pre ? "PRE" : "AH"

get_zone_width(atr_v) =>
    g_sub5 ? close * i_swing_zone_pct * 0.01 : atr_v * i_swing_zone_pct

atr_val              = ta.atr(14)
[bb_mid, bb_up, bb_dn] = ta.bb(close, i_regime_bb_len, 2.0)
bbw                  = (bb_up - bb_dn) / bb_mid * 100.0
bbw_rank             = ta.percentrank(bbw, 100)
atr_pct              = atr_val / close * 100.0
atr_rank             = ta.percentrank(atr_pct, 100)
[dmi_plus, dmi_minus, adx_val] = ta.dmi(i_regime_adx_len, i_regime_adx_len)
ema50                = ta.ema(close, i_regime_ema_len)
ema_slope            = (ema50 - ema50[5]) / ema50[5] * 100.0
is_squeeze           = bbw_rank < 10 and atr_rank < 15

regime_state = is_squeeze ? "SQUEEZE" : adx_val > 25 and ema_slope > 0.5 ? "STRONG_BULL" : adx_val > 18 and ema_slope > 0.1 ? "WEAK_BULL" : adx_val > 25 and ema_slope < -0.5 ? "STRONG_BEAR" : adx_val > 18 and ema_slope < -0.1 ? "WEAK_BEAR" : "RANGE"
var string prev_regime = "RANGE"

htf_d_v  = request.security(syminfo.tickerid, "D", volume, lookahead=barmerge.lookahead_off)
htf_w_v  = request.security(syminfo.tickerid, "W", volume, lookahead=barmerge.lookahead_off)
htf_d_ph = request.security(syminfo.tickerid, "D", ta.pivothigh(high, i_swing_left, i_swing_right), lookahead=barmerge.lookahead_off)
htf_d_pl = request.security(syminfo.tickerid, "D", ta.pivotlow(low,   i_swing_left, i_swing_right), lookahead=barmerge.lookahead_off)
htf_w_ph = request.security(syminfo.tickerid, "W", ta.pivothigh(high, i_swing_left, i_swing_right), lookahead=barmerge.lookahead_off)
htf_w_pl = request.security(syminfo.tickerid, "W", ta.pivotlow(low,   i_swing_left, i_swing_right), lookahead=barmerge.lookahead_off)

avg_vol_20   = ta.sma(volume, 20)

lei_value    = LEI.lei(close)
lei_zone_str = LEI.getZone(lei_value)
approach_spd = math.abs(close - close[2]) / math.max(atr_val, 0.0001)
vol_ratio    = volume / math.max(avg_vol_20, 1)

zone_exists_near(price_level, half_width) =>
    found = false
    n = array.size(g_zones)
    if n > 0
        for i = 0 to n - 1
            z = array.get(g_zones, i)
            if math.abs(z.mid - price_level) < half_width * 2
                found := true
                break
    found

push_zone(src, z_top, z_bot, htf, supp, vol_at_creation, cat_label) =>
    if array.size(g_zones) >= i_max_zones
        min_idx   = 0
        min_score = 999.0
        for i = 0 to array.size(g_zones) - 1
            zs = array.get(g_zones, i).score
            if zs < min_score
                min_score := zs
                min_idx   := i
        array.remove(g_zones, min_idx)
    cur_id = array.get(g_zone_id_arr, 0) + 1
    array.set(g_zone_id_arr, 0, cur_id)
    vol_norm = avg_vol_20 > 0 ? vol_at_creation / avg_vol_20 : 1.0
    nz = SRZone.new(id=cur_id, source=src, top=z_top, bottom=z_bot, mid=(z_top+z_bot)/2, bar_created=bar_index, htf_source=htf, is_support=supp, is_fresh=true, touches=0, violations=0, flip_count=0, creation_volume=vol_norm, touch_vol_avg=0.0, width_atr=(z_top-z_bot)/math.max(atr_val,0.0001), last_touch_bar=bar_index, confluence_count=1, score=50.0, grade="C", regime_at_creation=regime_state, is_naked_poc=false, catalyst_label=cat_label, box_id=-1, label_id=-1, bounce_count=0, break_count=0, avg_dist_at_touch_atr=0.0, momentum_signal="—")
    array.push(g_zones, nz)

if i_enable_swing
    ph_curr = ta.pivothigh(high, i_swing_left, i_swing_right)
    pl_curr = ta.pivotlow(low,   i_swing_left, i_swing_right)
    zw      = get_zone_width(atr_val)
    if not na(ph_curr) and not zone_exists_near(ph_curr, zw)
        push_zone("SWING", ph_curr + zw * 0.5, ph_curr - zw * 0.5, timeframe.period, false, volume[i_swing_right], "")
    if not na(pl_curr) and not zone_exists_near(pl_curr, zw)
        push_zone("SWING", pl_curr + zw * 0.5, pl_curr - zw * 0.5, timeframe.period, true, volume[i_swing_right], "")
    zw_d = zw * 1.5
    if not na(htf_d_ph) and not zone_exists_near(htf_d_ph, zw_d)
        push_zone("SWING", htf_d_ph + zw_d * 0.5, htf_d_ph - zw_d * 0.5, "D", false, htf_d_v, "")
    if not na(htf_d_pl) and not zone_exists_near(htf_d_pl, zw_d)
        push_zone("SWING", htf_d_pl + zw_d * 0.5, htf_d_pl - zw_d * 0.5, "D", true, htf_d_v, "")
    zw_w = zw * 2.0
    if not na(htf_w_ph) and not zone_exists_near(htf_w_ph, zw_w)
        push_zone("SWING", htf_w_ph + zw_w * 0.5, htf_w_ph - zw_w * 0.5, "W", false, htf_w_v, "")
    if not na(htf_w_pl) and not zone_exists_near(htf_w_pl, zw_w)
        push_zone("SWING", htf_w_pl + zw_w * 0.5, htf_w_pl - zw_w * 0.5, "W", true, htf_w_v, "")

if i_enable_ob and bar_index >= i_ob_lookback
    avg_vol_ob = ta.sma(volume, i_ob_lookback)
    is_bear1   = close[1] < open[1]
    bull_move  = (close - open[1]) / math.max(open[1], 0.0001) * 100 >= i_ob_min_move
    vol_bull   = volume >= avg_vol_ob * i_ob_vol_mult

    bull_disp  = close > math.max(high[2], math.max(high[3], math.max(high[4], high[5])))
    if is_bear1 and bull_move and vol_bull and bull_disp
        ob_t = math.max(open[1], close[1])
        ob_b = math.min(open[1], close[1])
        if not zone_exists_near((ob_t + ob_b) / 2, ob_t - ob_b)
            push_zone("OB", ob_t, ob_b, timeframe.period, true, volume[1], "")
    is_bull1  = close[1] > open[1]
    bear_move = (open[1] - close) / math.max(open[1], 0.0001) * 100 >= i_ob_min_move
    vol_bear  = volume >= avg_vol_ob * i_ob_vol_mult

    bear_disp  = close < math.min(low[2], math.min(low[3], math.min(low[4], low[5])))
    if is_bull1 and bear_move and vol_bear and bear_disp
        ob_t = math.max(open[1], close[1])
        ob_b = math.min(open[1], close[1])
        if not zone_exists_near((ob_t + ob_b) / 2, ob_t - ob_b)
            push_zone("OB", ob_t, ob_b, timeframe.period, false, volume[1], "")

if i_enable_vp and bar_index >= i_vp_lookback and bar_index % 5 == 0
    vp_h = ta.highest(high, i_vp_lookback)
    vp_l = ta.lowest(low,   i_vp_lookback)
    bsz  = (vp_h - vp_l) / i_vp_num_bins
    if bsz > 0
        max_bv  = 0.0
        poc_bin = 0
        for b = 0 to i_vp_num_bins - 1
            bv  = 0.0
            bb  = vp_l + b * bsz
            bt  = bb + bsz
            for k = 0 to i_vp_lookback - 1
                bm = (high[k] + low[k]) / 2
                if bm >= bb and bm < bt
                    bv += volume[k]
            if bv > max_bv
                max_bv  := bv
                poc_bin := b
        poc_price = vp_l + (poc_bin + 0.5) * bsz
        if not zone_exists_near(poc_price, bsz * 2)
            push_zone("POC", poc_price + bsz * 0.5, poc_price - bsz * 0.5, timeframe.period, close < poc_price, max_bv, "")

if i_enable_fvg and bar_index >= 2
    min_gap = close * i_fvg_min_size / 100.0

    bull_fvg_top = low[0]
    bull_fvg_bot = high[2]

    bull_mid_impulsive = close[1] > open[1] and (close[1] - open[1]) >= atr_val * 0.3
    if bull_fvg_top > bull_fvg_bot and bull_fvg_top - bull_fvg_bot >= min_gap and bull_mid_impulsive
        fvg_mid = (bull_fvg_top + bull_fvg_bot) / 2
        if not zone_exists_near(fvg_mid, (bull_fvg_top - bull_fvg_bot) * 0.5)
            push_zone("FVG", bull_fvg_top, bull_fvg_bot, timeframe.period, true, volume[1], "")

    bear_fvg_top = low[2]
    bear_fvg_bot = high[0]

    bear_mid_impulsive = close[1] < open[1] and (open[1] - close[1]) >= atr_val * 0.3
    if bear_fvg_top > bear_fvg_bot and bear_fvg_top - bear_fvg_bot >= min_gap and bear_mid_impulsive
        fvg_mid = (bear_fvg_top + bear_fvg_bot) / 2
        if not zone_exists_near(fvg_mid, (bear_fvg_top - bear_fvg_bot) * 0.5)
            push_zone("FVG", bear_fvg_top, bear_fvg_bot, timeframe.period, false, volume[1], "")

if i_enable_liq and bar_index >= i_liq_length
    tol      = close * i_liq_tolerance / 100.0
    liq_h    = ta.highest(high, i_liq_length)
    liq_l    = ta.lowest(low,   i_liq_length)

    eq_h_count = 0
    eq_l_count = 0
    for k = 0 to i_liq_length - 1
        if math.abs(high[k] - liq_h) <= tol
            eq_h_count += 1
        if math.abs(low[k] - liq_l) <= tol
            eq_l_count += 1

    if eq_h_count >= 2 and not zone_exists_near(liq_h, tol * 2)
        push_zone("LIQ", liq_h + tol, liq_h - tol, timeframe.period, false, ta.sma(volume, i_liq_length), "")
    if eq_l_count >= 2 and not zone_exists_near(liq_l, tol * 2)
        push_zone("LIQ", liq_l + tol, liq_l - tol, timeframe.period, true, ta.sma(volume, i_liq_length), "")

gap_pct = bar_index > 0 ? math.abs(open - close[1]) / math.max(close[1], 0.0001) * 100 : 0.0
auto_anchor_trigger = i_enable_avwap and i_avwap_auto and gap_pct >= i_avwap_gap_pct and volume >= avg_vol_20 * i_avwap_vol_mult

manual_anchor_trigger = i_enable_avwap and i_avwap_date > 0 and time >= i_avwap_date and time[1] < i_avwap_date

if auto_anchor_trigger or manual_anchor_trigger
    g_avwap_anchor_bar   := bar_index
    g_avwap_cum_pv       := 0.0
    g_avwap_cum_v        := 0.0
    g_avwap_anchor_price := open
    g_avwap_label        := auto_anchor_trigger ? "Gap+" + str.tostring(math.round(gap_pct, 1)) + "%" : "Catalyst"

if g_avwap_anchor_bar >= 0 and bar_index >= g_avwap_anchor_bar
    typ_p              = (high + low + close) / 3
    g_avwap_cum_pv    += typ_p * volume
    g_avwap_cum_v     += volume

avwap_value = g_avwap_cum_v > 0 ? g_avwap_cum_pv / g_avwap_cum_v : na

if i_enable_avwap and not na(avwap_value) and bar_index % 10 == 0
    zw_av = get_zone_width(atr_val) * 0.8
    if not zone_exists_near(avwap_value, zw_av * 3)
        push_zone("AVWAP", avwap_value + zw_av * 0.5, avwap_value - zw_av * 0.5, timeframe.period, close < avwap_value, volume, g_avwap_label)

plot(avwap_value, "AVWAP", color=color.new(#ab47bc, 30), linewidth=2, style=plot.style_line)

var label avwap_label = na
if barstate.islast
    label.delete(avwap_label)
    if not na(avwap_value)
        avwap_label := label.new(bar_index + 3, avwap_value, "⚓ AVWAP  " + g_avwap_label, color=color.new(#ab47bc, 10), textcolor=color.white, style=label.style_label_left, size=size.small, tooltip="AVWAP = Anchored Volume-Weighted Average Price\n\nThis PURPLE LINE is the institutional fair value anchor.\nIt starts from: " + g_avwap_label + "\n\nPrice ABOVE AVWAP → trading at a premium (expensive)\nPrice BELOW AVWAP → trading at a discount (cheap)\n\nInstitutions use AVWAP as a reference price.\nZones near AVWAP are especially strong.\n\n── Why it matters ──\nWhen price returns to AVWAP = fair value magnet\nAVWAP breaks = significant shift in sentiment")

if array.size(g_zones) > 0
    for i = array.size(g_zones) - 1 to 0
        z           = array.get(g_zones, i)
        in_zone_now = low <= z.top and high >= z.bottom
        close_above = close > z.top
        close_below = close < z.bottom

        if in_zone_now and bar_index > z.bar_created
            z.is_fresh      := false
            z.touches       += 1
            tvr              = avg_vol_20 > 0 ? volume / avg_vol_20 : 1.0
            z.touch_vol_avg := z.touches == 1 ? tvr : (z.touch_vol_avg * (z.touches - 1) + tvr) / z.touches
            z.last_touch_bar := bar_index
            g_tests_total   += 1
            bounced = z.is_support ? close > z.mid : close < z.mid

            if bounced
                g_hits_total   += 1
                is_first_bounce = z.bounce_count == 0  // capture before incrementing
                z.bounce_count += 1

                b_dist = math.abs(close - z.mid) / math.max(atr_val, 0.0001)
                z.avg_dist_at_touch_atr := z.bounce_count == 1 ? b_dist : (z.avg_dist_at_touch_atr * (z.bounce_count - 1) + b_dist) / z.bounce_count

                if is_first_bounce
                    htf_f   = z.htf_source == "W" ? 1.0 : z.htf_source == "D" ? 0.8 : 0.4
                    conf_f  = math.min(z.confluence_count, 5) / 5.0
                    fresh_f = z.is_fresh ? 1.0 : math.max(0.0, 1.0 - float(bar_index - z.last_touch_bar) / 300.0)
                    g_lb_vol   += z.touch_vol_avg
                    g_lb_htf   += htf_f
                    g_lb_conf  += conf_f
                    g_lb_fresh += fresh_f
                    g_n_bounce += 1

        z.momentum_signal := classify_momentum(z.is_support, math.abs(z.mid - close) / math.max(atr_val, 0.0001), z.touches, lei_value, vol_ratio, approach_spd)

        was_violated = z.is_support ? close_below : close_above
        if was_violated
            z.violations += 1
            if z.violations == 1

                z.break_count += 1
                htf_f   = z.htf_source == "W" ? 1.0 : z.htf_source == "D" ? 0.8 : 0.4
                conf_f  = math.min(z.confluence_count, 5) / 5.0
                fresh_f = z.is_fresh ? 1.0 : math.max(0.0, 1.0 - float(bar_index - z.last_touch_bar) / 300.0)
                g_lbr_vol   += z.touch_vol_avg
                g_lbr_htf   += htf_f
                g_lbr_conf  += conf_f
                g_lbr_fresh += fresh_f
                g_n_break   += 1
            if z.violations >= 2
                z.is_support := not z.is_support
                z.flip_count += 1

        if z.violations >= 3 or (z.source == "FVG" and not i_fvg_show_filled and z.touches >= 1)
            array.remove(g_zones, i)
        else
            array.set(g_zones, i, z)

n_zones = array.size(g_zones)
if n_zones > 0
    for i = 0 to n_zones - 1
        zi   = array.get(g_zones, i)
        conf = 1
        for j = 0 to n_zones - 1
            if i != j
                zj = array.get(g_zones, j)
                if zi.top >= zj.bottom and zi.bottom <= zj.top
                    conf += 1
        zi.confluence_count := conf
        array.set(g_zones, i, zi)

aw_vol   = calc_adaptive_w(i_w_touch_vol,  g_lb_vol,   g_lbr_vol,   g_n_bounce, g_n_break)
aw_htf   = calc_adaptive_w(i_w_htf,        g_lb_htf,   g_lbr_htf,   g_n_bounce, g_n_break)
aw_conf  = calc_adaptive_w(i_w_confluence, g_lb_conf,  g_lbr_conf,  g_n_bounce, g_n_break)
aw_fresh = calc_adaptive_w(i_w_fresh,      g_lb_fresh, g_lbr_fresh, g_n_bounce, g_n_break)
total_w  = aw_vol + aw_conf + aw_htf + i_w_touch + i_w_create_vol + i_w_regime + aw_fresh

n_score = array.size(g_zones)
if n_score > 0
    for i = 0 to n_score - 1
        z         = array.get(g_zones, i)
        touch_n   = math.min(z.touches, 5) / 5.0
        vol_conv  = math.min(z.touch_vol_avg, 3.0) / 3.0
        create_vn = math.min(z.creation_volume, 3.0) / 3.0
        conf_n    = math.min(z.confluence_count, 5) / 5.0
        fresh_n   = z.is_fresh ? 1.0 : math.max(0.0, 1.0 - float(bar_index - z.last_touch_bar) / 300.0)
        htf_n     = z.htf_source == "W" ? 1.0 : z.htf_source == "D" ? 0.8 : z.htf_source == "240" ? 0.6 : 0.3
        viol_pen  = math.min(z.violations * 0.25, 1.0)
        rf        = 0.5
        if z.is_support
            rf := regime_state == "STRONG_BULL" ? 1.0 : regime_state == "WEAK_BULL" ? 0.8 : regime_state == "RANGE" ? 0.5 : regime_state == "SQUEEZE" ? 0.6 : 0.2
        else
            rf := regime_state == "STRONG_BEAR" ? 1.0 : regime_state == "WEAK_BEAR" ? 0.8 : regime_state == "RANGE" ? 0.5 : regime_state == "SQUEEZE" ? 0.6 : 0.2

        stat_bonus = z.bounce_count + z.break_count > 4 ? (float(z.bounce_count) / math.max(z.bounce_count + z.break_count, 1) - 0.5) * 10.0 : 0.0
        raw = (touch_n * i_w_touch + vol_conv * aw_vol + create_vn * i_w_create_vol + htf_n * aw_htf + conf_n * aw_conf + fresh_n * aw_fresh + rf * i_w_regime - viol_pen * 6.0) / math.max(total_w, 1) * 100.0 + stat_bonus
        z.score  := math.max(0.0, math.min(100.0, raw))
        z.grade  := get_grade(z.score)   // grade ALWAYS derived from final score — no sync bug
        array.set(g_zones, i, z)

var string active_warning    = "—"
var string warning_severity  = "INFO"
var int    warning_zone_id   = -1
var string warning_zone_src  = ""
var float  warning_zone_score = 0.0
var string warning_zone_grade = ""

regime_changed = regime_state != prev_regime
prev_regime   := regime_state

active_warning   := "—"
warning_severity := "INFO"

if barstate.isconfirmed
    n_warn = array.size(g_zones)
    if n_warn > 0
        for i = 0 to n_warn - 1
            z    = array.get(g_zones, i)
            dist = math.abs(z.mid - close)
            if i_warn_regime and regime_changed and dist < atr_val * 1.5 and z.grade == "A"
                active_warning     := "REGIME_SHIFT"
                warning_severity   := "HIGH"
                warning_zone_id    := z.id
                warning_zone_src   := z.source
                warning_zone_score := z.score
                warning_zone_grade := z.grade
                break
            if i_warn_trap and z.violations >= 1 and dist < atr_val * 0.3 and volume < avg_vol_20 * 0.7
                active_warning     := "TRAP_SUSPECTED"
                warning_severity   := "HIGH"
                warning_zone_id    := z.id
                warning_zone_src   := z.source
                warning_zone_score := z.score
                warning_zone_grade := z.grade
                break
            if i_warn_sweep and z.source == "LIQ" and dist < atr_val * 0.3 and volume > avg_vol_20 * 1.5
                active_warning     := "SWEEP_IMMINENT"
                warning_severity   := "HIGH"
                warning_zone_id    := z.id
                warning_zone_src   := z.source
                warning_zone_score := z.score
                warning_zone_grade := z.grade
                break
            if i_warn_fvg and z.source == "FVG" and low <= z.top and high >= z.bottom and z.touches == 0
                active_warning   := "FVG_FILLING"
                warning_severity := "INFO"
                warning_zone_id  := z.id
            if i_warn_approach and (z.grade == "A" or z.grade == "B") and dist < atr_val * i_warn_atr_dist
                active_warning     := "ZONE_APPROACH"
                warning_severity   := z.grade == "A" ? "WARN" : "INFO"
                warning_zone_id    := z.id
                warning_zone_src   := z.source
                warning_zone_score := z.score
                warning_zone_grade := z.grade
    if active_warning == "—" and i_warn_cluster and n_warn > 0
        cluster_count = 0
        for i = 0 to n_warn - 1
            if math.abs(array.get(g_zones, i).mid - close) < atr_val
                cluster_count += 1
        if cluster_count >= 3
            active_warning   := "ZONE_CLUSTER_AHEAD"
            warning_severity := "WARN"

g_warning  := active_warning
g_warn_bar := bar_index

g_tp_dir    := "—"
g_tp_entry  := na
g_tp_stop   := na
g_tp_target := na
g_tp_rr     := na
g_tp_shares := 0
g_tp_score  := 0.0

if i_enable_trade_plan
    float best_long_score  = 0.0
    float best_long_top    = na
    float best_long_bot    = na
    float best_long_mid    = na
    float best_short_score = 0.0
    float best_short_top   = na
    float best_short_bot   = na
    float best_short_mid   = na

    n_tp = array.size(g_zones)
    if n_tp > 0
        for i = 0 to n_tp - 1
            z        = array.get(g_zones, i)
            dist_atr = math.abs(z.mid - close) / math.max(atr_val, 0.0001)

            if z.is_support and close > z.top and dist_atr < 1.5 and z.score > 60
                regime_ok = regime_state == "STRONG_BULL" or regime_state == "WEAK_BULL" or regime_state == "RANGE" or regime_state == "SQUEEZE"
                if regime_ok and z.score > best_long_score
                    best_long_score  := z.score
                    best_long_top    := z.top
                    best_long_bot    := z.bottom
                    best_long_mid    := z.mid

            if not z.is_support and close < z.bottom and dist_atr < 1.5 and z.score > 60
                regime_ok = regime_state == "STRONG_BEAR" or regime_state == "WEAK_BEAR" or regime_state == "RANGE" or regime_state == "SQUEEZE"
                if regime_ok and z.score > best_short_score
                    best_short_score  := z.score
                    best_short_top    := z.top
                    best_short_bot    := z.bottom
                    best_short_mid    := z.mid

    if not na(best_long_top) and best_long_score > 0
        tp_entry = best_long_top + atr_val * 0.1
        tp_stop  = best_long_bot - atr_val * 0.5
        tp_risk  = math.abs(tp_entry - tp_stop)
        if tp_risk > 0

            tp_tgt   = 999999.0
            n_tp2    = array.size(g_zones)
            if n_tp2 > 0
                for i = 0 to n_tp2 - 1
                    z = array.get(g_zones, i)
                    if not z.is_support and z.mid > tp_entry and z.mid < tp_tgt
                        tp_tgt := z.mid
            if tp_tgt < 999999.0
                tp_rr_val = (tp_tgt - tp_entry) / tp_risk
                if tp_rr_val >= i_min_rr
                    g_tp_dir    := "LONG"
                    g_tp_entry  := tp_entry
                    g_tp_stop   := tp_stop
                    g_tp_target := tp_tgt
                    g_tp_rr     := tp_rr_val
                    g_tp_score  := best_long_score
                    risk_dollar  = i_account_size * i_risk_pct / 100
                    g_tp_shares := int(math.round(risk_dollar / tp_risk))

    if not na(best_short_bot) and best_short_score > 0 and g_tp_dir == "—"
        tp_entry = best_short_bot - atr_val * 0.1
        tp_stop  = best_short_top + atr_val * 0.5
        tp_risk  = math.abs(tp_stop - tp_entry)
        if tp_r

Показано первые 50% кода (символов: 61623)

Открыть по тарифу

Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.

Разовая покупка файла

На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.

Чтобы оформить заказ, введите email и перейдите к оплате.

Стоимость полного кода
6 162,30₽ ≈ $76.21