پرش به محتوا
کریپکس

اسمارت مانی · اندیکاتور

Crypex SMC: ساختار بازار، اوردر بلاک، FVG و نقدینگی

اندیکاتور کامل اسمارت مانی برای TradingView: BOS و CHoCH (سوئینگ و داخلی)، اوردر بلاک، FVG با فیلتر کیفیت، EQH/EQL، سطوح روزانه/هفتگی/ماهانه، ناحیه‌ی پریمیوم و دیسکانت، استخرهای نقدینگی و کندل‌های جابه‌جایی.

smart-money-crypex.pine · Pine Script v6 · ۱٬۵۲۲ خط
// Crypex SMC — اندیکاتور تحلیل ساختار، نقدینگی و FVG
// © Crypex.org — All rights reserved. | حق نشر محفوظ برای Crypex.org
// License: personal, non-commercial use only. Redistribution or resale without written permission is prohibited.
// مجوز: استفاده شخصی و غیرتجاری. بازنشر یا فروش مجدد بدون اجازه‌ی کتبی ممنوع است.

//@version=6
indicator('Crypex SMC', 'Crypex SMC', overlay = true, max_labels_count = 500, max_lines_count = 500, max_boxes_count = 500)
//---------------------------------------------------------------------------------------------------------------------}
//CONSTANTS & STRINGS & INPUTS
//---------------------------------------------------------------------------------------------------------------------{
BULLISH_LEG                     = 1
BEARISH_LEG                     = 0

BULLISH                         = +1
BEARISH                         = -1

GREEN                           = #089981
RED                             = #F23645
BLUE                            = #2157f3
GRAY                            = #878b94
MONO_BULLISH                    = #b2b5be
MONO_BEARISH                    = #5d606b
MONO_MID                        = #878b94
// Opaque grays for Color Candles in Monochrome (any transparency lets the chart's native candles show through)
MONO_CANDLE_MUTED_BULL          = #9da0a8
MONO_CANDLE_MUTED_BEAR          = #6d7078

HISTORICAL                      = 'Historical'
PRESENT                         = 'Present'

COLORED                         = 'Colored'
MONOCHROME                      = 'Monochrome'

ALL                             = 'All'
BOS                             = 'BOS'
CHOCH                           = 'CHoCH'

TINY                            = size.tiny
SMALL                           = size.small
NORMAL                          = size.normal
LARGE                           = size.large
HUGE                            = size.huge

// پیش‌فرض نوشته‌ها برای خوانایی در اسکرین‌شات (دو پله بالاتر از حداقلِ قدیمی)
DEFAULT_LABEL_SIZE              = LARGE

ATR                             = 'Atr'
RANGE                           = 'Cumulative Mean Range'

CLOSE                           = 'Close'
HIGHLOW                         = 'High/Low'

SOLID                           = '⎯⎯⎯'
DASHED                          = '----'
DOTTED                          = '····'

SMART_GROUP                     = 'Smart Money Crypex'
INTERNAL_GROUP                  = 'Real Time Internal Structure'
SWING_GROUP                     = 'Real Time Swing Structure'
BLOCKS_GROUP                    = 'Order Blocks'
EQUAL_GROUP                     = 'EQH/EQL'
GAPS_GROUP                      = 'Fair Value Gaps'
LEVELS_GROUP                    = 'Highs & Lows MTF'
ZONES_GROUP                     = 'Premium & Discount Zones'
LIQ_GROUP                       = 'Liquidity Pools'
DISP_GROUP                      = 'Displacement Candles'
WM_GROUP                        = 'Watermark'
BRAND_GREEN                     = #0BA31C
BRAND_DARK                      = #1E2229

showWatermark = input.bool(true, 'Show crypex.org watermark', group = WM_GROUP)
wmPos = input.string(position.bottom_left, 'Position', options = [position.bottom_left, position.bottom_center, position.bottom_right, position.middle_center, position.top_center, position.top_left], group = WM_GROUP)
wmGap = input.float(5.0, 'Gap from left (%)', minval = 0, maxval = 30, step = 0.5, group = WM_GROUP, tooltip = 'Bottom-left only: empty space (in % of chart width) so the watermark sits right beside the TradingView logo instead of on top of it.')
wmSizeStr = input.string('large', 'Size', options = ['small', 'normal', 'large', 'huge'], group = WM_GROUP)
wmTransp = input.int(0, 'Transparency', minval = 0, maxval = 95, group = WM_GROUP, tooltip = '0 = exact logo colours, 95 = barely visible.')

modeTooltip                     = 'Allows to display historical Structure or only the recent ones'
styleTooltip                    = 'Indicator color theme'
showTrendTooltip                = 'Display additional candles with a color reflecting the current trend detected by structure'
showInternalsTooltip            = 'Display internal market structure'
internalFilterConfluenceTooltip = 'Filter non significant internal structure breakouts'
showStructureTooltip            = 'Display swing market Structure'
showSwingsTooltip               = 'Display swing point as labels on the chart'
showHighLowSwingsTooltip        = 'Highlight most recent strong and weak high/low points on the chart'
showInternalOrderBlocksTooltip  = 'Display internal order blocks on the chart\n\nNumber of internal order blocks to display on the chart'
showSwingOrderBlocksTooltip     = 'Display swing order blocks on the chart\n\nNumber of internal swing blocks to display on the chart'
orderBlockFilterTooltip         = 'Method used to filter out volatile order blocks \n\nIt is recommended to use the cumulative mean range method when a low amount of data is available'
orderBlockMitigationTooltip     = 'Select what values to use for order block mitigation'
showEqualHighsLowsTooltip       = 'Display equal highs and equal lows on the chart'
equalHighsLowsLengthTooltip     = 'Number of bars used to confirm equal highs and equal lows'
equalHighsLowsThresholdTooltip  = 'Sensitivity threshold in a range (0, 1) used for the detection of equal highs & lows\n\nLower values will return fewer but more pertinent results'
showFairValueGapsTooltip        = 'Display fair values gaps on the chart'
fairValueGapsThresholdTooltip   = 'Filter out non significant fair value gaps'
fairValueGapsTimeframeTooltip   = 'Fair value gaps timeframe'
fairValueGapsExtendTooltip      = 'Determine how many bars to extend the Fair Value Gap boxes on chart'
showPremiumDiscountZonesTooltip = 'Display premium, discount, and equilibrium zones on chart'

modeInput                       = input.string( HISTORICAL, 'Mode',                     group = SMART_GROUP,    tooltip = modeTooltip, options = [HISTORICAL, PRESENT])
styleInput                      = input.string( COLORED,    'Style',                    group = SMART_GROUP,    tooltip = styleTooltip,options = [COLORED, MONOCHROME])
showTrendInput                  = input(        false,      'Color Candles',            group = SMART_GROUP,    tooltip = showTrendTooltip)
// رنگ‌بندی کندل‌ها بر اساس تطابق روند سوئینگ و داخلی:
//   هر دو صعودی   → رنگ «صعودی کامل»   (روند قوی تأیید‌شده)
//   هر دو نزولی   → رنگ «نزولی کامل»   (روند قوی تأیید‌شده)
//   سوئینگ صعودی + داخلی نزولی → رنگ «صعودی در پولبک»  (روند کلی صعودی، بازگشت کوتاه‌مدت)
//   سوئینگ نزولی + داخلی صعودی → رنگ «نزولی در باونس» (روند کلی نزولی، جهش کوتاه‌مدت)
//   بدون تطابق / ابتدای چارت   → رنگ خنثی
trendFullBullColorInput         = input.color(  color.new(#089981,  0), '▲ صعودی کامل',         group = SMART_GROUP, inline = 'tc1', tooltip = 'هر دو سوئینگ و داخلی صعودی‌اند — روند قوی تأیید‌شده')
trendMutedBullColorInput        = input.color(  color.new(#089981, 60), '▲ صعودی / پولبک',      group = SMART_GROUP, inline = 'tc2', tooltip = 'سوئینگ صعودی ولی داخلی نزولی — روند کلی صعودی با اصلاح کوتاه‌مدت')
trendFullBearColorInput         = input.color(  color.new(#F23645,  0), '▼ نزولی کامل',         group = SMART_GROUP, inline = 'tc1', tooltip = 'هر دو سوئینگ و داخلی نزولی‌اند — روند قوی تأیید‌شده')
trendMutedBearColorInput        = input.color(  color.new(#F23645, 60), '▼ نزولی / باونس',      group = SMART_GROUP, inline = 'tc2', tooltip = 'سوئینگ نزولی ولی داخلی صعودی — روند کلی نزولی با جهش کوتاه‌مدت')
trendNeutralColorInput          = input.color(  color.new(#878b94, 40), '— خنثی',               group = SMART_GROUP,               tooltip = 'هنوز ساختار کاملی شکل نگرفته یا روند سوئینگ و داخلی هنوز همراستا نشده‌اند')

showInternalsInput              = input(        false,      'Show Internal Structure',  group = INTERNAL_GROUP, tooltip = showInternalsTooltip)
showInternalBullInput           = input.string( ALL,        'Bullish Structure',        group = INTERNAL_GROUP, inline = 'ibull', options = [ALL,BOS,CHOCH])
internalBullColorInput          = input(        GREEN,      '',                         group = INTERNAL_GROUP, inline = 'ibull')
showInternalBearInput           = input.string( ALL,        'Bearish Structure' ,       group = INTERNAL_GROUP, inline = 'ibear', options = [ALL,BOS,CHOCH])
internalBearColorInput          = input(        RED,        '',                         group = INTERNAL_GROUP, inline = 'ibear')
internalFilterConfluenceInput   = input(        false,      'Confluence Filter',        group = INTERNAL_GROUP, tooltip = internalFilterConfluenceTooltip)
internalDispFilterInput         = input(        true,       'Displacement Filter',      group = INTERNAL_GROUP, tooltip = 'Only register Internal BOS/CHoCH when the breaking candle has a strong body (>= Disp. × ATR). Filters weak/noise breaks.')
internalDispAtrMulInput         = input.float(  0.30,       'Disp. (× ATR)',            group = INTERNAL_GROUP, tooltip = 'Min body of breaking candle as a multiple of ATR(14). 0.30 = moderate, 0.50 = strict.', minval = 0.05, maxval = 1.5, step = 0.05)
internalStructureSize           = input.string( DEFAULT_LABEL_SIZE, 'Internal Label Size', group = INTERNAL_GROUP, options = [TINY,SMALL,NORMAL,LARGE,HUGE])
internalLineStyleInput          = input.string( DASHED,     'Internal Line Style',      group = INTERNAL_GROUP, options = [SOLID,DASHED,DOTTED])

showStructureInput              = input(        true,       'Show Swing Structure',     group = SWING_GROUP,    tooltip = showStructureTooltip)
showSwingBullInput              = input.string( ALL,        'Bullish Structure',        group = SWING_GROUP,    inline = 'bull',    options = [ALL,BOS,CHOCH])
swingBullColorInput             = input(        GREEN,      '',                         group = SWING_GROUP,    inline = 'bull')
showSwingBearInput              = input.string( ALL,        'Bearish Structure',        group = SWING_GROUP,    inline = 'bear',    options = [ALL,BOS,CHOCH])
swingBearColorInput             = input(        RED,        '',                         group = SWING_GROUP,    inline = 'bear')
swingStructureSize              = input.string( DEFAULT_LABEL_SIZE, 'Swing Label Size',    group = SWING_GROUP,    options = [TINY,SMALL,NORMAL,LARGE,HUGE])
swingLineStyleInput             = input.string( SOLID,      'Swing Line Style',         group = SWING_GROUP,    options = [SOLID,DASHED,DOTTED])
swingDispFilterInput            = input(        false,      'Displacement Filter',      group = SWING_GROUP,    tooltip = 'Only register Swing BOS/CHoCH when the breaking candle shows real displacement (strong body >= Disp. × ATR). Filters fake/wick breaks and slow grinds.')
swingDispAtrMulInput            = input.float(  0.40,       'Disp. (× ATR)',            group = SWING_GROUP,    tooltip = 'Min body of breaking candle vs ATR(14). 0.40 = moderate, 0.60 = strict (clean setups only).', minval = 0.05, maxval = 2.0, step = 0.05)
showSwingsInput                 = input(        false,      'Show Swings Points',       group = SWING_GROUP,    tooltip = showSwingsTooltip,inline = 'swings')
swingsLengthInput               = input.int(    50,         '',                         group = SWING_GROUP,    minval = 10,                inline = 'swings')
showHighLowSwingsInput          = input(        true,       'Show Strong/Weak High/Low',group = SWING_GROUP,    tooltip = showHighLowSwingsTooltip)

showInternalOrderBlocksInput    = input(        false,      'Internal Order Blocks' ,   group = BLOCKS_GROUP,   tooltip = showInternalOrderBlocksTooltip,   inline = 'iob')
internalOrderBlocksSizeInput    = input.int(    10,         '',                         group = BLOCKS_GROUP,   minval = 1, maxval = 20,                    inline = 'iob')
showSwingOrderBlocksInput       = input(        true,       'Swing Order Blocks',       group = BLOCKS_GROUP,   tooltip = showSwingOrderBlocksTooltip,      inline = 'ob')
swingOrderBlocksSizeInput       = input.int(    10,         '',                         group = BLOCKS_GROUP,   minval = 1, maxval = 20,                    inline = 'ob') 
orderBlockFilterInput           = input.string( ATR,        'Order Block Filter',       group = BLOCKS_GROUP,   tooltip = orderBlockFilterTooltip,          options = [ATR, RANGE])
orderBlockMitigationInput       = input.string( HIGHLOW,    'Order Block Mitigation',   group = BLOCKS_GROUP,   tooltip = orderBlockMitigationTooltip,      options = [CLOSE,HIGHLOW])
showOBRangeLabelInput           = input(        true,       'Show OB Range Label',      group = BLOCKS_GROUP)
obRangeLabelSizeInput           = input.string( DEFAULT_LABEL_SIZE, 'OB Label Size',         group = BLOCKS_GROUP,   options = [TINY, SMALL, NORMAL, LARGE, HUGE])
internalBullishOrderBlockColor  = input.color(color.new(#3179f5, 80), 'Internal Bullish OB',    group = BLOCKS_GROUP)
internalBearishOrderBlockColor  = input.color(color.new(#f77c80, 80), 'Internal Bearish OB',    group = BLOCKS_GROUP)
swingBullishOrderBlockColor     = input.color(color.new(#1848cc, 80), 'Bullish OB',             group = BLOCKS_GROUP)
swingBearishOrderBlockColor     = input.color(color.new(#b22833, 80), 'Bearish OB',             group = BLOCKS_GROUP)

showEqualHighsLowsInput         = input(        false,      'Equal High/Low',           group = EQUAL_GROUP,    tooltip = showEqualHighsLowsTooltip)
equalHighsLowsLengthInput       = input.int(    3,          'Bars Confirmation',        group = EQUAL_GROUP,    tooltip = equalHighsLowsLengthTooltip,      minval = 1)
equalHighsLowsThresholdInput    = input.float(  0.1,        'Threshold',                group = EQUAL_GROUP,    tooltip = equalHighsLowsThresholdTooltip,   minval = 0, maxval = 0.5, step = 0.1)
equalHighsLowsSizeInput         = input.string( DEFAULT_LABEL_SIZE, 'Label Size',            group = EQUAL_GROUP,    options = [TINY,SMALL,NORMAL,LARGE,HUGE])

showFairValueGapsInput          = input(        true,       'Fair Value Gaps',          group = GAPS_GROUP,     tooltip = showFairValueGapsTooltip)
fairValueGapsThresholdInput     = input(        true,       'Auto Threshold',           group = GAPS_GROUP,     tooltip = fairValueGapsThresholdTooltip)
fairValueGapsTimeframeInput     = input.timeframe('',       'Timeframe',                group = GAPS_GROUP,     tooltip = fairValueGapsTimeframeTooltip)
fairValueGapsBullColorInput     = input.color(color.new(#00ff68, 70), 'Bullish FVG' , group = GAPS_GROUP)
fairValueGapsBearColorInput     = input.color(color.new(#ff0008, 70), 'Bearish FVG' , group = GAPS_GROUP)
fairValueGapsExtendInput        = input.int(    2,          'Extend FVG',               group = GAPS_GROUP,     tooltip = fairValueGapsExtendTooltip,       minval = 0)
showFvgRangeLabelInput          = input(        true,       'Show FVG Range Label',     group = GAPS_GROUP)
fvgLabelSizeInput               = input.string( DEFAULT_LABEL_SIZE, 'FVG Label Size',        group = GAPS_GROUP,     options = [TINY,SMALL,NORMAL,LARGE,HUGE])
fvgLabelBullTextColorInput      = input.color(  color.new(#00ff68, 0), 'FVG Label Text Bull',  group = GAPS_GROUP, inline = 'fvglbltxt', tooltip = 'Text color for bullish FVG range labels (independent of box fill).')
fvgLabelBearTextColorInput      = input.color(  color.new(#ff1744, 0), 'FVG Label Text Bear',  group = GAPS_GROUP, inline = 'fvglbltxt', tooltip = 'Text color for bearish FVG range labels (independent of box fill).')
fvgStrictValidityInput          = input(        true,       'Valid FVG Only',           group = GAPS_GROUP,     tooltip = 'Enables all quality filters below. Turn off to show every 3-candle imbalance.')
fvgMinGapAtrMulInput            = input.float(  0.30,       'Min Gap (× ATR)',          group = GAPS_GROUP,     tooltip = 'Gap height ≥ this × ATR(14). 0.30–0.50 = strict clean setups. 0 = off.',    minval = 0, maxval = 1.0, step = 0.05)
fvgMinTicksInput                = input.int(    5,          'Min Gap (ticks)',          group = GAPS_GROUP,     tooltip = 'Absolute minimum gap in ticks. Filters noise regardless of ATR.',               minval = 0)
fvgDispAtrMulInput              = input.float(  0.50,       'Min Displacement (× ATR)', group = GAPS_GROUP,     tooltip = 'Middle candle BODY must be ≥ this × ATR(14). Ensures the FVG was formed by a sharp impulsive move (displacement), not a slow grind. 0 = off.', minval = 0, maxval = 2.0, step = 0.05)
fvgMitigationInput              = input.string( HIGHLOW,    'FVG Mitigation',           group = GAPS_GROUP,     tooltip = 'High/Low = invalid on first wick touch. Close = invalid only when price CLOSES through the far edge.',  options = [CLOSE, HIGHLOW])

showDailyLevelsInput            = input(        false,      'Daily',    group = LEVELS_GROUP,   inline = 'daily')
dailyLevelsStyleInput           = input.string( SOLID,      '',         group = LEVELS_GROUP,   inline = 'daily',   options = [SOLID,DASHED,DOTTED])
dailyLevelsColorInput           = input(        BLUE,       '',         group = LEVELS_GROUP,   inline = 'daily')
showWeeklyLevelsInput           = input(        false,      'Weekly',   group = LEVELS_GROUP,   inline = 'weekly')
weeklyLevelsStyleInput          = input.string( SOLID,      '',         group = LEVELS_GROUP,   inline = 'weekly',  options = [SOLID,DASHED,DOTTED])
weeklyLevelsColorInput          = input(        BLUE,       '',         group = LEVELS_GROUP,   inline = 'weekly')
showMonthlyLevelsInput          = input(        false,      'Monthly',   group = LEVELS_GROUP,   inline = 'monthly')
monthlyLevelsStyleInput         = input.string( SOLID,      '',         group = LEVELS_GROUP,   inline = 'monthly', options = [SOLID,DASHED,DOTTED])
monthlyLevelsColorInput         = input(        BLUE,       '',         group = LEVELS_GROUP,   inline = 'monthly')

showPremiumDiscountZonesInput   = input(        false,      'Premium/Discount Zones',   group = ZONES_GROUP , tooltip = showPremiumDiscountZonesTooltip)
premiumZoneColorInput           = input.color(  RED,        'Premium Zone',             group = ZONES_GROUP)
equilibriumZoneColorInput       = input.color(  GRAY,       'Equilibrium Zone',         group = ZONES_GROUP)
discountZoneColorInput          = input.color(  GREEN,      'Discount Zone',            group = ZONES_GROUP)

showLiquidityInput              = input(        true,       'Show Liquidity Pools',             group = LIQ_GROUP)
liquidityMaxInput               = input.int(    6,          'Max Levels Each Side',             group = LIQ_GROUP,  minval = 1, maxval = 20)
showBslInput                    = input(        true,       'Show BSL (Buy-Side)',               group = LIQ_GROUP,                                      inline = 'liqsides')
showSslInput                    = input(        true,       'Show SSL (Sell-Side)',              group = LIQ_GROUP,                                      inline = 'liqsides')
bslColorInput                   = input.color(color.new(#f7525f, 20), 'BSL Color',              group = LIQ_GROUP,                                      inline = 'liqbsl')
sslColorInput                   = input.color(color.new(#22ab94, 20), 'SSL Color',              group = LIQ_GROUP,                                      inline = 'liqssl')
liqLineStyleInput               = input.string( SOLID,     'Line Style',                        group = LIQ_GROUP,  options = [SOLID,DASHED,DOTTED])
liqLineWidthInput               = input.int(    2,          'Line Width',                       group = LIQ_GROUP,  minval = 1, maxval = 4)
showLiqLabelsInput              = input(        true,       'Show Labels',                      group = LIQ_GROUP,                                      inline = 'liqlbl')
liqLabelSizeInput               = input.string( DEFAULT_LABEL_SIZE, '',                                 group = LIQ_GROUP,  options = [TINY,SMALL,NORMAL,LARGE,HUGE],          inline = 'liqlbl')
liqSweepSourceInput             = input.string( HIGHLOW,    'Sweep Source',                     group = LIQ_GROUP,  options = [CLOSE,HIGHLOW])
showSweptLiqInput               = input(        true,       'Show Swept Levels',                group = LIQ_GROUP,                                      inline = 'liqswept')
sweptLiqColorInput              = input.color(color.new(#787b86, 50), '',                       group = LIQ_GROUP,                                      inline = 'liqswept')
liqMaxAgeInput                  = input.int(    500,        'Max Level Age (bars)',              group = LIQ_GROUP,  minval = 10, maxval = 5000,          tooltip = 'Remove liquidity levels older than this many bars. Old levels rarely have fresh stop orders. Lower = stricter freshness filter.')

showDispCandlesInput            = input(        true,       'Show Displacement Candles',         group = DISP_GROUP, tooltip = 'Highlight impulsive candles whose body ≥ ATR threshold and whose close is near the candle extreme (low wick ratio). Optionally require the candle to also form an FVG or break structure.')
dispAtrMulInput                 = input.float(  2.2,        'Min Body (× ATR-14)',               group = DISP_GROUP, minval = 0.5, maxval = 5.0, step = 0.1,   tooltip = '1.0 = normal  |  1.2 = validated  |  2.0+ = very strict (default 2.2).')
dispWickRatioInput              = input.float(  0.35,       'Max Wick/Body Ratio',               group = DISP_GROUP, minval = 0.0, maxval = 1.0, step = 0.05,  tooltip = 'Rejection wick relative to body. 0.35 means the counter-wick must be ≤ 35 % of the body. Keeps only clean momentum candles.')
dispRequireFvgInput             = input(        true,       'Only with FVG',                     group = DISP_GROUP, tooltip = 'Mark displacement only when the same candle (as middle candle) created a valid FVG. Significantly reduces noise.')
dispRequireStructureInput       = input(        false,      'Only with BOS / CHoCH',             group = DISP_GROUP, tooltip = 'Additionally require a structure break (BOS or CHoCH, internal or swing) on the same bar.')
dispBullColorInput              = input.color(  color.new(#2196F3, 0),  'Bullish',               group = DISP_GROUP, inline = 'dcol')
dispBearColorInput              = input.color(  color.new(#9C27B0, 0),  'Bearish',               group = DISP_GROUP, inline = 'dcol')
dispMarkerSizeInput             = input.string( DEFAULT_LABEL_SIZE, 'Marker Size',                       group = DISP_GROUP, options = [TINY, SMALL, NORMAL, LARGE, HUGE])

//---------------------------------------------------------------------------------------------------------------------}
//DATA STRUCTURES & VARIABLES
//---------------------------------------------------------------------------------------------------------------------{
// @type                            UDT representing alerts as bool fields
// @field internalBullishBOS        internal structure custom alert
// @field internalBearishBOS        internal structure custom alert
// @field internalBullishCHoCH      internal structure custom alert
// @field internalBearishCHoCH      internal structure custom alert
// @field swingBullishBOS           swing structure custom alert
// @field swingBearishBOS           swing structure custom alert
// @field swingBullishCHoCH         swing structure custom alert
// @field swingBearishCHoCH         swing structure custom alert
// @field internalBullishOrderBlock internal order block custom alert
// @field internalBearishOrderBlock internal order block custom alert
// @field swingBullishOrderBlock    swing order block custom alert
// @field swingBearishOrderBlock    swing order block custom alert
// @field equalHighs                equal high low custom alert
// @field equalLows                 equal high low custom alert
// @field bullishFairValueGap       fair value gap custom alert
// @field bearishFairValueGap       fair value gap custom alert
type alerts
    bool internalBullishBOS         = false
    bool internalBearishBOS         = false
    bool internalBullishCHoCH       = false
    bool internalBearishCHoCH       = false
    bool swingBullishBOS            = false
    bool swingBearishBOS            = false
    bool swingBullishCHoCH          = false
    bool swingBearishCHoCH          = false
    bool internalBullishOrderBlock  = false
    bool internalBearishOrderBlock  = false
    bool swingBullishOrderBlock     = false
    bool swingBearishOrderBlock     = false
    bool equalHighs                 = false
    bool equalLows                  = false
    bool bullishFairValueGap        = false
    bool bearishFairValueGap        = false
    bool bslSwept                   = false
    bool sslSwept                   = false

// @type                            UDT representing last swing extremes (top & bottom)
// @field top                       last top swing price
// @field bottom                    last bottom swing price
// @field barTime                   last swing bar time
// @field barIndex                  last swing bar index
// @field lastTopTime               last top swing time
// @field lastBottomTime            last bottom swing time
type trailingExtremes
    float top
    float bottom
    int barTime
    int barIndex
    int lastTopTime
    int lastBottomTime

// @type                            UDT representing Fair Value Gaps
// @field top                       top price
// @field bottom                    bottom price
// @field bias                      bias (BULLISH or BEARISH)
// @field topBox                    top box
// @field bottomBox                 bottom box
// @field l_abel                    range label (e.g. "4300-4310 FVG")
type fairValueGap
    float top
    float bottom
    int bias
    box topBox
    box bottomBox
    label l_abel = na

// @type                            UDT representing trend bias
// @field bias                      BULLISH or BEARISH
type trend
    int bias    

// @type                            UDT representing Equal Highs Lows display
// @field l_ine                     displayed line
// @field l_abel                    displayed label
type equalDisplay
    line l_ine      = na
    label l_abel    = na

// @type                            UDT representing a pivot point (swing point) 
// @field currentLevel              current price level
// @field lastLevel                 last price level
// @field crossed                   true if price level is crossed
// @field barTime                   bar time
// @field barIndex                  bar index    
type pivot
    float currentLevel
    float lastLevel
    bool crossed
    int barTime     = time
    int barIndex    = bar_index

// @type                            UDT representing an order block
// @field barHigh                   bar high
// @field barLow                    bar low
// @field barTime                   bar time
// @field bias                      BULLISH or BEARISH
type orderBlock
    float barHigh
    float barLow
    int barTime
    int bias

// @type                            UDT representing a single BSL or SSL liquidity level
// @field level                     price level
// @field barTime                   bar time when the swing was detected
// @field swept                     true once the wick has taken out this level (Turtle Soup)
// @field isBsl                     true = Buy-Side Liquidity (above), false = Sell-Side (below)
// @field isEqual                   true when level is an Equal High / Equal Low (double liquidity pool)
// @field sweepTime                 bar time when the sweep occurred (for the × marker)
type liquidityPool
    float level
    int   barTime
    bool  swept     = false
    bool  isBsl
    bool  isEqual   = false
    int   sweepTime = na
    int   barIdx    = 0

// @variable                        current swing pivot high    
var pivot swingHigh                 = pivot.new(na,na,false)
// @variable                        current swing pivot low
var pivot swingLow                  = pivot.new(na,na,false)
// @variable                        current internal pivot high
var pivot internalHigh              = pivot.new(na,na,false)
// @variable                        current internal pivot low
var pivot internalLow               = pivot.new(na,na,false)
// @variable                        current equal high pivot
var pivot equalHigh                 = pivot.new(na,na,false)
// @variable                        current equal low pivot
var pivot equalLow                  = pivot.new(na,na,false)
// @variable                        swing trend bias
var trend swingTrend                = trend.new(0)
// @variable                        internal trend bias
var trend internalTrend             = trend.new(0)
// @variable                        equal high display
var equalDisplay equalHighDisplay   = equalDisplay.new()
// @variable                        equal low display
var equalDisplay equalLowDisplay    = equalDisplay.new()
// @variable                        storage for fairValueGap UDTs
var array<fairValueGap> fairValueGaps = array.new<fairValueGap>()
// @variable                        storage for parsed highs
var array<float> parsedHighs        = array.new<float>()
// @variable                        storage for parsed lows
var array<float> parsedLows         = array.new<float>()
// @variable                        storage for raw highs
var array<float> highs              = array.new<float>()
// @variable                        storage for raw lows
var array<float> lows               = array.new<float>()
// @variable                        storage for bar time values
var array<int> times                = array.new<int>()
// @variable                        last trailing swing high and low
var trailingExtremes trailing       = trailingExtremes.new()
// @variable                                storage for orderBlock UDTs (swing order blocks)
var array<orderBlock> swingOrderBlocks      = array.new<orderBlock>()
// @variable                                storage for orderBlock UDTs (internal order blocks)
var array<orderBlock> internalOrderBlocks   = array.new<orderBlock>()
// @variable                                storage for swing order blocks boxes
var array<box> swingOrderBlocksBoxes        = array.new<box>()
// @variable                                storage for internal order blocks boxes
var array<box> internalOrderBlocksBoxes     = array.new<box>()
// @variable                                storage for swing order blocks labels
var array<label> swingOrderBlocksLabels     = array.new<label>()
// @variable                                storage for internal order blocks labels
var array<label> internalOrderBlocksLabels  = array.new<label>()
// @variable                        BSL liquidity pools (Buy-Side Liquidity — above swing highs)
var array<liquidityPool> bslPools       = array.new<liquidityPool>()
// @variable                        SSL liquidity pools (Sell-Side Liquidity — below swing lows)
var array<liquidityPool> sslPools       = array.new<liquidityPool>()
// @variable                        pre-allocated line pairs (L/R) — gap in the middle for centered price label
var array<line>  liqBslLinesL           = array.new<line>()
var array<line>  liqBslLinesR           = array.new<line>()
var array<line>  liqSslLinesL           = array.new<line>()
var array<line>  liqSslLinesR           = array.new<line>()
// @variable                        pre-allocated labels for BSL levels
var array<label> liqBslLabels           = array.new<label>()
// @variable                        pre-allocated labels for SSL levels
var array<label> liqSslLabels           = array.new<label>()
// @variable                        color for swing bullish structures (recomputed each bar so Style / monochrome toggles apply)
swingBullishColor                   = styleInput == MONOCHROME ? MONO_BULLISH : swingBullColorInput
// @variable                        color for swing bearish structures
swingBearishColor                   = styleInput == MONOCHROME ? MONO_BEARISH : swingBearColorInput
// @variable                        color for bullish fair value gaps
fairValueGapBullishColor            = styleInput == MONOCHROME ? color.new(MONO_BULLISH, 70) : fairValueGapsBullColorInput
// @variable                        color for bearish fair value gaps
fairValueGapBearishColor            = styleInput == MONOCHROME ? color.new(MONO_BEARISH, 70) : fairValueGapsBearColorInput
// @variable                        color for premium zone
premiumZoneColor                    = styleInput == MONOCHROME ? MONO_BEARISH : premiumZoneColorInput
// @variable                        color for discount zone
discountZoneColor                   = styleInput == MONOCHROME ? MONO_BULLISH : discountZoneColorInput
// Monochrome liquidity / levels helpers (liquidity sweep markers & lines use these instead of colored inputs)
bsLiqDrawColor                      = styleInput == MONOCHROME ? MONO_BEARISH : bslColorInput
sslLiqDrawColor                     = styleInput == MONOCHROME ? MONO_BULLISH : sslColorInput
sweptLiqDrawColor                   = styleInput == MONOCHROME ? color.new(MONO_MID, 45)       : sweptLiqColorInput
dispShapeBullColor                  = styleInput == MONOCHROME ? MONO_BULLISH                  : dispBullColorInput
dispShapeBearColor                  = styleInput == MONOCHROME ? MONO_BEARISH                  : dispBearColorInput
// @variable                        bar index on current script iteration
varip int currentBarIndex           = bar_index
// @variable                        bar index on last script iteration
varip int lastBarIndex              = bar_index
// @variable                        alerts in current bar
alerts currentAlerts                = alerts.new()
// @variable                        time at start of chart
var initialTime                     = time

// we create the needed boxes for displaying order blocks at the first execution
if barstate.isfirst
    if showSwingOrderBlocksInput
        for index = 1 to swingOrderBlocksSizeInput
            swingOrderBlocksBoxes.push(box.new(na,na,na,na,xloc = xloc.bar_time,extend = extend.right))
            swingOrderBlocksLabels.push(label.new(na,na,xloc = xloc.bar_time, color = color(na), style = label.style_none, size = LARGE))
    if showInternalOrderBlocksInput
        for index = 1 to internalOrderBlocksSizeInput
            internalOrderBlocksBoxes.push(box.new(na,na,na,na,xloc = xloc.bar_time,extend = extend.right))
            internalOrderBlocksLabels.push(label.new(na,na,xloc = xloc.bar_time, color = color(na), style = label.style_none, size = LARGE))
    if showLiquidityInput
        for index = 1 to liquidityMaxInput
            liqBslLinesL.push(line.new(na,na,na,na, xloc=xloc.bar_time, style=line.style_dotted, width=2))
            liqBslLinesR.push(line.new(na,na,na,na, xloc=xloc.bar_time, style=line.style_dotted, width=2))
            liqSslLinesL.push(line.new(na,na,na,na, xloc=xloc.bar_time, style=line.style_dotted, width=2))
            liqSslLinesR.push(line.new(na,na,na,na, xloc=xloc.bar_time, style=line.style_dotted, width=2))
            liqBslLabels.push(label.new(na,na, xloc=xloc.bar_time, color=color(na), size=LARGE, style=label.style_none))
            liqSslLabels.push(label.new(na,na, xloc=xloc.bar_time, color=color(na), size=LARGE, style=label.style_none))

// @variable                        source to use in bearish order blocks mitigation
bearishOrderBlockMitigationSource   = orderBlockMitigationInput == CLOSE ? close : high
// @variable                        source to use in bullish order blocks mitigation
bullishOrderBlockMitigationSource   = orderBlockMitigationInput == CLOSE ? close : low
// @variable                        ATR(14) for displacement candle detection
atr14                               = ta.atr(14)
// @variable                        default volatility measure
atrMeasure                          = ta.atr(200)
// @variable                        cumulative true range (always calculated for consistency)
cumulativeTR                        = ta.cum(ta.tr)
// @variable                        parsed volatility measure by user settings
volatilityMeasure                   = orderBlockFilterInput == ATR ? atrMeasure : bar_index > 0 ? cumulativeTR / bar_index : ta.tr
// @variable                        true if current bar is a high volatility bar
highVolatilityBar                   = (high - low) >= (2 * volatilityMeasure)
// @variable                        parsed high
parsedHigh                          = highVolatilityBar ? low : high
// @variable                        parsed low
parsedLow                           = highVolatilityBar ? high : low

// we store current values into the arrays at each bar
parsedHighs.push(parsedHigh)
parsedLows.push(parsedLow)
highs.push(high)
lows.push(low)
times.push(time)

//---------------------------------------------------------------------------------------------------------------------}
//USER-DEFINED FUNCTIONS
//---------------------------------------------------------------------------------------------------------------------{
// @function            get line style from string
// @param style         line style
// @returns             string
getStyle(string style) =>
    switch style
        SOLID  => line.style_solid
        DASHED => line.style_dashed
        DOTTED => line.style_dotted

// @function            Get the value of the current leg, it can be 0 (bearish) or 1 (bullish)
// @returns             int
leg(int size) =>
    var int leg = na
    newLegHigh  = high[size] > ta.highest(size)
    newLegLow   = low[size]  < ta.lowest(size)

    if newLegHigh
        leg := BEARISH_LEG
    else if newLegLow
        leg := BULLISH_LEG
    leg

// @function            Identify whether the current value is the start of a new leg (swing)
// @param leg           (int) Current leg value
// @returns             bool
startOfNewLeg(int leg)      => not na(leg) and leg != leg[1]

// @function            Identify whether the current level is the start of a new bearish leg (swing)
// @param leg           (int) Current leg value
// @returns             bool
startOfBearishLeg(int leg)  => leg == BEARISH_LEG and leg != leg[1] and not na(leg)

// @function            Identify whether the current level is the start of a new bullish leg (swing)
// @param leg           (int) Current leg value
// @returns             bool
startOfBullishLeg(int leg)  => leg == BULLISH_LEG and leg != leg[1] and not na(leg)

// @function            create a new label
// @param labelTime     bar time coordinate
// @param labelPrice    price coordinate
// @param tag           text to display
// @param labelColor    text color
// @param labelStyle    label style
// @returns             label ID
drawLabel(int labelTime, float labelPrice, string tag, color labelColor, string labelStyle) =>    
    var label l_abel = na

    if modeInput == PRESENT
        l_abel.delete()

    l_abel := label.new(chart.point.new(labelTime,na,labelPrice),tag,xloc.bar_time,color=color(na),textcolor=labelColor,style = labelStyle,size = swingStructureSize)

// @function            create a new line and label representing an EQH or EQL
// @param p_ivot        starting pivot
// @param level         price level of current pivot
// @param size          how many bars ago was the current pivot detected
// @param equalHigh     true for EQH, false for EQL
// @returns             label ID
drawEqualHighLow(pivot p_ivot, float level, int size, bool equalHigh) =>
    equalDisplay e_qualDisplay = equalHigh ? equalHighDisplay : equalLowDisplay
    
    string tag          = 'EQL'
    color equalColor    = swingBullishColor
    string labelStyle   = label.style_label_up

    if equalHigh
        tag         := 'EQH'
        equalColor  := swingBearishColor
        labelStyle  := label.style_label_down

    if modeInput == PRESENT
        line.delete(    e_qualDisplay.l_ine)
        label.delete(   e_qualDisplay.l_abel)
        
    e_qualDisplay.l_ine     := line.new(chart.point.new(p_ivot.barTime,na,p_ivot.currentLevel), chart.point.new(time[size],na,level), xloc = xloc.bar_time, color = equalColor, style = line.style_dotted)
    labelPosition           = math.round(0.5*(p_ivot.barIndex + bar_index - size))
    e_qualDisplay.l_abel    := label.new(chart.point.new(na,labelPosition,level), tag, xloc.bar_index, color = color(na), textcolor = equalColor, style = labelStyle, size = equalHighsLowsSizeInput)

// @function            store current structure and trailing swing points, and also display swing points and equal highs/lows
// @param size          (int) structure size
// @param equalHighLow  (bool) true for displaying current highs/lows
// @param internal      (bool) true for getting internal structures
// @returns             label ID
getCurrentStructure(int size,bool equalHighLow = false, bool internal = false) =>        
    currentLeg              = leg(size)
    newPivot                = startOfNewLeg(currentLeg)
    pivotLow                = startOfBullishLeg(currentLeg)
    pivotHigh               = startOfBearishLeg(currentLeg)

    if newPivot
        if pivotLow
            pivot p_ivot = equalHighLow ? equalLow : internal ? internalLow : swingLow

            if equalHighLow and math.abs(p_ivot.currentLevel - low[size]) < equalHighsLowsThresholdInput * atrMeasure
                drawEqualHighLow(p_ivot, low[size], size, false)
                currentAlerts.equalLows := true
                // EQL = concentrated SSL — upgrade any nearby SSL pool to isEqual
                if showLiquidityInput
                    upgraded = false
                    if sslPools.size() > 0
                        for j = 0 to sslPools.size() - 1
                            ep = sslPools.get(j)
                            if math.abs(ep.level - low[size]) < equalHighsLowsThresholdInput * atrMeasure
                                ep.isEqual := true
                                upgraded := true
                                break
                    if not upgraded
                        sslPools.unshift(liquidityPool.new(low[size], time[size], false, false, true, na, bar_index[size]))

            p_ivot.lastLevel    := p_ivot.currentLevel
            p_ivot.currentLevel := low[size]
            p_ivot.crossed      := false
            p_ivot.barTime      := time[size]
            p_ivot.barIndex     := bar_index[size]

            if not equalHighLow and not internal
                trailing.bottom         := p_ivot.currentLevel
                trailing.barTime        := p_ivot.barTime
                trailing.barIndex       := p_ivot.barIndex
                trailing.lastBottomTime := p_ivot.barTime

            if showSwingsInput and not internal and not equalHighLow
                drawLabel(time[size], low[size], not na(p_ivot.lastLevel) and low[size] < p_ivot.lastLevel ? 'LL' : 'HL', swingBullishColor, label.style_label_up)

            // Add every new swing low as a Sell-Side Liquidity (SSL) pool
            if showLiquidityInput and not equalHighLow and not internal
                sslPools.unshift(liquidityPool.new(low[size], time[size], false, false, false, na, bar_index[size]))
                if sslPools.size() > 200
                    sslPools.pop()
        else
            pivot p_ivot = equalHighLow ? equalHigh : internal ? internalHigh : swingHigh

            if equalHighLow and math.abs(p_ivot.currentLevel - high[size]) < equalHighsLowsThresholdInput * atrMeasure
                drawEqualHighLow(p_ivot, high[size], size, true)
                currentAlerts.equalHighs := true
                // EQH = concentrated BSL — upgrade any nearby BSL pool to isEqual
                if showLiquidityInput
                    upgraded = false
                    if bslPools.size() > 0
                        for j = 0 to bslPools.size() - 1
                            ep = bslPools.get(j)
                            if math.abs(ep.level - high[size]) < equalHighsLowsThresholdInput * atrMeasure
                                ep.isEqual := true
                                upgraded := true
                                break
                    if not upgraded
                        bslPools.unshift(liquidityPool.new(high[size], time[size], false, true, true, na, bar_index[size]))

            p_ivot.lastLevel    := p_ivot.currentLevel
            p_ivot.currentLevel := high[size]
            p_ivot.crossed      := false
            p_ivot.barTime      := time[size]
            p_ivot.barIndex     := bar_index[size]

            if not equalHighLow and not internal
                trailing.top            := p_ivot.currentLevel
                trailing.barTime        := p_ivot.barTime
                trailing.barIndex       := p_ivot.barIndex
                trailing.lastTopTime    := p_ivot.barTime

            if showSwingsInput and not internal and not equalHighLow
                drawLabel(time[size], high[size], not na(p_ivot.lastLevel) and high[size] > p_ivot.lastLevel ? 'HH' : 'LH', swingBearishColor, label.style_label_down)

            // Add every new swing high as a Buy-Side Liquidity (BSL) pool
            if showLiquidityInput and not equalHighLow and not internal
                bslPools.unshift(liquidityPool.new(high[size], time[size], false, true, false, na, bar_index[size]))
                if bslPools.size() > 200
                    bslPools.pop()
                
// @function                draw line and label representing a structure
// @param p_ivot            base pivot point
// @param tag               test to display
// @param structureColor    base color
// @param lineStyle         line style
// @param labelStyle        label style
// @param labelSize         text size
// @returns                 label ID
drawStructure(pivot p_ivot, string tag, color structureColor, string lineStyle, string labelStyle, string labelSize) =>    
    var line l_ine      = line.new(na,na,na,na,xloc = xloc.bar_time)
    var label l_abel    = label.new(na,na)

    if modeInput == PRESENT
        l_ine.delete()
        l_abel.delete()

    l_ine   := line.new(chart.point.new(p_ivot.barTime,na,p_ivot.currentLevel), chart.point.new(time,na,p_ivot.currentLevel), xloc.bar_time, color=structureColor, style=lineStyle)
    l_abel  := label.new(chart.point.new(na,math.round(0.5*(p_ivot.barIndex+bar_index)),p_ivot.currentLevel), tag, xloc.bar_index, color=color(na), textcolor=structureColor, style=labelStyle, size = labelSize)

// @function            delete order blocks
// @param internal      true for internal order blocks
// @returns             orderBlock ID
deleteOrderBlocks(bool internal = false) =>
    array<orderBlock> orderBlocks = internal ? internalOrderBlocks : swingOrderBlocks

    if orderBlocks.size() > 0
        for index = orderBlocks.size() - 1 to 0
            eachOrderBlock = orderBlocks.get(index)
            bool crossedOderBlock = false

            if bearishOrderBlockMitigationSource > eachOrderBlock.barHigh and eachOrderBlock.bias == BEARISH
                crossedOderBlock := true
                if internal
                    currentAlerts.internalBearishOrderBlock := true
                else
                    currentAlerts.swingBearishOrderBlock    := true
            else if bullishOrderBlockMitigationSource < eachOrderBlock.barLow and eachOrderBlock.bias == BULLISH
                crossedOderBlock := true
                if internal
                    currentAlerts.internalBullishOrderBlock := true
                else
                    currentAlerts.swingBullishOrderBlock    := true
            if crossedOderBlock
                orderBlocks.remove(index)

// @function            fetch and store order blocks
// @param p_ivot        base pivot point
// @param internal      true for internal order blocks
// @param bias          BULLISH or BEARISH
// @returns             void
storeOrdeBlock(pivot p_ivot,bool internal = false,int bias) =>
    if (not internal and showSwingOrderBlocksInput) or (internal and showInternalOrderBlocksInput)

        array<float> a_rray = na
        int localOffset = na

        if bias == BEARISH
            a_rray      := parsedHighs.slice(p_ivot.barIndex, bar_index)
            localOffset := a_rray.indexof(a_rray.max())
        else
            a_rray      := parsedLows.slice(p_ivot.barIndex, bar_index)
            localOffset := a_rray.indexof(a_rray.min())

        // Guard: localOffset == -1 when the slice is empty or all values are na
        if not na(localOffset) and localOffset >= 0
            int parsedIndex = p_ivot.barIndex + localOffset

            // Use actual highs/lows for box coordinates.
            // parsedHighs/parsedLows are only used to select WHICH bar is the OB —
            // on high-volatility bars they are swapped (parsedHigh = low, parsedLow = high),
            // so using them directly would produce an inverted box.
            if parsedIndex < highs.size() and parsedIndex < lows.size()
                orderBlock o_rderBlock          = orderBlock.new(highs.get(parsedIndex), lows.get(parsedIndex), times.get(parsedIndex), bias)
                array<orderBlock> orderBlocks   = internal ? internalOrderBlocks : swingOrderBlocks

                if orderBlocks.size() >= 100
                    orderBlocks.pop()
                orderBlocks.unshift(o_rderBlock)

// @function            format a price value: no decimals if integer, up to 5 significant digits otherwise
// @param p             price value
// @returns             string
formatPrice(float p) =>
    int decimals = p >= 1000 ? 1 : p >= 100 ? 2 : p >= 10 ? 3 : p >= 1 ? 4 : 5
    str.tostring(p, '#.' + str.repeat('0', decimals))

// @function            FVG range label at box right edge; text extends right
// @param rightTime     same as FVG box right (formation time + extend)
// @returns             label id or na
createFvgLabel(int rightTime, float topP, float bottomP, bool isBull) =>
    if showFvgRangeLabelInput
        float hi  = math.max(topP, bottomP)
        float lo  = math.min(topP, bottomP)
        float mid = math.avg(hi, lo)
        string txt = formatPrice(hi) + '-' + formatPrice(lo) + ' FVG'
        color tc   = styleInput == MONOCHROME ? (isBull ? MONO_BULLISH : MONO_BEARISH) : (isBull ? fvgLabelBullTextColorInput : fvgLabelBearTextColorInput)
        label.new(chart.point.new(rightTime, na, mid), txt, xloc.bar_time, color=color(na), textcolor=tc, style=label.style_label_left, size=fvgLabelSizeInput)
    else
        na

// @function            draw order blocks as boxes with price range labels.
//                      OBs are sorted by proximity to current close so the most
//                      relevant levels are always visible without manual count tuning.
// @param internal      true for internal order blocks
// @returns             void
drawOrderBlocks(bool internal = false) =>
    array<orderBlock> orderBlocks = internal ? internalOrderBlocks : swingOrderBlocks
    maxOrderBlocks                = internal ? internalOrderBlocksSizeInput : swingOrderBlocksSizeInput
    array<box>   b_oxes           = internal ? internalOrderBlocksBoxes    : swingOrderBlocksBoxes
    array<label> l_abels          = internal ? internalOrderBlocksLabels   : swingOrderBlocksLabels

    int nSnap = orderBlocks.size()

    array<float> distances  = array.new<float>(nSnap)
    bool         canSortObs = nSnap > 0

    if canSortObs
        for i = 0 to nSnap - 1
            distances.set(i, 1e100)
        for i = 0 to nSnap - 1
            ob = orderBlocks.get(i)
            float midPk = nz((ob.barHigh + ob.barLow) * 0.5, na)
            float distV = na(midPk) or na(close) ? 1e100 : math.abs(close - midPk)
            distances.set(i, distV)

    array<int> sortedIdx = canSortObs ? array.sort_indices(distances, order.ascending) : array.new<int>()

    int nSort = sortedIdx.size()
    int displayCount = nSort > 0 ? math.min(maxOrderBlocks, math.min(nSort, nSnap)) : 0

    int nRendered = b_oxes.size()
    if nRendered > 0
        for slot = 0 to nRendered - 1
            box   b_ox   = b_oxes.get(slot)
            label l_abel = l_abels.get(slot)

            int nLiveSlots = orderBlocks.size()
            bool showObSlot = slot < displayCount and slot < nSort
            if showObSlot and nLiveSlots > 0
                int obIdx = sortedIdx.get(slot)

                if obIdx >= 0 and obIdx < nLiveSlots
                    eachOrderBlock = orderBlocks.get(obIdx)

                    orderBlockColor = styleInput == MONOCHROME ? (eachOrderBlock.bias == BEARISH ? color.new(MONO_BEARISH,80) : color.new(MONO_BULLISH,80)) : internal ? (eachOrderBlock.bias == BEARISH ? internalBearishOrderBlockColor : internalBullishOrderBlockColor) : (eachOrderBlock.bias == BEARISH ? swingBearishOrderBlockColor : swingBullishOrderBlockColor)
                    color labelTxtCol =
                         styleInput == MONOCHROME
                             ? (eachOrderBlock.bias == BEARISH ? MONO_BEARISH : MONO_BULLISH)
                             : (eachOrderBlock.bias == BEARISH
                                     ? (internal ? internalBearColorInput : swingBearColorInput)
                                     : (internal ? internalBullColorInput : swingBullColorInput))

                    b_ox.set_top_left_point(    chart.point.new(eachOrderBlock.barTime, na, eachOrderBlock.barHigh))
                    b_ox.set_bottom_right_point(chart.point.new(last_bar_time,          na, eachOrderBlock.barLow))
                    b_ox.set_border_color(      internal ? na : orderBlockColor)
                    b_ox.set_bgcolor(           orderBlockColor)

                    if showOBRangeLabelInput
                        string obRangeText = na(eachOrderBlock.barHigh) or na(eachOrderBlock.barLow) ? '' :
                             formatPrice(eachOrderBlock.barHigh) + '-' + formatPrice(eachOrderBlock.barLow)
                        // lower_left @ (barTime, barHigh): pin = lower-left of label → text sits ABOVE the top edge, grows right.
                        // upper_left @ (barTime, barLow): pin = upper-left of label → text sits BELOW the bottom edge, grows right.
                        // (upper_right / lower_right pin the opposite corner — text bleeds LEFT of barTime; do not use.)
                        if eachOrderBlock.bias == BEARISH
                            l_abel.set_style(label.style_label_lower_left)
                            l_abel.set_point(chart.point.new(eachOrderBlock.barTime, na, eachOrderBlock.barHigh))
                        else
                            l_abel.set_style(label.style_label_upper_left)
                            l_abel.set_point(chart.point.new(eachOrderBlock.barTime, na, eachOrderBlock.barLow))
                        l_abel.set_color(color(na))
                        l_abel.set_text(obRangeText)
                        l_abel.set_textcolor(labelTxtCol)
                        l_abel.set_size(obRangeLabelSizeInput)
                    else
                        l_abel.set_text('')
                        l_abel.set_color(color(na))
                else
                    b_ox.set_bgcolor(     color(na))
                    b_ox.set_border_color(color(na))
                    l_abel.set_text('')
            else
                // Hide this slot — no OB close enough to fill it
                b_ox.set_bgcolor(     color(na))
                b_ox.set_border_color(color(na))
                l_abel.set_text('')

// @function            detect and draw structures, also detect and store order blocks
// @param internal      true for internal structures or order blocks
// @returns             void
displayStructure(bool internal = false) =>
    var bullishBar = true
    var bearishBar = true

    if internalFilterConfluenceInput
        bullishBar := high - math.max(close, open) > math.min(close, open) - low
        bearishBar := high - math.max(close, open) < math.min(close, open) - low

    // Displacement check: breaking candle body must be >= dispAtrMul × ATR(14)
    // A small-body or wick-only break often signals a sweep/trap, not a real structural break.
    float bodySize      = math.abs(close - open)
    float dispAtrMul    = internal ? internalDispAtrMulInput : swingDispAtrMulInput
    bool  dispEnabled   = internal ? internalDispFilterInput : swingDispFilterInput
    bool  hasDisplacement = not dispEnabled or bodySize >= dispAtrMul * atrMeasure

    pivot p_ivot    = internal ? internalHigh : swingHigh
    trend t_rend    = internal ? internalTrend : swingTrend

    lineStyle       = internal ? getStyle(internalLineStyleInput) : getStyle(swingLineStyleInput)
    labelSize       = internal ? internalStructureSize : swingStructureSize

    extraCondition  = internal ? internalHigh.currentLevel != swingHigh.currentLevel and bullishBar : true
    bullishColor    = styleInput == MONOCHROME ? MONO_BULLISH : internal ? internalBullColorInput : swingBullColorInput

    bullishBreak = not na(p_ivot.currentLevel) and close > p_ivot.currentLevel and close[1] <= p_ivot.currentLevel

    if bullishBreak and not p_ivot.crossed and extraCondition and hasDisplacement
        string tag = t_rend.bias == BEARISH ? CHOCH : BOS

        if internal
            currentAlerts.internalBullishCHoCH  := tag == CHOCH
            currentAlerts.internalBullishBOS    := tag == BOS
        else
            currentAlerts.swingBullishCHoCH     := tag == CHOCH
            currentAlerts.swingBullishBOS       := tag == BOS

        p_ivot.crossed  := true
        t_rend.bias     := BULLISH

        displayCondition = internal ? showInternalsInput and (showInternalBullInput == ALL or (showInternalBullInput == BOS and tag != CHOCH) or (showInternalBullInput == CHOCH and tag == CHOCH)) : showStructureInput and (showSwingBullInput == ALL or (showSwingBullInput == BOS and tag != CHOCH) or (showSwingBullInput == CHOCH and tag == CHOCH))

        if displayCondition                        
            drawStructure(p_ivot,tag,bullishColor,lineStyle,label.style_label_down,labelSize)

        if (internal and showInternalOrderBlocksInput) or (not internal and showSwingOrderBlocksInput)
            storeOrdeBlock(p_ivot,internal,BULLISH)

    p_ivot          := internal ? internalLow : swingLow    
    extraCondition  := internal ? internalLow.currentLevel != swingLow.currentLevel and bearishBar : true
    bearishColor    = styleInput == MONOCHROME ? MONO_BEARISH : internal ? internalBearColorInput : swingBearColorInput

    bearishBreak = not na(p_ivot.currentLevel) and close < p_ivot.currentLevel and close[1] >= p_ivot.currentLevel

    if bearishBreak and not p_ivot.crossed and extraCondition and hasDisplacement
        string tag = t_rend.bias == BULLISH ? CHOCH : BOS

        if internal
            currentAlerts.internalBearishCHoCH  := tag == CHOCH
            currentAlerts.internalBearishBOS    := tag == BOS
        else
            currentAlerts.swingBearishCHoCH     := tag == CHOCH
            currentAlerts.swingBearishBOS       := tag == BOS

        p_ivot.crossed := true
        t_rend.bias := BEARISH

        displayCondition = internal ? showInternalsInput and (showInternalBearInput == ALL or (showInternalBearInput == BOS and tag != CHOCH) or (showInternalBearInput == CHOCH and tag == CHOCH)) : showStructureInput and (showSwingBearInput == ALL or (showSwingBearInput == BOS and tag != CHOCH) or (showSwingBearInput == CHOCH and tag == CHOCH))
        
        if displayCondition                        
            drawStructure(p_ivot,tag,bearishColor,lineStyle,label.style_label_up,labelSize)

        if (internal and showInternalOrderBlocksInput) or (not internal and showSwingOrderBlocksInput)
            storeOrdeBlock(p_ivot,internal,BEARISH)

// @function            draw one fair value gap box (each fair value gap has two boxes)
// @param leftTime      left time coordinate
// @param rightTime     right time coordinate
// @param topPrice      top price level
// @param bottomPrice   bottom price level
// @param boxColor      box color
// @returns             box ID
fairValueGapBox(leftTime,rightTime,topPrice,bottomPrice,boxColor) => box.new(chart.point.new(leftTime,na,topPrice),chart.point.new(rightTime + fairValueGapsExtendInput * (time-time[1]),na,bottomPrice), xloc=xloc.bar_time, border_color = boxColor, bgcolor = boxColor)

// @function            delete fair value gaps
// @returns             fairValueGap ID
deleteFairValueGaps() =>
    // FVG struct layout (note: naming is relative to direction):
    //   BULLISH FVG: top = low[0] (upper/near edge), bottom = high[2] (lower/far edge)
    //   BEARISH FVG: top = high[0] (lower/near edge), bottom = low[2] (upper/far edge)
    //
    // HIGHLOW (touch) mode: invalidate as soon as wick ENTERS the gap zone (touches near edge)
    //   Bull: low < top   (wick enters from above — first touch of upper boundary)
    //   Bear: high > top  (wick enters from below — first touch of lower boundary)
    //
    // CLOSE mode: invalidate only when price CLOSES through the far edge (full fill)
    //   Bull: close < bottom  (closes below lower boundary)
    //   Bear: close > bottom  (closes above upper boundary)
    useWickMitigation = fvgMitigationInput == HIGHLOW
    if fairValueGaps.size() > 0
        for index = fairValueGaps.size() - 1 to 0
            eachFairValueGap = fairValueGaps.get(index)
            bool bullMitigated = eachFairValueGap.bias == BULLISH and (useWickMitigation ? low  < eachFairValueGap.top    : close < eachFairValueGap.bottom)
            bool bearMitigated = eachFairValueGap.bias == BEARISH and (useWickMitigation ? high > eachFairValueGap.top    : close > eachFairValueGap.bottom)
            if bullMitigated or bearMitigated
                eachFairValueGap.topBox.delete()
                eachFairValueGap.bottomBox.delete()
                if not na(eachFairValueGap.l_abel)
                    eachFairValueGap.l_abel.delete()
                fairValueGaps.remove(index)
    
// @function            draw fair value gaps
// @returns             fairValueGap ID
drawFairValueGaps() =>
    [lastClose, lastOpen, lastTime, currentHigh, currentLow, currentTime, last2High, last2Low, midHigh, midLow, currentClose, fvgTfAtr] = request.security(syminfo.tickerid, fairValueGapsTimeframeInput, [close[1], open[1], time[1], high[0], low[0], time[0], high[2], low[2], high[1], low[1], close[0], ta.atr(14)], lookahead = barmerge.lookahead_on)

    barDeltaPercent     = lastOpen != 0 ? (lastClose - lastOpen) / (lastOpen * 100) : 0.0
    newTimeframe        = timeframe.change(fairValueGapsTimeframeInput)
    threshold           = fairValueGapsThresholdInput ? ta.cum(math.abs(newTimeframe ? barDeltaPercent : 0)) / bar_index * 2 : 0

    // Base 3-candle imbalance on the FVG timeframe
    bool baseBull       = currentLow > last2High and lastClose > last2High and barDeltaPercent > threshold and newTimeframe
    bool baseBear       = currentHigh < last2Low and lastClose < last2Low and -barDeltaPercent > threshold and newTimeframe

    // Strict validity filters (all applied on the FVG timeframe data):
    //
    //  1) Gap size (absolute)  : gap height >= fvgMinTicksInput × mintick
    //     Removes micro / noise gaps of just a few ticks regardless of ATR.
    //
    //  2) Gap size (relative)  : gap height >= fvgMinGapAtrMulInput × ATR(14)
    //     Recommended 0.30–0.50 for clean setups; 0 = disabled.
    //
    //  3) Displacement         : body of middle candle (bar[1]) >= fvgDispAtrMulInput × ATR(14)
    //     Ensures the FVG was created by a SHARP impulsive move, not a slow grind.
    //     body = |close[1] − open[1]|
    //     Recommended 0.30–0.50 (same range as gap size). 0 = disabled.
    //
    //  4) Direction            : middle candle body must agree with FVG direction
    //     Bullish FVG → bar[1] closes above open; bearish → closes below open.
    float atrRef        = math.max(nz(fvgTfAtr, ta.atr(14)), syminfo.mintick)
    float bullGapH      = currentLow  - last2High
    float bearGapH      = last2Low    - currentHigh
    float midBody       = math.abs(lastClose - lastOpen)
    float minTickSize   = fvgMinTicksInput * syminfo.mintick

    bool  bullTickOk    = bullGapH >= minTickSize
    bool  bearTickOk    = bearGapH >= minTickSize
    bool  bullAtrOk     = fvgMinGapAtrMulInput <= 0 or bullGapH >= fvgMinGapAtrMulInput * atrRef
    bool  bearAtrOk     = fvgMinGapAtrMulInput <= 0 or bearGapH >= fvgMinGapAtrMulInput * atrRef
    bool  dispOk        = fvgDispAtrMulInput    <= 0 or midBody  >= fvgDispAtrMulInput    * atrRef
    bool  bullDirection = lastClose > lastOpen
    bool  bearDirection = lastClose < lastOpen

    bool  strictBull    = bullTickOk and bullAtrOk and dispOk and bullDirection
    bool  strictBear    = bearTickOk and bearAtrOk and dispOk and bearDirection

    bullishFairValueGap = baseBull and (not fvgStrictValidityInput or strictBull)
    bearishFairValueGap = baseBear and (not fvgStrictValidityInput or strictBear)

    if bullishFairValueGap
        currentAlerts.bullishFairValueGap := true
        int fvgRightT = currentTime + fairValueGapsExtendInput * (time - time[1])
        fairValueGaps.unshift(fairValueGap.new(currentLow, last2High, BULLISH, fairValueGapBox(lastTime, currentTime, currentLow, math.avg(currentLow, last2High), fairValueGapBullishColor), fairValueGapBox(lastTime, currentTime, math.avg(currentLow, last2High), last2High, fairValueGapBullishColor), createFvgLabel(fvgRightT, currentLow, last2High, true)))
    if bearishFairValueGap
        currentAlerts.bearishFairValueGap := true
        int fvgRightT = currentTime + fairValueGapsExtendInput * (time - time[1])
        fairValueGaps.unshift(fairValueGap.new(currentHigh, last2Low, BEARISH, fairValueGapBox(lastTime, currentTime, currentHigh, math.avg(currentHigh, last2Low), fairValueGapBearishColor), fairValueGapBox(lastTime, currentTime, math.avg(currentHigh, last2Low), last2Low, fairValueGapBearishColor), createFvgLabel(fvgRightT, currentHigh, last2Low, false)))

// @function            draw MultiTimeFrame levels
// @param timeframe     base timeframe
// @param sameTimeframe true if chart timeframe is same as base timeframe
// @param style         line style
// @param levelColor    line and text color
// @returns             void
drawLevels(string timeframe, bool sameTimeframe, string style, color levelColor) =>
    [topLevel, bottomLevel, leftTime, rightTime] = request.security(syminfo.tickerid, timeframe, [high[1], low[1], time[1], time],lookahead = barmerge.lookahead_on)

    float parsedTop         = sameTimeframe ? high : topLevel
    float parsedBottom      = sameTimeframe ? low : bottomLevel    

    int parsedLeftTime      = sameTimeframe ? time : leftTime
    int parsedRightTime     = sameTimeframe ? time : rightTime

    int parsedTopTime       = time
    int parsedBottomTime    = time

    if not sameTimeframe
        int leftIndex               = times.binary_search_rightmost(parsedLeftTime)
        int rightIndex              = times.binary_search_rightmost(parsedRightTime)

        array<int> timeArray        = times.slice(leftIndex,rightIndex)
        array<float> topArray       = highs.slice(leftIndex,rightIndex)
        array<float> bottomArray    = lows.slice(leftIndex,rightIndex)

        parsedTopTime               := timeArray.size() > 0 ? timeArray.get(topArray.indexof(topArray.max())) : initialTime
        parsedBottomTime            := timeArray.size() > 0 ? timeArray.get(bottomArray.indexof(bottomArray.min())) : initialTime

    var line topLine        = line.new(na, na, na, na, xloc = xloc.bar_time, color = levelColor, style = getStyle(style))
    var line bottomLine     = line.new(na, na, na, na, xloc = xloc.bar_time, color = levelColor, style = getStyle(style))
    var label topLabel      = label.new(na, na, xloc = xloc.bar_time, text = str.format('P{0}H',timeframe), color=color(na), textcolor = levelColor, size = LARGE, style = label.style_label_left)
    var label bottomLabel   = label.new(na, na, xloc = xloc.bar_time, text = str.format('P{0}L',timeframe), color=color(na), textcolor = levelColor, size = LARGE, style = label.style_label_left)

    topLine.set_color(levelColor)
    bottomLine.set_color(levelColor)
    topLine.set_style(getStyle(style))
    bottomLine.set_style(getStyle(style))
    topLabel.set_textcolor(levelColor)
    bottomLabel.set_textcolor(levelColor)
    topLabel.set_size(LARGE)
    bottomLabel.set_size(LARGE)

    topLine.set_first_point(    chart.point.new(parsedTopTime,na,parsedTop))
    topLine.set_second_point(   chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedTop))   
    topLabel.set_point(         chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedTop))

    bottomLine.set_first_point( chart.point.new(parsedBottomTime,na,parsedBottom))    
    bottomLine.set_second_point(chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedBottom))
    bottomLabel.set_point(      chart.point.new(last_bar_time + 20 * (time-time[1]),na,parsedBottom))

// @function            true if chart timeframe is higher than provided timeframe
// @param timeframe     timeframe to check
// @returns             bool
higherTimeframe(string tf) => timeframe.in_seconds() > timeframe.in_seconds(tf)

// @function            check whether any BSL or SSL has been swept or invalidated.
//
//                      HIGHLOW mode (ICT Turtle Soup — default):
//                        • Sweep  : wick pierces the level BUT close stays on the near side
//                                   → mark swept, draw × marker, fire alert
//                        • Invalidate: close crosses the level (genuine structural break)
//                                   → remove level entirely (no lingering display)
//                        • If a level was already swept and price now closes beyond it
//                                   → also remove it
//
//                      CLOSE mode (conservative):
//                        • Only a close crossing the level counts as a sweep.
//                        • Level is marked swept (shown faded) and never removed automatically.
//
// @returns             void
checkLiquiditySweeps() =>
    useClose = liqSweepSourceInput == CLOSE

    // --- BSL (Buy-Side Liquidity — above swing highs) ---
    if bslPools.size() > 0
        for i = bslPools.size() - 1 to 0
            pool          = bslPools.get(i)
            // Age filter: remove stale levels — old levels no longer have fresh stop orders
            bool bslTooOld = liqMaxAgeInput > 0 and (bar_index - pool.barIdx) > liqMaxAgeInput
            if bslTooOld
                bslPools.remove(i)
            else
                isBslSweep    = not pool.swept and (useClose ? close > pool.level : (high > pool.level and close < pool.level))
                isBslInvalid  = not useClose and close > pool.level

                if isBslSweep
                    pool.swept         := true
                    pool.sweepTime     := time
                    currentAlerts.bslSwept := true
                    if showLiqLabelsInput
                        label.new(chart.point.new(time, na, pool.level), '×', xloc.bar_time, color=color(na), textcolor=bsLiqDrawColor, style=label.style_label_left, size=liqLabelSizeInput)

                if isBslInvalid
                    bslPools.remove(i)

    // --- SSL (Sell-Side Liquidity — below swing lows) ---
    if sslPools.size() > 0
        for i = sslPools.size() - 1 to 0
            pool          = sslPools.get(i)
            bool sslTooOld = liqMaxAgeInput > 0 and (bar_index - pool.barIdx) > liqMaxAgeInput
            if sslTooOld
                sslPools.remove(i)
            else
                isSslSweep    = not pool.swept and (useClose ? close < pool.level : (low < pool.level and close > pool.level))
                isSslInvalid  = not useClose and close < pool.level

                if isSslSweep
                    pool.swept         := true
                    pool.sweepTime     := time
                    currentAlerts.sslSwept := true
                    if showLiqLabelsInput
                        label.new(chart.point.new(time, na, pool.level), '×', xloc.bar_time, color=color(na), textcolor=sslLiqDrawColor, style=label.style_label_left, size=liqLabelSizeInput)

                if isSslInvalid
                    sslPools.remove(i)

// @function            draw one side (BSL or SSL) of liquidity pools.
//                      Each level uses two line segments with a time-gap at the geometric centre
//                      so the price label sits in a clear “missing” stretch of the line.
// @param pools         array of liquidityPool UDTs for this side
// @param linesL        left segment lines
// @param linesR        right segment lines
// @param labels        pre-allocated label objects (centre: price + tag)
// @param tag           'BSL' or 'SSL'
// @param activeColor   color for unswept levels
// @param showSide      when false, hides all slots for this side
// @param rightEdge     right edge bar time for line extension
// @param activeStyle   line.style_* for unswept lines
// @returns             void
drawLiqOneSide(array<liquidityPool> pools, array<line> linesL, array<line> linesR, array<label> labels, string tag, color activeColor, bool showSide, int rightEdge, string activeStyle) =>
    int nSlots = linesL.size()
    if not showSide
        for slot = 0 to nSlots - 1
            linesL.get(slot).set_color(color(na))
            linesR.get(slot).set_color(color(na))
            labels.get(slot).set_text('')
    else
        array<liquidityPool> combined = array.new<liquidityPool>()
        if pools.size() > 0
            for i = 0 to pools.size() - 1
                pool = pools.get(i)
                if not pool.swept
                    combined.push(pool)
            if showSweptLiqInput
                for i = 0 to pools.size() - 1
                    pool = pools.get(i)
                    if pool.swept
                        combined.push(pool)
        int total = combined.size()
        array<float> scores = array.new<float>(total)
        if total > 0
            for i = 0 to total - 1
                pool = combined.get(i)
                dist = math.abs(close - pool.level)
                // Scoring priority (lower score = displayed first):
                //   1. Fresh EQH/EQL (concentrated double liquidity) — highest priority, shown even if farther
                //   2. Fresh regular BSL/SSL — sorted by proximity
                //   3. Swept levels — always last regardless of distance
                float score = pool.swept ? dist + 1e12 : pool.isEqual ? dist * 0.01 : dist
                scores.set(i, score)
        array<int> sortedIdx = total > 0 ? array.sort_indices(scores, order.ascending) : array.new<int>()
        int showCount = math.min(nSlots, total)
        int barMs     = time - time[1]
        if barMs <= 0
            barMs := 1
        for slot = 0 to nSlots - 1
            l_ineL = linesL.get(slot)
            l_ineR = linesR.get(slot)
            l_abel = labels.get(slot)
            if slot < showCount and sortedIdx.size() > slot
                pool     = combined.get(sortedIdx.get(slot))
                c        = pool.swept ? sweptLiqDrawColor : activeColor
                st       = pool.swept ? line.style_dashed : pool.isEqual ? line.style_solid : activeStyle
                w        = pool.swept ? 1 : pool.isEqual ? liqLineWidthInput + 1 : liqLineWidthInput
                span     = rightEdge - pool.barTime
                midTime  = pool.barTime + int(span / 2)
                // Gap width: at least ~2 bars, or ~7% of span — creates a visible break for the price label
                gapMs    = int(math.max(barMs * 2, span * 0.07))
                halfGap  = int(gapMs / 2)
                tLeftEnd = midTime - halfGap
                tRightSt = midTime + halfGap
                twoSeg   = span > gapMs * 2 and tLeftEnd > pool.barTime and tRightSt < rightEdge and tLeftEnd < tRightSt

                l_ineL.set_color(c)
                l_ineL.set_style(st)
                l_ineL.set_width(w)
                l_ineR.set_color(c)
                l_ineR.set_style(st)
                l_ineR.set_width(w)

                if twoSeg
                    l_ineL.set_first_point( chart.point.new(pool.barTime, na, pool.level))
                    l_ineL.set_second_point(chart.point.new(tLeftEnd,     na, pool.level))
                    l_ineR.set_first_point( chart.point.new(tRightSt,     na, pool.level))
                    l_ineR.set_second_point(chart.point.new(rightEdge,    na, pool.level))
                else
                    // Short span: single continuous segment on the left line, hide the right segment
                    l_ineL.set_first_point( chart.point.new(pool.barTime, na, pool.level))
                    l_ineL.set_second_point(chart.point.new(rightEdge,    na, pool.level))
                    l_ineR.set_color(color(na))

                if showLiqLabelsInput
                    // Two-line label centred in the time-gap: price / tag - style_none = pure text, no bubble
                    string liqTag  = pool.isEqual ? (tag == 'BSL' ? 'EQH' : 'EQL') : tag
                    string liqText = pool.swept ? formatPrice(pool.level) + '\n' + liqTag + ' ✓' : formatPrice(pool.level) + '\n' + liqTag
                    l_abel.set_point(chart.point.new(midTime, na, pool.level))
                    l_abel.set_text(liqText)
                    l_abel.set_textcolor(c)
                    l_abel.set_size(liqLabelSizeInput)
                    l_abel.set_style(label.style_none)
                    l_abel.set_color(color(na))
                else
                    l_abel.set_text('')
                    l_abel.set_color(color(na))
            else
                l_ineL.set_color(color(na))
                l_ineR.set_color(color(na))
                l_abel.set_text('')
                l_abel.set_color(color(na))

// @function            draw BSL and SSL liquidity pools using pre-allocated lines & labels.
// @returns             void
drawLiquidityPools() =>
    int  rightEdge    = last_bar_time + 20 * (time - time[1])
    string activeStyle = getStyle(liqLineStyleInput)
    drawLiqOneSide(bslPools, liqBslLinesL, liqBslLinesR, liqBslLabels, 'BSL', bsLiqDrawColor, showBslInput, rightEdge, activeStyle)
    drawLiqOneSide(sslPools, liqSslLinesL, liqSslLinesR, liqSslLabels, 'SSL', sslLiqDrawColor, showSslInput, rightEdge, activeStyle)

// @function            update trailing swing points
// @returns             int
updateTrailingExtremes() =>
    trailing.top            := na(trailing.top) ? high : math.max(high, trailing.top)
    trailing.lastTopTime    := trailing.top == high ? time : trailing.lastTopTime
    trailing.bottom         := na(trailing.bottom) ? low : math.min(low, trailing.bottom)
    trailing.lastBottomTime := trailing.bottom == low ? time : trailing.lastBottomTime

// @function            draw trailing swing points
// @returns             void
drawHighLowSwings() =>
    var line topLine        = line.new(na, na, na, na, color = swingBearishColor, xloc = xloc.bar_time)
    var line bottomLine     = line.new(na, na, na, na, color = swingBullishColor, xloc = xloc.bar_time)
    var label topLabel      = label.new(na, na, color=color(na), textcolor = swingBearishColor, xloc = xloc.bar_time, style = label.style_label_down, size = swingStructureSize)
    var label bottomLabel   = label.new(na, na, color=color(na), textcolor = swingBullishColor, xloc = xloc.bar_time, style = label.style_label_up, size = swingStructureSize)

    topLine.set_color(swingBearishColor)
    bottomLine.set_color(swingBullishColor)
    topLabel.set_textcolor(swingBearishColor)
    bottomLabel.set_textcolor(swingBullishColor)
    topLabel.set_size(swingStructureSize)
    bottomLabel.set_size(swingStructureSize)

    rightTimeBar            = last_bar_time + 20 * (time - time[1])

    topLine.set_first_point(    chart.point.new(trailing.lastTopTime, na, trailing.top))
    topLine.set_second_point(   chart.point.new(rightTimeBar, na, trailing.top))
    topLabel.set_point(         chart.point.new(rightTimeBar, na, trailing.top))
    topLabel.set_text(          swingTrend.bias == BEARISH ? 'Strong High' : 'Weak High')

    bottomLine.set_first_point( chart.point.new(trailing.lastBottomTime, na, trailing.bottom))
    bottomLine.set_second_point(chart.point.new(rightTimeBar, na, trailing.bottom))
    bottomLabel.set_point(      chart.point.new(rightTimeBar, na, trailing.bottom))
    bottomLabel.set_text(       swingTrend.bias == BULLISH ? 'Strong Low' : 'Weak Low')

// @function            draw a zone with a label and a box
// @param labelLevel    price level for label
// @param labelIndex    bar index for label
// @param top           top price level for box
// @param bottom        bottom price level for box
// @param tag           text to display
// @param zoneColor     base color
// @param style         label style
// @returns             void
drawZone(float labelLevel, int labelIndex, float top, float bottom, string tag, color zoneColor, string style) =>
    var label l_abel    = label.new(na,na,text = tag, color=color(na),textcolor = zoneColor, style = style, size = swingStructureSize)
    var box b_ox        = box.new(na,na,na,na,bgcolor = color.new(zoneColor,80),border_color = color(na), xloc = xloc.bar_time)

    b_ox.set_top_left_point(    chart.point.new(trailing.barTime,na,top))
    b_ox.set_bottom_right_point(chart.point.new(last_bar_time,na,bottom))

    l_abel.set_point(           chart.point.new(na,labelIndex,labelLevel))
    l_abel.set_textcolor(zoneColor)
    l_abel.set_size(swingStructureSize)
    b_ox.set_bgcolor(color.new(zoneColor, 80))

// @function            draw premium/discount zones
// @returns             void
drawPremiumDiscountZones() =>
    drawZone(trailing.top, math.round(0.5*(trailing.barIndex + last_bar_index)), trailing.top, 0.95*trailing.top + 0.05*trailing.bottom, 'Premium', premiumZoneColor, label.style_label_down)

    equilibriumLevel = math.avg(trailing.top, trailing.bottom)
    drawZone(equilibriumLevel, last_bar_index, 0.525*trailing.top + 0.475*trailing.bottom, 0.525*trailing.bottom + 0.475*trailing.top, 'Equilibrium', styleInput == MONOCHROME ? MONO_MID : equilibriumZoneColorInput, label.style_label_left)

    drawZone(trailing.bottom, math.round(0.5*(trailing.barIndex + last_bar_index)), 0.95*trailing.bottom + 0.05*trailing.top, trailing.bottom, 'Discount', discountZoneColor, label.style_label_up)

//---------------------------------------------------------------------------------------------------------------------}
//MUTABLE VARIABLES & EXECUTION
//---------------------------------------------------------------------------------------------------------------------{
parsedOpen  = showTrendInput ? open : na
// ─── Trend candle coloring ────────────────────────────────────────────────────
// Two independent trend trackers are used:
//   swingTrend   : updated by swing pivot (length = swingsLengthInput, default 50)
//                  Reflects the higher-timeframe / macro direction.
//   internalTrend: updated by internal pivot (length = 5)
//                  Reflects the short-term / micro direction.
//
// Five states (all handled without "na" risk):
//   1. Swing BULLISH + Internal BULLISH  → Full Bull   (both TFs agree — strong trend)
//   2. Swing BEARISH + Internal BEARISH  → Full Bear   (both TFs agree — strong trend)
//   3. Swing BULLISH + Internal BEARISH  → Muted Bull  (macro up, micro pullback)
//   4. Swing BEARISH + Internal BULLISH  → Muted Bear  (macro down, micro bounce)
//   5. Either bias still = 0 (not yet set) → Neutral
//
// NOTE: displayStructure(true) and displayStructure() are both called whenever
//       showTrendInput is true (even if Internal/Swing display toggles are off),
//       so both biases are always updated on every bar.
_swBull  = swingTrend.bias    == BULLISH
_swBear  = swingTrend.bias    == BEARISH
_inBull  = internalTrend.bias == BULLISH
_inBear  = internalTrend.bias == BEARISH

// Derived states — exactly one will be true (or none = neutral)
_fullBull  = _swBull and _inBull
_fullBear  = _swBear and _inBear
_mutedBull = _swBull and _inBear
_mutedBear = _swBear and _inBull

candleColor = styleInput == MONOCHROME ?
                 (_fullBull  ? MONO_BULLISH :
                  _fullBear  ? MONO_BEARISH :
                  _mutedBull ? MONO_CANDLE_MUTED_BULL :
                  _mutedBear ? MONO_CANDLE_MUTED_BEAR :
                  MONO_MID) :
                 (_fullBull  ? trendFullBullColorInput  :
                  _fullBear  ? trendFullBearColorInput  :
                  _mutedBull ? trendMutedBullColorInput :
                  _mutedBear ? trendMutedBearColorInput :
                  trendNeutralColorInput)
plotcandle(parsedOpen,high,low,close,color = candleColor, wickcolor = candleColor, bordercolor = candleColor)

if showHighLowSwingsInput or showPremiumDiscountZonesInput
    updateTrailingExtremes()

    if showHighLowSwingsInput
        drawHighLowSwings()

    if showPremiumDiscountZonesInput
        drawPremiumDiscountZones()

if showFairValueGapsInput
    deleteFairValueGaps()

getCurrentStructure(swingsLengthInput,false)
getCurrentStructure(5,false,true)

if showEqualHighsLowsInput
    getCurrentStructure(equalHighsLowsLengthInput,true)

// Always run both structure detectors when Color Candles is on so
// swingTrend and internalTrend are current regardless of other toggles.
if showInternalsInput or showInternalOrderBlocksInput or showTrendInput
    displayStructure(true)

if showStructureInput or showSwingOrderBlocksInput or showHighLowSwingsInput or showTrendInput
    displayStructure()

if showInternalOrderBlocksInput
    deleteOrderBlocks(true)

if showSwingOrderBlocksInput
    deleteOrderBlocks()

if showFairValueGapsInput
    drawFairValueGaps()

if showLiquidityInput
    checkLiquiditySweeps()

//---------------------------------------------------------------------------------------------------------------------}
//DISPLACEMENT CANDLE DETECTION
//---------------------------------------------------------------------------------------------------------------------{
// The displacement candle is always bar[1] (the impulsive middle candle).
// Signal fires at bar[0] (confirmation bar).  plotshape offset=-1 marks bar[1].
// Works regardless of whether FVG display is enabled.
//
// Quality layers (always active, not optional):
//   1. ATR body filter  : body[1] >= dispAtrMulInput × ATR14[1]
//   2. Wick filter      : counter-wick <= body × wickRatio  (clean close at extreme)
//   3. Body/range ratio : body[1] >= 0.65 × range[1]        (no doji/spinning-top)
//   4. Relative size    : body[1] >= 1.5 × avg-body(10)     (dominant vs recent bars)
//   5. Breakout close   : bull → close[1] > high[2]; bear → close[1] < low[2]
//                         (candle actually closes OUTSIDE prior candle's range)
// Optional filters (user-controlled):
//   6. FVG confirmation : current bar creates a 3-candle imbalance
//   7. Structure break  : BOS or CHoCH on current bar

// ── ATR reference for bar[1] ─────────────────────────────────────────────────────
_atrRef1        = nz(atr14[1], atr14)

// ── Metrics of the MIDDLE candle (bar[1]) ────────────────────────────────────────
_midBody        = math.abs(close[1] - open[1])
_midRange       = high[1] - low[1]

// Layer 1: ATR body threshold
_bodyAtrOk      = _midBody >= dispAtrMulInput * _atrRef1 and _midRange > 0

// Layer 2: wick shape  (counter-wick ≤ body × ratio)
_bullWickOk     = (high[1] - close[1]) <= _midBody * dispWickRatioInput
_bearWickOk     = (close[1] - low[1])  <= _midBody * dispWickRatioInput

// Layer 3: body/range ratio — body must cover ≥ 65 % of total candle range
_bodyRangeOk    = _midRange > 0 and (_midBody / _midRange) >= 0.65

// Layer 4: dominant vs recent context — body must be ≥ 1.5× avg body of last 10 bars
_avgBody10      = math.avg(math.abs(close[1]-open[1]), math.abs(close[2]-open[2]), math.abs(close[3]-open[3]), math.abs(close[4]-open[4]), math.abs(close[5]-open[5]), math.abs(close[6]-open[6]), math.abs(close[7]-open[7]), math.abs(close[8]-open[8]), math.abs(close[9]-open[9]), math.abs(close[10]-open[10]))
_relSizeOk      = _avgBody10 > 0 and _midBody >= 1.5 * _avgBody10

// Layer 5: breakout close — the candle must close beyond the prior candle's range
_bullBreakout   = close[1] > high[2]
_bearBreakout   = close[1] < low[2]

// Combine base quality (all hard-filters must pass)
_midBullShape   = close[1] > open[1] and _bodyAtrOk and _bullWickOk and _bodyRangeOk and _relSizeOk and _bullBreakout
_midBearShape   = close[1] < open[1] and _bodyAtrOk and _bearWickOk and _bodyRangeOk and _relSizeOk and _bearBreakout

// ── Optional: FVG confirmation (3-candle imbalance on chart TF) ──────────────────
_localBullFvg   = low  > high[2]
_localBearFvg   = high < low[2]
_fvgOkBull      = not dispRequireFvgInput or _localBullFvg
_fvgOkBear      = not dispRequireFvgInput or _localBearFvg

// ── Optional: structure break ────────────────────────────────────────────────────
_dispStructBull = currentAlerts.internalBullishBOS or currentAlerts.swingBullishBOS or currentAlerts.internalBullishCHoCH or currentAlerts.swingBullishCHoCH
_dispStructBear = currentAlerts.internalBearishBOS or currentAlerts.swingBearishBOS or currentAlerts.internalBearishCHoCH or currentAlerts.swingBearishCHoCH
_structOkBull   = not dispRequireStructureInput or _dispStructBull
_structOkBear   = not dispRequireStructureInput or _dispStructBear

// ── Final signals (fire at bar[0]; plotshape offset=-1 marks bar[1]) ─────────────
bullDisplacement = showDispCandlesInput and _midBullShape and _fvgOkBull and _structOkBull
bearDisplacement = showDispCandlesInput and _midBearShape and _fvgOkBear and _structOkBear
//---------------------------------------------------------------------------------------------------------------------}

// Order blocks هر بار به‌روز می‌شوند. قبلاً فقط در آخرین بار رسم می‌شدند و گاهی لیبل محدوده خالی یا قدیمی می‌ماند (ریپلی، لود، کشیدن روی چارت، …).
if showInternalOrderBlocksInput
    drawOrderBlocks(true)

if showSwingOrderBlocksInput
    drawOrderBlocks()

if barstate.islastconfirmedhistory or barstate.islast
    if showLiquidityInput
        drawLiquidityPools()

lastBarIndex    := currentBarIndex
currentBarIndex := bar_index
newBar          = currentBarIndex != lastBarIndex

if barstate.islastconfirmedhistory or (barstate.isrealtime and newBar)
    if showDailyLevelsInput and not higherTimeframe('D')
        drawLevels('D',timeframe.isdaily,dailyLevelsStyleInput,styleInput == MONOCHROME ? MONO_BEARISH : dailyLevelsColorInput)

    if showWeeklyLevelsInput and not higherTimeframe('W')
        drawLevels('W',timeframe.isweekly,weeklyLevelsStyleInput,styleInput == MONOCHROME ? MONO_BEARISH : weeklyLevelsColorInput)

    if showMonthlyLevelsInput and not higherTimeframe('M')
        drawLevels('M',timeframe.ismonthly,monthlyLevelsStyleInput,styleInput == MONOCHROME ? MONO_BEARISH : monthlyLevelsColorInput)

//---------------------------------------------------------------------------------------------------------------------}
//ALERTS
//---------------------------------------------------------------------------------------------------------------------{
alertcondition(currentAlerts.internalBullishBOS,        'Internal Bullish BOS',         'Internal Bullish BOS formed')
alertcondition(currentAlerts.internalBullishCHoCH,      'Internal Bullish CHoCH',       'Internal Bullish CHoCH formed')
alertcondition(currentAlerts.internalBearishBOS,        'Internal Bearish BOS',         'Internal Bearish BOS formed')
alertcondition(currentAlerts.internalBearishCHoCH,      'Internal Bearish CHoCH',       'Internal Bearish CHoCH formed')

alertcondition(currentAlerts.swingBullishBOS,           'Bullish BOS',                  'Bullish BOS formed')
alertcondition(currentAlerts.swingBullishCHoCH,         'Bullish CHoCH',                'Bullish CHoCH formed')
alertcondition(currentAlerts.swingBearishBOS,           'Bearish BOS',                  'Bearish BOS formed')
alertcondition(currentAlerts.swingBearishCHoCH,         'Bearish CHoCH',                'Bearish CHoCH formed')

alertcondition(currentAlerts.internalBullishOrderBlock, 'Bullish Internal OB Breakout', 'Price broke bullish internal OB')
alertcondition(currentAlerts.internalBearishOrderBlock, 'Bearish Internal OB Breakout', 'Price broke bearish internal OB')
alertcondition(currentAlerts.swingBullishOrderBlock,    'Bullish Swing OB Breakout',    'Price broke bullish swing OB')
alertcondition(currentAlerts.swingBearishOrderBlock,    'Bearish Swing OB Breakout',    'Price broke bearish swing OB')

alertcondition(currentAlerts.equalHighs,                'Equal Highs',                  'Equal highs detected')
alertcondition(currentAlerts.equalLows,                 'Equal Lows',                   'Equal lows detected')

alertcondition(currentAlerts.bullishFairValueGap,       'Bullish FVG',                  'Bullish FVG formed')
alertcondition(currentAlerts.bearishFairValueGap,       'Bearish FVG',                  'Bearish FVG formed')

alertcondition(currentAlerts.bslSwept,                  'BSL Swept',                    'Buy-Side Liquidity swept (Turtle Soup — potential short)')
alertcondition(currentAlerts.sslSwept,                  'SSL Swept',                    'Sell-Side Liquidity swept (Turtle Soup — potential long)')

alertcondition(bullDisplacement,                        'Bullish Displacement Candle',  'Impulsive bullish candle detected (body ≥ ATR threshold, strong close)')
alertcondition(bearDisplacement,                        'Bearish Displacement Candle',  'Impulsive bearish candle detected (body ≥ ATR threshold, strong close)')

//---------------------------------------------------------------------------------------------------------------------}
//DISPLACEMENT CANDLE MARKERS
//---------------------------------------------------------------------------------------------------------------------{
// plotshape size must be const — branch per user-selected size.
// offset=-1: the signal fires at bar[0] but the triangle is drawn on bar[1]
//            (the actual impulsive middle candle that created the FVG / structure break).
plotshape(bullDisplacement and dispMarkerSizeInput == TINY,   title = 'Bull Displacement', style = shape.triangleup,   location = location.belowbar, color = dispShapeBullColor, size = size.tiny,   text = 'D', textcolor = dispShapeBullColor, offset = -1)
plotshape(bullDisplacement and dispMarkerSizeInput == SMALL,  title = 'Bull Displacement', style = shape.triangleup,   location = location.belowbar, color = dispShapeBullColor, size = size.small,  text = 'D', textcolor = dispShapeBullColor, offset = -1)
plotshape(bullDisplacement and dispMarkerSizeInput == NORMAL, title = 'Bull Displacement', style = shape.triangleup,   location = location.belowbar, color = dispShapeBullColor, size = size.normal, text = 'D', textcolor = dispShapeBullColor, offset = -1)
plotshape(bullDisplacement and dispMarkerSizeInput == LARGE,  title = 'Bull Displacement', style = shape.triangleup,   location = location.belowbar, color = dispShapeBullColor, size = size.large,  text = 'D', textcolor = dispShapeBullColor, offset = -1)
plotshape(bullDisplacement and dispMarkerSizeInput == HUGE,   title = 'Bull Displacement', style = shape.triangleup,   location = location.belowbar, color = dispShapeBullColor, size = size.huge,   text = 'D', textcolor = dispShapeBullColor, offset = -1)
plotshape(bearDisplacement and dispMarkerSizeInput == TINY,   title = 'Bear Displacement', style = shape.triangledown, location = location.abovebar, color = dispShapeBearColor, size = size.tiny,   text = 'D', textcolor = dispShapeBearColor, offset = -1)
plotshape(bearDisplacement and dispMarkerSizeInput == SMALL,  title = 'Bear Displacement', style = shape.triangledown, location = location.abovebar, color = dispShapeBearColor, size = size.small,  text = 'D', textcolor = dispShapeBearColor, offset = -1)
plotshape(bearDisplacement and dispMarkerSizeInput == NORMAL, title = 'Bear Displacement', style = shape.triangledown, location = location.abovebar, color = dispShapeBearColor, size = size.normal, text = 'D', textcolor = dispShapeBearColor, offset = -1)
plotshape(bearDisplacement and dispMarkerSizeInput == LARGE,  title = 'Bear Displacement', style = shape.triangledown, location = location.abovebar, color = dispShapeBearColor, size = size.large,  text = 'D', textcolor = dispShapeBearColor, offset = -1)
plotshape(bearDisplacement and dispMarkerSizeInput == HUGE,   title = 'Bear Displacement', style = shape.triangledown, location = location.abovebar, color = dispShapeBearColor, size = size.huge,   text = 'D', textcolor = dispShapeBearColor, offset = -1)
//---------------------------------------------------------------------------------------------------------------------}

//---------------------------------------------------------------------------------------------------------------------}

//=== Watermark ===================================================================
// "crypex" brand green · ".org" dark (white on dark charts). One table keeps the parts side by side.
var table wm = na
if showWatermark and barstate.islast
    if na(wm)
        wm := table.new(wmPos, 3, 1, border_width = 0, frame_width = 0)
    string wmSize = wmSizeStr == 'small' ? size.small : wmSizeStr == 'normal' ? size.normal : wmSizeStr == 'large' ? size.large : size.huge
    // Empty spacer cell keeps the text clear of the TradingView logo in the bottom-left corner.
    float spacerW = wmPos == position.bottom_left ? wmGap : 0.0
    bool bgLight = (color.r(chart.bg_color) * 299 + color.g(chart.bg_color) * 587 + color.b(chart.bg_color) * 114) / 1000 > 128
    color orgCol = bgLight ? BRAND_DARK : color.white
    table.cell(wm, 0, 0, '', width = spacerW, text_size = wmSize)
    table.cell(wm, 1, 0, 'crypex', text_color = color.new(BRAND_GREEN, wmTransp), text_size = wmSize, text_halign = text.align_right, text_valign = text.align_bottom, text_formatting = text.format_bold)
    table.cell(wm, 2, 0, '.org',   text_color = color.new(orgCol, wmTransp),      text_size = wmSize, text_halign = text.align_left, text_valign = text.align_bottom, text_formatting = text.format_bold)

منطق کار

  • ساختار بازار: BOS و CHoCH هم برای سوئینگ‌های بزرگ و هم برای ساختار داخلی. فیلتر «جابه‌جایی» فقط شکستی را ثبت می‌کند که کندل شکست‌دهنده بدنه‌ی قوی دارد (بر حسب ضریبی از ATR) و فیلتر «هم‌گرایی» شکست‌های ضعیف داخلی را حذف می‌کند.
  • قوی و ضعیف: آخرین سقف و کف سوئینگ با برچسب Strong/Weak High/Low مشخص می‌شوند؛ کف یا سقفی که ساختار را نگه داشته «قوی» و مقابلش «ضعیف» است.
  • اوردر بلاک: برای سوئینگ و ساختار داخلی، با فیلتر نوسان (ATR یا میانگین رنج)، حداکثر تعداد قابل تنظیم و حذف خودکار هنگام میتیگیشن (با سایه یا با بسته‌شدن کندل).
  • FVG: شکاف سه‌کندلی با گزینه‌ی «فقط FVG معتبر»: حداقل اندازه بر حسب ATR و تیک، و بدنه‌ی کندل وسط باید جابه‌جایی واقعی باشد. می‌توان FVG را از تایم‌فریم دیگر گرفت. ناحیه با اولین لمس یا بسته‌شدن قیمت از آن (به‌دلخواه) باطل می‌شود.
  • EQH و EQL: سقف‌ها و کف‌های برابر با حساسیت قابل تنظیم، به‌عنوان نشانه‌ی نقدینگی.
  • استخر نقدینگی: سطوح BSL (بالای سقف‌ها) و SSL (زیر کف‌ها) با تشخیص جاروشدن (sweep)؛ سطوح جاروشده کم‌رنگ می‌شوند و سطوح قدیمی‌تر از حد مجاز حذف می‌شوند.
  • سطوح بالا و پایین قبلی روزانه، هفتگی و ماهانه، و ناحیه‌ی پریمیوم، تعادل و دیسکانت.
  • رنگ‌بندی کندل بر اساس هم‌راستایی روند سوئینگ و داخلی: صعودی کامل، صعودی در پولبک، نزولی کامل، نزولی در باونس یا خنثی.
  • ۲۰ آلارم آماده برای شکست ساختار، تغییر کاراکتر، اوردر بلاک، سقف و کف برابر و FVG.

تنظیمات مهم

Mode / Style
نمایش تاریخی یا فقط ساختار اخیر؛ رنگی یا تک‌رنگ
Show Internal / Swing Structure
روشن یا خاموش‌کردن ساختار داخلی و سوئینگ، و انتخاب BOS، CHoCH یا هر دو
Displacement Filter (× ATR)
حداقل بدنه‌ی کندل شکست؛ ۰٫۳۰ متوسط و ۰٫۵۰ سخت‌گیرانه
Order Blocks
تعداد، فیلتر نوسان و روش میتیگیشن
Fair Value Gaps / Valid FVG Only
حداقل اندازه‌ی گپ، فیلتر جابه‌جایی و تایم‌فریم FVG
Liquidity Pools
حداکثر سطح در هر سمت، منبع جاروشدن و حداکثر عمر سطح
Daily / Weekly / Monthly
نمایش سقف و کف دوره‌ی قبل
Premium/Discount Zones
ناحیه‌ی گران، تعادل و ارزان بر اساس رنج سوئینگ
Show crypex.org watermark
واترمارک قابل خاموش‌کردن، با ورودی جایگاه، اندازه و شفافیت

کجا بهتر کار می‌کند

تحلیل چندتایم‌فریمی روی طلا، جفت‌ارزهای اصلی، شاخص‌ها و ارزهای دیجیتال پرنقدینگی؛ مثلاً ساختار و اوردر بلاک در ۴ ساعته و جست‌وجوی ورود در ۱۵ دقیقه. برای ساخت نقشه‌ی سطوح و نواحی نقدینگی قبل از ورود مناسب است.

نقطه‌ضعف

سیگنال خرید و فروش نمی‌دهد و تفسیر نواحی با خود تحلیلگر است. با همه‌ی لایه‌ها روشن، چارت شلوغ می‌شود؛ بهتر است لایه‌ها را مرحله‌ای روشن کنید. کد بزرگ و پرشیء است (تا ۵۰۰ خط، لیبل و باکس) و روی تایم‌فریم‌های خیلی پایین یا دستگاه‌های ضعیف ممکن است کند شود. فقط برای TradingView است؛ نسخه‌ی سبک‌تر برای MT4 و MT5 در همین بخش موجود است.

این کد آموزشی است و توصیه‌ی سرمایه‌گذاری نیست. قبل از استفاده روی حساب واقعی، کامپایل و رفتار آن را روی حساب دمو و در بک‌تست بررسی کنید؛ نتیجه‌ی گذشته تضمین‌کننده‌ی آینده نیست.

← همه‌ی اسکریپت‌ها