Indicator · Pine Script v6 · On the chart

Fractal Model (TTrades)

by abhinaynatraj· code credited to mindyourbuisness

An overlay that draws higher-timeframe candles beside the current chart and adds T-spot detection, sweep logic and fractal model labelling.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code2,098
Settings40inputs
LicenceNone statedfile: MPL-2.0
Last update3 months ago4 Jul 2026

Add It to Your TradingView Chart

  1. 1Copy the scriptPress Copy script above, or the copy button on the code below.
  2. 2Open the Pine EditorOpen a TradingView chart and choose Pine Editor at the bottom of the screen.
  3. 3Paste over the templateCreate a new indicator, select all of the template text and paste. Press Save and give it a name.
  4. 4Add it to the chartPress Add to chart, then adjust the settings with the gear icon next to its name.

Source Code

2,098 lines of Pine Script v6 from Fractal Sweep/pine/ttfm+fadi.pine, published under MPL-2.0 (file header). Keep the author's credit and licence lines when you reuse it.

ttfm+fadi.pine
// This Pine Script™ code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © mindyourbuisness

// Based on HTF Candles by Fadi
// Enhanced with T-spot detection, sweep logic, and TTFM labeling system

//@version=6
indicator("Fractal Model (TTrades)", shorttitle = "TTFM°", overlay = true, max_bars_back = 5000, max_boxes_count = 500, max_lines_count = 500)


//HTF Candle Start


import mindyourbuisness/CustomAlertLib/3 as CAL

// Alert Settings
alertFormat = input.text_area('{"ticker":"{ticker}", "interval":"{interval}", "alert":"{default}"}', "Custom alert message format", group="Alert Settings")


// ── 🎨 PALETTE — keep in sync with palette-block reference ────────────────────
// Source of truth: docs/superpowers/specs/2026-05-06-unified-pine-palette-block.pine
// When that file changes, copy verbatim into the block below.
// ╔══════════════════════════════════════════════════════════════════════════╗
// ║ 🎨 CANONICAL PALETTE BLOCK                                                ║
// ╚══════════════════════════════════════════════════════════════════════════╝
G_THEME = "🎨 Theme"
gTheme = input.string("Auto", "Theme",
                      options=["Auto", "Dark", "Light"],
                      group=G_THEME,
                      tooltip="Auto reads chart bg brightness; Dark/Light forces the palette.")

// All palette state assigned ONCE on the first bar via barstate.isfirst gate.
// `chart.bg_color` is a series, so reading it makes downstream colors series-
// typed and grows historical-buffer requirements on every live bar. The
// barstate.isfirst gate confines the read to a single bar.
var bool _AUTO_IS_LIGHT = false
var bool IS_LIGHT_MODE  = false
var color P_LONG_BASE   = #34d399
var color P_SHORT_BASE  = #f87171
var color P_WARN_BASE   = #fbbf24
var color P_INFO_BASE   = #38bdf8
var color P_ACCENT_BASE = #a78bfa
var color P_STRUCT_BASE = #cbd5e1
var color P_INK         = #f1f5f9
var color P_SURFACE     = #334155
var color P_ON_SURFACE  = #f1f5f9

if barstate.isfirst
    _AUTO_IS_LIGHT := (color.r(chart.bg_color) + color.g(chart.bg_color) + color.b(chart.bg_color)) / 3.0 > 127
    IS_LIGHT_MODE  := gTheme == "Light" or (gTheme == "Auto" and _AUTO_IS_LIGHT)
    P_LONG_BASE    := IS_LIGHT_MODE ? #047857 : #34d399
    P_SHORT_BASE   := IS_LIGHT_MODE ? #b91c1c : #f87171
    P_WARN_BASE    := IS_LIGHT_MODE ? #b45309 : #fbbf24
    P_INFO_BASE    := IS_LIGHT_MODE ? #0369a1 : #38bdf8
    P_ACCENT_BASE  := IS_LIGHT_MODE ? #6d28d9 : #a78bfa
    P_STRUCT_BASE  := IS_LIGHT_MODE ? #475569 : #cbd5e1
    P_INK          := IS_LIGHT_MODE ? #0f172a : #f1f5f9

tier_ink(color c)     => color.new(c, 0)
tier_strong(color c)  => color.new(c, 25)
tier_mid(color c)     => color.new(c, 60)
tier_soft(color c)    => color.new(c, 85)
tier_whisper(color c) => color.new(c, 95)

pal_long_ink()       => tier_ink(P_LONG_BASE)
pal_long_strong()    => tier_strong(P_LONG_BASE)
pal_long_mid()       => tier_mid(P_LONG_BASE)
pal_long_soft()      => tier_soft(P_LONG_BASE)
pal_long_whisper()   => tier_whisper(P_LONG_BASE)
pal_short_ink()      => tier_ink(P_SHORT_BASE)
pal_short_strong()   => tier_strong(P_SHORT_BASE)
pal_short_mid()      => tier_mid(P_SHORT_BASE)
pal_short_soft()     => tier_soft(P_SHORT_BASE)
pal_short_whisper()  => tier_whisper(P_SHORT_BASE)
pal_warn_ink()       => tier_ink(P_WARN_BASE)
pal_warn_strong()    => tier_strong(P_WARN_BASE)
pal_warn_mid()       => tier_mid(P_WARN_BASE)
pal_warn_soft()      => tier_soft(P_WARN_BASE)
pal_warn_whisper()   => tier_whisper(P_WARN_BASE)
pal_info_ink()       => tier_ink(P_INFO_BASE)
pal_info_strong()    => tier_strong(P_INFO_BASE)
pal_info_mid()       => tier_mid(P_INFO_BASE)
pal_info_soft()      => tier_soft(P_INFO_BASE)
pal_info_whisper()   => tier_whisper(P_INFO_BASE)
pal_accent_ink()     => tier_ink(P_ACCENT_BASE)
pal_accent_strong()  => tier_strong(P_ACCENT_BASE)
pal_accent_mid()     => tier_mid(P_ACCENT_BASE)
pal_accent_soft()    => tier_soft(P_ACCENT_BASE)
pal_accent_whisper() => tier_whisper(P_ACCENT_BASE)
pal_struct_ink()     => tier_ink(P_STRUCT_BASE)
pal_struct_strong()  => tier_strong(P_STRUCT_BASE)
pal_struct_mid()     => tier_mid(P_STRUCT_BASE)
pal_struct_soft()    => tier_soft(P_STRUCT_BASE)
pal_struct_whisper() => tier_whisper(P_STRUCT_BASE)
pal_ink()            => P_INK
pal_surface()        => P_SURFACE
pal_surface_strong() => tier_strong(P_SURFACE)
pal_surface_soft()   => tier_soft(P_SURFACE)
pal_on_surface()     => P_ON_SURFACE
// ╚══════════════════════════════════════════════════════════════════════════╝

TEMPLATE_COLOR = pal_ink()
// Line Styles
var string LINE_STYLE_SOLID = line.style_solid
var string LINE_STYLE_DASHED = line.style_dashed
var string LINE_STYLE_DOTTED = line.style_dotted

// Text Sizes
var string SIZE_TINY = size.tiny
var string SIZE_SMALL = size.small
var string SIZE_NORMAL = size.normal
var string SIZE_LARGE = size.large
var string SIZE_HUGE = size.huge







type Candle
    float           o
    float           c
    float           h
    float           l
    int             o_idx
    int             c_idx
    int             h_idx
    int             l_idx
    box             body
    line            wick_up
    line            wick_down

type Trace
    line            o
    line            c
    line            h
    line            l
    label           o_l
    label           c_l
    label           h_l
    label           l_l

type Imbalance
    box             b
    int             idx

type CandleSettings
    bool            show
    string          htf
    int             max_display

type Settings
    int             max_sets
    color           bull_body
    color           bull_border
    color           bull_wick
    color           bear_body
    color           bear_border
    color           bear_wick
    int             offset
    int             buffer
    int             htf_buffer
    int             width
    bool            trace_show
    color           trace_o_color
    string          trace_o_style
    int             trace_o_size
    color           trace_c_color
    string          trace_c_style
    int             trace_c_size
    color           trace_h_color
    string          trace_h_style
    int             trace_h_size
    color           trace_l_color
    string          trace_l_style
    int             trace_l_size
    string          trace_anchor
    bool            label_show
    color           label_color
    string          label_size
    bool            fvg_show
    color           fvg_color
    bool            vi_show
    color           vi_color
    bool            htf_label_show
    color           htf_label_color
    string          htf_label_size
    bool            htf_timer_show
    color           htf_timer_color
    string          htf_timer_size
    bool            htf_line_show
    color           htf_line_color
    int             htf_line_width
    string          htf_line_style
    int             midline_width
    bool            show_chart_sweeps

type CandleSet
    Candle[]        candles
    Imbalance[]     imbalances
    CandleSettings  settings
    label           tfName
    label           tfTimer
    line[]          htf_starts

type Helper
    string name             = "Helper"

Settings settings           = Settings.new()



var CandleSettings SettingsHTF1 = CandleSettings.new()
var CandleSettings SettingsHTF2 = CandleSettings.new()
var CandleSettings SettingsHTF3 = CandleSettings.new()
var CandleSettings SettingsHTF4 = CandleSettings.new()
var CandleSettings SettingsHTF5 = CandleSettings.new()
var CandleSettings SettingsHTF6 = CandleSettings.new()

var Candle[] candles_1        = array.new<Candle>(0)
var Candle[] candles_2        = array.new<Candle>(0)
var Candle[] candles_3        = array.new<Candle>(0)
var Candle[] candles_4        = array.new<Candle>(0)
var Candle[] candles_5        = array.new<Candle>(0)
var Candle[] candles_6        = array.new<Candle>(0)

var Imbalance[] imbalances_1    = array.new<Imbalance>()
var Imbalance[] imbalances_2    = array.new<Imbalance>()
var Imbalance[] imbalances_3    = array.new<Imbalance>()
var Imbalance[] imbalances_4    = array.new<Imbalance>()
var Imbalance[] imbalances_5    = array.new<Imbalance>()
var Imbalance[] imbalances_6    = array.new<Imbalance>()

var CandleSet htf1          = CandleSet.new()
htf1.settings               := SettingsHTF1
htf1.candles                := candles_1
htf1.imbalances             := imbalances_1
htf1.htf_starts            := array.new<line>()

var CandleSet htf2          = CandleSet.new()
htf2.settings               := SettingsHTF2
htf2.candles                := candles_2
htf2.imbalances             := imbalances_2
htf2.htf_starts            := array.new<line>()

var CandleSet htf3          = CandleSet.new()
htf3.settings               := SettingsHTF3
htf3.candles                := candles_3
htf3.imbalances             := imbalances_3
htf3.htf_starts            := array.new<line>()

var CandleSet htf4          = CandleSet.new()
htf4.settings               := SettingsHTF4
htf4.candles                := candles_4
htf4.imbalances             := imbalances_4
htf4.htf_starts            := array.new<line>()

var CandleSet htf5          = CandleSet.new()
htf5.settings               := SettingsHTF5
htf5.candles                := candles_5
htf5.imbalances             := imbalances_5
htf5.htf_starts            := array.new<line>()

var CandleSet htf6          = CandleSet.new()
htf6.settings               := SettingsHTF6
htf6.candles                := candles_6
htf6.imbalances             := imbalances_6
htf6.htf_starts            := array.new<line>()

//+------------------------------------------------------------------------------------------------------------+//
//+--- Settings                                                                                             ---+//
//+------------------------------------------------------------------------------------------------------------+//

// HTF Selection - Auto or Custom
htf_mode = input.string("Auto", "HTF Mode", options=["Auto", "Custom"], group="HTF Candle")
custom_htf = input.timeframe("1W", "Custom Timeframe", group="HTF Candle")
tspot_bias = input.string("None", "T-Spot Bias", options=["None", "Bullish", "Bearish"], group="HTF Candle")

htf1.settings.show          := input.bool(true, "Show HTF", group="HTF Candle")
htf1.settings.max_display   := input.int(4, "Max Display", group="HTF Candle")
use_actual_day_change       = input.bool(false, "Use Actual Day Change for Daily HTF", group="HTF Candle", tooltip="When enabled, uses dayofmonth() comparison instead of ta.change() for daily timeframe detection")

// Auto HTF Detection Logic
htf_1 = if htf_mode == "Auto"
    if timeframe.period == "1"
        "15"
    else if timeframe.period == "3"
        "30"
    else if timeframe.period == "5"
        "60"
    else if timeframe.period == "15"
        "240"
    else if timeframe.period == "30" or timeframe.period == "60"
        "1D"
    else if timeframe.period == "240" or timeframe.period == "480"
        "1W"
    else if timeframe.period == "1D"
        "1M"
    else
        "1W"  // Default fallback
else
    custom_htf

htf1.settings.htf := htf_1

htf2.settings.show          := false
htf3.settings.show          := false
htf4.settings.show          := false
htf5.settings.show          := false
htf6.settings.show          := false

settings.max_sets           := 1

settings.bull_body       := pal_long_strong()
settings.bear_body       := pal_short_strong()
settings.bull_border     := pal_long_mid()
settings.bear_border     := pal_short_mid()
settings.bull_wick       := pal_long_mid()
settings.bear_wick       := pal_short_mid()

settings.offset          := input.int(20, "padding from current candles", minval = 1, group="HTF Candle")
settings.buffer          := input.int(1, "space between candles", minval = 1, maxval = 4, group="HTF Candle")
settings.htf_buffer      := input.int(5, "space between Higher Timeframes", minval = 1, maxval = 10, group="HTF Candle")
settings.width           := input.int(1, "Candle Width", minval = 1, maxval = 4, group="HTF Candle")*2

settings.htf_label_show  := input.bool(true, "HTF Label           ", inline="HTFlabel", group="HTF Candle")
settings.htf_label_color := pal_ink()
settings.htf_label_size  := input.string(SIZE_NORMAL, "", [SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE], inline="HTFlabel", group="HTF Candle")

settings.htf_timer_show  := input.bool(true, "Remaining time      ", inline="timer", group="HTF Candle")
settings.htf_timer_color := pal_ink()
settings.htf_timer_size  := input.string(SIZE_NORMAL, "", [SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE], inline="timer", group="HTF Candle")

// Table display settings
show_table = input.bool(true, "Show Info Table", group="HTF Candle")
table_position = input.string("Top Right", "Table Position", options=["Top Left", "Top Center", "Top Right", "Middle Left", "Middle Center", "Middle Right", "Bottom Left", "Bottom Center", "Bottom Right"], group="HTF Candle")
table_bg_color = pal_surface()
// Solid border/frame so they're actually visible on the dark surface bg
// (pal_struct_strong / pal_struct_mid are 25%/60% transparent and read as
// near-invisible stripes against pal_surface).
table_border_color = color.new(pal_on_surface(), 70)
table_text_color = pal_on_surface()
table_size = input.string(SIZE_SMALL, "Table Text Size", options=[SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE], group="HTF Candle")
table_border_width = input.int(1, "Table Border Width", minval=0, maxval=5, group="HTF Candle")
table_frame_color = color.new(pal_on_surface(), 40)
table_frame_width = input.int(1, "Table Frame Width", minval=0, maxval=5, group="HTF Candle")

settings.fvg_show        := input.bool(true, "Fair Value Gap   ", inline="fvg", group="HTF Candle")
settings.fvg_color       := pal_struct_soft()

settings.vi_show         := input.bool(true, "Volume Imbalance", inline="vi", group="HTF Candle")
settings.vi_color        := pal_warn_mid()

settings.trace_show      := input.bool(false, "Trace lines", group="HTF Candle")
settings.trace_o_color   := pal_struct_mid()
settings.trace_o_style   := input.string(LINE_STYLE_DOTTED, '', options = [LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline='1', group="HTF Candle")
settings.trace_o_size    := input.int(1, '', options = [1,2,3,4], inline='1', group="HTF Candle")
settings.trace_c_color   := pal_struct_mid()
settings.trace_c_style   := input.string(LINE_STYLE_DOTTED, '', options = [LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline='2', group="HTF Candle")
settings.trace_c_size    := input.int(1, '', options = [1,2,3,4], inline='2', group="HTF Candle")
settings.trace_h_color   := pal_struct_mid()
settings.trace_h_style   := input.string(LINE_STYLE_DOTTED, '', options = [LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline='3', group="HTF Candle")
settings.trace_h_size    := input.int(1, '', options = [1,2,3,4], inline='3', group="HTF Candle")
settings.trace_l_color   := pal_struct_mid()
settings.trace_l_style   := input.string(LINE_STYLE_DOTTED, '', options = [LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline='4', group="HTF Candle")
settings.trace_l_size    := input.int(1, '', options = [1,2,3,4], inline='4', group="HTF Candle")
settings.trace_anchor    := input.string("First Timeframe", "Anchor to", options=["First Timeframe", "Last Timeframe"], group="HTF Candle")

settings.label_show      := input.bool(false, "Price Label           ", inline="label", group="HTF Candle")
settings.label_color     := pal_surface()
settings.label_size      := input.string(size.small, "", [size.tiny, size.small, size.normal, size.large, size.huge], inline="label", group="HTF Candle")

settings.htf_line_show   := input.bool(true, "HTF Start Line    ", inline="htfline", group="HTF Candle")
settings.htf_line_color  := pal_ink()
settings.htf_line_width  := input.int(1, "", options=[1,2,3], inline="htfline", group="HTF Candle")
settings.htf_line_style  := input.string(LINE_STYLE_DOTTED, "", options=[LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline="htfline", group="HTF Candle")
settings.show_chart_sweeps := input.bool(true, "Show Chart Sweeps", inline="sweeps", group="HTF Candle")
show_tspot = input.bool(true, "Show T-Spot", inline="tspot", group="HTF Candle")
extend_latest_tspot = input.bool(true, "Extend Latest T-Spot to Current Bar", inline="tspot2", group="HTF Candle")
show_only_latest_tspot = input.bool(true, "Show Only Latest T-Spot", inline="tspot2", group="HTF Candle")
show_only_latest_sweep = input.bool(false, "Show Only Latest Sweep", inline="tspot2", group="HTF Candle")
show_tspot_sweeps = input.bool(true, "Show T-Spot Sweeps", inline="tspot_sweeps", group="HTF Candle")
tspot_sweep_label_color = pal_ink()
tspot_sweep_label_size = input.string(SIZE_NORMAL, "Label Size", options=[SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE ], inline="tspot_sweeps", group="HTF Candle")
show_confirmation_lines = input.bool(true, "Show Confirmation Lines", inline="tspot_sweeps", group="HTF Candle")
usebody_for_confirmation = input.bool(true, "Use Body for Confirmation", inline="tspot_sweeps", group="HTF Candle")
show_confirmation_labels = input.bool(true, "Show Confirmation Labels", inline="tspot_sweeps", group="HTF Candle")
confirmation_line_style = input.string(LINE_STYLE_SOLID, "Confirmation Line Style", options=[LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline="tspot", group="HTF Candle")
show_ttfm_labels = input.bool(true, "Show TTFM Labels", inline="ttfm", group="HTF Candle")
ttfm_label_size = input.string(SIZE_SMALL, "TTFM Label Size", options=[SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE], inline="ttfm", group="HTF Candle")
show_tspot_close_line = input.bool(true, "Show T-Spot Close Line", inline="tspot_close", group="HTF Candle")

// CISD and Projections Settings
show_cisd = input.bool(true, "Show CISD Lines", group="CISD & Projections")
cisd_color = pal_info_ink()
cisd_width = input.int(2, "CISD Line Width", minval=1, maxval=5, group="CISD & Projections")
show_projections = input.bool(true, "Show Projections", group="CISD & Projections")
projection_color = pal_struct_mid()
projection_levels = input.string("0.5,1.0,1.5,2.0", "Projection Levels (comma-separated)", group="CISD & Projections")
extend_latest_projections = input.bool(true, "Extend Latest Projections to Current Bar", group="CISD & Projections")
projection_label_size = input.string(SIZE_TINY, "Projection Label Size", options=[SIZE_TINY, SIZE_SMALL, SIZE_NORMAL, SIZE_LARGE, SIZE_HUGE], group="CISD & Projections")
tspot_line_style = input.string(LINE_STYLE_SOLID, "T-Spot Line Style", options=[LINE_STYLE_SOLID, LINE_STYLE_DASHED, LINE_STYLE_DOTTED], inline="tspot", group="HTF Candle")
tspot_transparency = input.int(90, "T-Spot Transparency", minval=0, maxval=100, inline="tspot", group="HTF Candle")
settings.midline_width := input.int(1, "Midline Width", minval=1, maxval=4, inline="tspot", group="HTF Candle")
show_silver_tspot = input.bool(true, "Show Silver T-Spot", inline="silver_tspot", group="HTF Candle")

// Position Sizing Settings
string GRP_POSITION_SIZING = 'Position Sizing'
showPositionSize = input.bool(false, 'Show Position Size for Latest Signal', group=GRP_POSITION_SIZING)
accountBalance = input.float(10000.0, 'Account Balance (USD)', minval=0.0, group=GRP_POSITION_SIZING)
riskType = input.string('Percentage', 'Risk Type', options=['Percentage', 'Fixed Amount'], group=GRP_POSITION_SIZING)
riskAmount = input.float(1.0, 'Risk Amount (% or USD)', minval=0.0, group=GRP_POSITION_SIZING)
usingProp = input.bool(false, 'Using Prop Firm', group=GRP_POSITION_SIZING, tooltip='Use prop firm contract size calculations')
abbreviatePositionInfo = input.bool(false, 'Abbreviate Position Info', group=GRP_POSITION_SIZING, tooltip='Shorten position sizing text (Size, Entry, Stop, contracts, units)')
hide_against_tspots = input.bool(true, "Hide T-Spots When Trading Against", inline="silver_tspot", group="HTF Candle")
delete_against_tspots = input.bool(false, "Delete T-Spots When Trading Against", inline="silver_tspot", group="HTF Candle")

var line[] htf_lines = array.new_line(0)
var box[] tspot_boxes = array.new_box(0)
var line[] tspot_midlines = array.new_line(0)
var line[] tspot_closelines = array.new_line(0)
var line[] sweep_lines = array.new_line(0)
var line[] midpoint_lines = array.new_line(0)
var label[] c2_labels = array.new_label(0)
var label[] c3_labels = array.new_label(0)
var line[] confirmation_lines = array.new_line(0)

// Table element
var table info_table = na

// T-spot sweep confirmation tracking
var bool tspot_touched = false
var string tspot_type = ""
var float tspot_level = 0.0
var int touch_bar = 0
var int last_tspot_start_bar = 0
var float last_pivot_high = na
var float last_pivot_low = na
var int last_pivot_high_bar = 0
var int last_pivot_low_bar = 0

// Latest confirmed sweep signal data for position sizing
var float latest_entry_price = na
var float latest_stop_price = na
var string latest_signal_type = ""
var float latest_position_size = na

// Track table dimensions
var int current_table_rows = 3


// Function to find bar index where level was formed
findLevelBarIndex(target_level, max_bars_back) =>
    var int found_bar = 0
    for i = 0 to max_bars_back
        if high[i] == target_level or low[i] == target_level
            found_bar := bar_index - i
            break
    found_bar

// Get contract size for common futures
getContractSize() =>
    if usingProp
        ticker = syminfo.ticker
        if syminfo.type == "forex" or str.contains(ticker,"6")
            100000.0
        // Check for micro contracts first (they should be prioritized)
        else if str.contains(ticker, "MES") // Micro E-mini S&P 500
            1.0
        else if str.contains(ticker, "MNQ") // Micro E-mini Nasdaq-100
            1.0
        else if str.contains(ticker, "MYM") // Micro E-mini Dow
            1.0
        else if str.contains(ticker, "M2K") // Micro Russell 2000
            1.0
        else if str.contains(ticker, "MGC") // Micro Gold
            1.0
        else if str.contains(ticker, "MSI") // Micro Silver
            1.0
        else if str.contains(ticker, "MCL") // Micro Crude Oil
            1.0
        else if str.contains(ticker, "MNG") // Micro Natural Gas
            1.0
        else if str.contains(ticker, "MZC") // Micro Corn
            1.0
        else if str.contains(ticker, "MZW") // Micro Wheat
            1.0
        else if str.contains(ticker, "MZS") // Micro Soybeans
            1.0
        else if str.contains(ticker, "MHG") // Micro Copper
            1.0
        // Micro Currency contracts (1/10th of regular currency contracts)
        else if str.contains(ticker, "M6E") // Micro Euro FX
            1.0
        else if str.contains(ticker, "M6J") // Micro Japanese Yen
            1.0
        else if str.contains(ticker, "M6B") // Micro British Pound
            1.0
        else if str.contains(ticker, "M6A") // Micro Australian Dollar
            1.0
        else if str.contains(ticker, "M6C") // Micro Canadian Dollar
            1.0
        else if str.contains(ticker, "M6S") // Micro Swiss Franc
            1.0
        else if str.contains(ticker, "M6N") // Micro New Zealand Dollar
            1.0
        else if str.contains(ticker, "M6M") // Micro Mexican Peso
            1.0
        // Regular contracts
        else if str.contains(ticker, "ES") or str.contains(ticker, "NQ") or str.contains(ticker, "YM") or str.contains(ticker, "RTY") or str.contains(ticker, "DJI") or str.contains(ticker, "USTEC") or str.contains(ticker, "US100") or str.contains(ticker, "US500") or str.contains(ticker, "SP500") or str.contains(ticker, "US2000") or str.contains(ticker, "SPX") or str.contains(ticker, "NDX") or str.contains(ticker, "DJX") or str.contains(ticker, "USTECX") or str.contains(ticker, "US100X") or str.contains(ticker, "US500X") or str.contains(ticker, "SP500X") or str.contains(ticker, "US2000X")
            1.0
        else if str.contains(ticker, "GC") or str.contains(ticker, "XAU") or str.contains(ticker, "GOLD")
            100.0
        else if str.contains(ticker, "SI") or str.contains(ticker, "XAG") or str.contains(ticker, "SILVER")
            5000.0
        else if str.contains(ticker, "NG") or str.contains(ticker, "NATGAS") or str.contains(ticker, "NATURALGAS")
            1000.0
        else if str.contains(ticker, "HO") or str.contains(ticker, "HEATOIL") or str.contains(ticker, "HEATINGOIL")
            100.0
        else if str.contains(ticker, "DXY") or str.contains(ticker, "DX") or str.contains(ticker, "DOLLARINDEX")
            100.0
        else if str.contains(ticker, "CL") or str.contains(ticker, "OIL") or str.contains(ticker, "CRUDE") or str.contains(ticker, "BRENT") or str.contains(ticker, "WTI")
            100.0
        else if str.contains(ticker, "ZC") or str.contains(ticker, "CORN")
            5000.0
        else if str.contains(ticker, "ZW") or str.contains(ticker, "WHEAT")
            5000.0
        else if str.contains(ticker, "ZS") or str.contains(ticker, "SOYBEANS")
            5000.0
        else if str.contains(ticker, "HG") or str.contains(ticker, "COPPER")
            25000.0
        else if str.contains(ticker, "PL") or str.contains(ticker, "PLATINUM")
            50.0
        else if str.contains(ticker, "PA") or str.contains(ticker, "PALLADIUM")
            100.0
        else
            1.0
    else
        ticker = syminfo.ticker
        // Check for micro contracts first (they should be prioritized)
        if str.contains(ticker, "MES") // Micro E-mini S&P 500
            5.0 // $5 x S&P 500 Index (1/10th of ES)
        else if str.contains(ticker, "MNQ") // Micro E-mini Nasdaq-100
            2.0 // $2 x Nasdaq-100 Index (1/10th of NQ)
        else if str.contains(ticker, "MYM") // Micro E-mini Dow
            0.5 // $0.5 x DJIA (1/10th of YM)
        else if str.contains(ticker, "M2K") // Micro Russell 2000
            5.0 // $5 x Russell 2000 Index
        else if str.contains(ticker, "MGC") // Micro Gold
            10.0 // 10 oz per contract (1/10th of GC)
        else if str.contains(ticker, "MSI") // Micro Silver
            1000.0 // 1000 oz per contract (1/5th of SI)
        else if str.contains(ticker, "MCL") // Micro Crude Oil
            100.0 // 100 barrels per contract (1/10th of CL)
        else if str.contains(ticker, "MNG") // Micro Natural Gas
            1000.0 // 1000 MMBtu per contract (1/10th of NG)
        else if str.contains(ticker, "MZC") // Micro Corn
            500.0 // 500 bushels per contract (1/10th of ZC)
        else if str.contains(ticker, "MZW") // Micro Wheat
            500.0 // 500 bushels per contract (1/10th of ZW)
        else if str.contains(ticker, "MZS") // Micro Soybeans
            500.0 // 500 bushels per contract (1/10th of ZS)
        else if str.contains(ticker, "MHG") // Micro Copper
            2500.0 // 2500 lbs per contract (1/10th of HG)
        // Micro Currency contracts (1/10th of regular currency contracts)
        else if str.contains(ticker, "M6E") // Micro Euro FX
            12500.0 // 12,500 EUR per contract (1/10th of 6E)
        else if str.contains(ticker, "M6J") // Micro Japanese Yen
            1250000.0 // 1.25 million JPY per contract (1/10th of 6J)
        else if str.contains(ticker, "M6B") // Micro British Pound
            6250.0 // 6,250 GBP per contract (1/10th of 6B)
        else if str.contains(ticker, "M6A") // Micro Australian Dollar
            10000.0 // 10,000 AUD per contract (1/10th of 6A)
        else if str.contains(ticker, "M6C") // Micro Canadian Dollar
            10000.0 // 10,000 CAD per contract (1/10th of 6C)
        else if str.contains(ticker, "M6S") // Micro Swiss Franc
            12500.0 // 12,500 CHF per contract (1/10th of 6S)
        else if str.contains(ticker, "M6N") // Micro New Zealand Dollar
            10000.0 // 10,000 NZD per contract (1/10th of 6N)
        else if str.contains(ticker, "M6M") // Micro Mexican Peso
            50000.0 // 50,000 MXN per contract (1/10th of 6M)
        // Regular contracts
        else if str.contains(ticker, "GC") or str.contains(ticker, "XAU") or str.contains(ticker, "GOLD") // Gold futures and CFDs
            100.0 // 100 oz per contract
        else if str.contains(ticker, "SI") or str.contains(ticker, "XAG") or str.contains(ticker, "SILVER") // Silver futures and CFDs
            5000.0 // 5000 oz per contract
        else if str.contains(ticker, "CL") // Crude Oil futures
            1000.0 // 1000 barrels per contract
        else if str.contains(ticker, "NG") // Natural Gas
            10000.0 // 10,000 MMBtu per contract
        else if str.contains(ticker, "ZC") // Corn
            5000.0 // 5,000 bushels per contract
        else if str.contains(ticker, "ZW") // Wheat
            5000.0 // 5,000 bushels per contract
        else if str.contains(ticker, "ZS") // Soybeans
            5000.0 // 5,000 bushels per contract
        else if str.contains(ticker, "HG") // Copper
            25000.0 // 25,000 lbs per contract
        else if str.contains(ticker, "PL") // Platinum
            50.0 // 50 oz per contract
        else if str.contains(ticker, "PA") // Palladium
            100.0 // 100 oz per contract
        else if str.contains(ticker, "ES") // E-mini S&P 500
            50.0 // $50 x S&P 500 Index
        else if str.contains(ticker, "NQ") // E-mini Nasdaq-100
            20.0 // $20 x Nasdaq-100 Index
        else if str.contains(ticker, "YM") // E-mini Dow
            5.0 // $5 x DJIA
        else if str.contains(ticker, "NKD") // Nikkei 225
            5.0 // $5 x Nikkei 225 Index
        // Currency futures (typically start with a number)
        else if str.contains(ticker, "6E") // Euro FX
            125000.0 // 125,000 EUR per contract
        else if str.contains(ticker, "6J") // Japanese Yen
            12500000.0 // 12.5 million JPY per contract
        else if str.contains(ticker, "6B") // British Pound
            62500.0 // 62,500 GBP per contract
        else if str.contains(ticker, "6A") // Australian Dollar
            100000.0 // 100,000 AUD per contract
        else if str.contains(ticker, "6C") // Canadian Dollar
            100000.0 // 100,000 CAD per contract
        else if str.contains(ticker, "6S") // Swiss Franc
            125000.0 // 125,000 CHF per contract
        else if str.contains(ticker, "6N") // New Zealand Dollar
            100000.0 // 100,000 NZD per contract
        else if str.contains(ticker, "6M") // Mexican Peso
            500000.0 // 500,000 MXN per contract
        else
            1.0 // Default multiplier

// Function to calculate position size based on risk parameters
calculatePositionSize(entry, stop, riskUsd) =>
    riskPerUnit = math.abs(entry - stop)
    positionSize = riskPerUnit != 0 ? riskUsd / riskPerUnit : 0
    
    // Format based on market type
    detectedMarket = syminfo.type
    if detectedMarket == "forex"
        // For forex pairs where quote currency is not USD, adjust position size calculation
        ticker = syminfo.ticker
        needsConversion = not (str.contains(ticker, "USD") and str.endswith(ticker, "USD")) or str.contains(ticker, "6") // USD not quote currency or currency futures
        if needsConversion
            positionSize := (positionSize * entry) / 100000.0  // Adjust for currency conversion and convert to lots
        else
            positionSize := positionSize / 100000.0  // Convert to lots
    else if detectedMarket == "futures" or detectedMarket == "future" or detectedMarket == "cfd" or detectedMarket == "index"
        contractSize = getContractSize()
        positionSize := positionSize / contractSize  // Convert to contracts/units
    else if detectedMarket == "crypto" or detectedMarket == "stock" or detectedMarket == "other"
        // For crypto and stocks, position size is already in the correct units
        // No division needed - the calculation is already correct
        positionSize := positionSize
    else
        positionSize := positionSize
    
    positionSize

// Function to get precision string (e.g., "#####" for 5 decimals)
getPrecisionString() =>
    int decimals = int(math.log10(1 / syminfo.mintick))
    string precisionString = ''
    for i = 0 to decimals - 1 by 1
        precisionString := precisionString + '#'
        precisionString
    precisionString

// Function to format position size based on market type
formatPositionSize(size) =>
    detectedMarket = syminfo.type
    sizeText = ""
    if detectedMarket == "forex"
        sizeText := str.tostring(size, '#.##') + (abbreviatePositionInfo ? ' lt' : ' lots')
    else if detectedMarket == "futures" or detectedMarket == "future" or detectedMarket == "cfd" or detectedMarket == "index"
        sizeText := str.tostring(size, '#.##') + (abbreviatePositionInfo ? ' ct' : ' contracts')
    else if detectedMarket == "crypto" or detectedMarket == "stock"
        sizeText := str.tostring(size, '#.####') + (abbreviatePositionInfo ? ' un' : ' units')
    else
        sizeText := str.tostring(size, '#.####')
    sizeText

// Projection arrays for extension management
var array<line> projection_lines = array.new<line>()
var array<label> projection_labels = array.new<label>()

// Complete CISD and Projections system - no arrays
detectCISDAndProjections(int c2_bar, float c2_extreme_level, bool is_bullish, int end_bar) =>
    
    // Find the series by going back from C2 bar until polarity changes
    series_high = usebody_for_confirmation ? math.max(open[bar_index - c2_bar], close[bar_index - c2_bar]) : c2_extreme_level
    series_low = usebody_for_confirmation ? math.min(open[bar_index - c2_bar], close[bar_index - c2_bar]) : c2_extreme_level
    series_count = 0
    
    // Always include the C2 bar in the series (it can be as little as 1 candle)
    series_count := 1
    
    // Go back from C2 bar until polarity changes
    for i = 1 to 20
        check_bar = c2_bar - i
        if check_bar < 0
            break
        
        // Check candle polarity
        is_bullish_candle = close[bar_index - check_bar] > open[bar_index - check_bar]
        
        if is_bullish
            // For bullish T-spot, we want down candles (bearish candles)
            if not is_bullish_candle
                candle_high = usebody_for_confirmation ? math.max(open[bar_index - check_bar], close[bar_index - check_bar]) : high[bar_index - check_bar]
                candle_low = usebody_for_confirmation ? math.min(open[bar_index - check_bar], close[bar_index - check_bar]) : low[bar_index - check_bar]
                series_high := math.max(series_high, candle_high)
                series_low := math.min(series_low, candle_low)
                series_count += 1
            else
                break
        else
            // For bearish T-spot, we want up candles (bullish candles)
            if is_bullish_candle
                candle_high = usebody_for_confirmation ? math.max(open[bar_index - check_bar], close[bar_index - check_bar]) : high[bar_index - check_bar]
                candle_low = usebody_for_confirmation ? math.min(open[bar_index - check_bar], close[bar_index - check_bar]) : low[bar_index - check_bar]
                series_high := math.max(series_high, candle_high)
                series_low := math.min(series_low, candle_low)
                series_count += 1
            else
                break
    
    // Look forward from C2 bar to find where series breaks
    series_broken = false
    break_bar = 0
    
    // Check from C2 bar forward to current bar
    for i = 0 to (bar_index - c2_bar)
        check_bar = c2_bar + i
        if check_bar > bar_index
            break
        
        if is_bullish
            // For bullish T-spot, check if price closed above series high
            if close[bar_index - check_bar] > series_high
                series_broken := true
                break_bar := check_bar
                break
        else
            // For bearish T-spot, check if price closed below series low
            if close[bar_index - check_bar] < series_low
                series_broken := true
                break_bar := check_bar
                break
    
    // If series was broken, draw CISD line and projections
    if series_broken
        // Draw CISD line if enabled
        if show_cisd
            // CISD line is horizontal from last bar in series to break point
            series_extreme = is_bullish ? series_high : series_low  // For bullish: series high, for bearish: series low
            // Find the last bar in the series (furthest back from C2)
            last_series_bar = c2_bar - (series_count - 1)
            line.new(last_series_bar, series_extreme, break_bar, series_extreme, color=cisd_color, style=line.style_solid, width=cisd_width)
        
        // Draw projections if enabled
        if show_projections
            series_range = series_high - series_low
            
            // Only draw projections if series range is not 0
            if series_range > 0
                break_price = is_bullish ? series_high : series_low
                
                // Parse projection levels from user input
                levels = str.split(projection_levels, ",")
                for i = 0 to array.size(levels) - 1
                    level_str = str.trim(array.get(levels, i))
                    if level_str != ""
                        level = str.tonumber(level_str)
                        if not na(level)
                            // For bullish: project UP from break price, for bearish: project DOWN from break price
                            proj_price = is_bullish ? break_price + (series_range * level) : break_price - (series_range * level)
                            
                            // Draw line from break_bar to bar_index (or end_bar if not extending)
                            line_end = extend_latest_projections ? bar_index : end_bar
                            proj_line = line.new(bar_index, proj_price, line_end, proj_price, color=projection_color, style=line.style_dotted, width=1)
                            proj_label = label.new(bar_index, proj_price, str.tostring(level), color=color.new(color.white, 100), textcolor=projection_color, style=label.style_label_right, size=projection_label_size, text_font_family=font.family_monospace)
                            
                            // Store in arrays for extension management
                            array.unshift(projection_lines, proj_line)
                            array.unshift(projection_labels, proj_label)

//+------------------------------------------------------------------------------------------------------------+//
//+--- Variables                                                                                            ---+//
//+------------------------------------------------------------------------------------------------------------+//

Helper    helper        = Helper.new()
var Trace trace         = Trace.new()
color color_transparent = #ffffff00

//+------------------------------------------------------------------------------------------------------------+//
//+--- Internal Functions                                                                                   ---+//
//+------------------------------------------------------------------------------------------------------------+//

method LineStyle(Helper helper, string style) =>
    helper.name := style
    out = switch style
        '----' => line.style_dashed
        '····' => line.style_dotted
        => line.style_solid


method ValidTimeframe(Helper helper, string HTF) =>
    helper.name := HTF
    if timeframe.in_seconds(HTF) >= timeframe.in_seconds("1D") and timeframe.in_seconds(HTF) > timeframe.in_seconds()
        true
    else
        n1 = timeframe.in_seconds()
        n2 = timeframe.in_seconds(HTF)
        n3 = n1 % n2
        (n1 < n2 and math.round(n2/n1) == n2/n1)


method RemainingTime(Helper helper, string HTF) =>
    helper.name     := HTF
    if barstate.isrealtime
        timeRemaining   = (time_close(HTF) - timenow)/1000
        days            = math.floor(timeRemaining / 86400)
        hours           = math.floor((timeRemaining - (days*86400)) / 3600)
        minutes         = math.floor((timeRemaining - (days*86400) - (hours*3600))/ 60)
        seconds         = math.floor(timeRemaining - (days*86400) - (hours*3600) - (minutes*60))

        r = str.tostring(seconds, "00")
        if minutes > 0 or hours > 0 or days > 0
            r := str.tostring(minutes, "00") + ":" + r
        if hours > 0 or days > 0
            r := str.tostring(hours, "00") + ":" + r
        if days > 0
            r := str.tostring(days) + "D " + r
        r
    else
        "n/a"

method HTFName(Helper helper, string HTF) =>
    helper.name := "HTFName"
    formatted = HTF

    seconds = timeframe.in_seconds(HTF)
    if seconds < 60
        formatted := str.tostring(seconds) + "S"
    else if (seconds / 60) < 60
        formatted := str.tostring((seconds/60)) + "M"
    else if (seconds/60/60) < 24
        formatted := str.tostring((seconds/60/60)) + "H" 
    formatted

method GetCurrentTimeframe(Helper helper) =>
    helper.name := "GetCurrentTimeframe"
    current_seconds = timeframe.in_seconds()
    if current_seconds < 60
        str.tostring(current_seconds) + "S"
    else if (current_seconds / 60) < 60
        str.tostring((current_seconds/60)) + "M"
    else if (current_seconds/60/60) < 24
        str.tostring((current_seconds/60/60)) + "H"
    else
        str.tostring((current_seconds/60/60/24)) + "D"

method GetCurrentBias(Helper helper) =>
    helper.name := "GetCurrentBias"
    if tspot_bias == "Bullish"
        "Bias: Bullish"
    else if tspot_bias == "Bearish"
        "Bias: Bearish"
    else
        "Bias: Neutral"

method GetHTFBias(Helper helper, Candle last_closed, Candle prev_closed) =>
    helper.name := "GetHTFBias"
    
    // Check if we have valid candles
    if na(last_closed) or na(prev_closed)
        "Bias: Neutral"
    else
        last_close = last_closed.c
        prev_close = prev_closed.c
        last_high = last_closed.h
        last_low = last_closed.l
        prev_high = prev_closed.h
        prev_low = prev_closed.l
        last_open = last_closed.o
        
        // Check for sweeps
        swept_high = last_high > prev_high
        swept_low = last_low < prev_low
        
        // Basic cases
        if last_close > prev_high
            "Bias: Bullish"
        else if last_close < prev_low
            "Bias: Bearish"
        // Sweep cases
        else if swept_high and last_close < prev_high
            "Bias: Bearish"
        else if swept_low and last_close > prev_low
            "Bias: Bullish"
        // Inside candle case (no sweeps)
        else if not swept_high and not swept_low
            "Bias: Neutral"
        // Swept both high and low + close within
        else if swept_high and swept_low
            upper_wick = last_high - math.max(last_open, last_close)
            lower_wick = math.min(last_open, last_close) - last_low
            if lower_wick > upper_wick
                "Bias: Bearish"
            else if upper_wick > lower_wick
                "Bias: Bullish"
            else
                "Bias: Neutral"
        else
            "Bias: Neutral"


method GetTablePosition(Helper helper, string position) =>
    helper.name := "GetTablePosition"
    switch position
        'Top Left' => position.top_left
        'Top Center' => position.top_center
        'Top Right' => position.top_right
        'Middle Left' => position.middle_left
        'Middle Center' => position.middle_center
        'Middle Right' => position.middle_right
        'Bottom Left' => position.bottom_left
        'Bottom Center' => position.bottom_center
        'Bottom Right' => position.bottom_right
        => position.top_right

method HTFEnabled(Helper helper) =>
    helper.name := "HTFEnabled"
    int enabled =0
    enabled += htf1.settings.show ? 1 : 0
    enabled += htf2.settings.show ? 1 : 0
    enabled += htf3.settings.show ? 1 : 0
    enabled += htf4.settings.show ? 1 : 0
    enabled += htf5.settings.show ? 1 : 0
    enabled += htf6.settings.show ? 1 : 0
    int last = math.min(enabled, settings.max_sets)

    last

method CandleSetHigh(Helper helper, Candle[] candles, float h) =>
    helper.name := "CandlesSetHigh"
    float _h = h
    if array.size(candles) > 0
        for i = 0 to array.size(candles)-1
            Candle c = array.get(candles, i)
            if c.h > _h
                _h := c.h
    _h

method CandlesHigh(Helper helper, Candle[] candles) =>
    helper.name := "CandlesHigh"
    h   = 0.0
    int cnt = 0
    int last = helper.HTFEnabled()

    if htf1.settings.show and helper.ValidTimeframe(htf1.settings.htf)
        h := helper.CandleSetHigh(htf1.candles, h)
        cnt += 1
    if htf2.settings.show and helper.ValidTimeframe(htf2.settings.htf) and cnt < last
        h := helper.CandleSetHigh(htf2.candles, h)
        cnt +=1
    if htf3.settings.show and helper.ValidTimeframe(htf3.settings.htf) and cnt < last
        h := helper.CandleSetHigh(htf3.candles, h)
        cnt += 1
    if htf4.settings.show and helper.ValidTimeframe(htf4.settings.htf) and cnt < last
        h := helper.CandleSetHigh(htf4.candles, h)
        cnt += 1
    if htf5.settings.show and helper.ValidTimeframe(htf5.settings.htf) and cnt < last
        h := helper.CandleSetHigh(htf5.candles, h)
        cnt += 1
    if htf6.settings.show and helper.ValidTimeframe(htf6.settings.htf) and cnt < last
        h := helper.CandleSetHigh(htf6.candles, h)
    h

    if array.size(candles) > 0
        for i = 0 to array.size(candles)-1
            Candle c = array.get(candles, i)
            if c.h > h
                h := c.h
    h

method Reorder(CandleSet candleSet, int offset) =>
    size = candleSet.candles.size()

    if size > 0
        for i = size-1 to 0
            Candle candle = candleSet.candles.get(i)
            t_buffer = offset + ((settings.width+settings.buffer)*(size-i-1))
            box.set_left(candle.body, bar_index + t_buffer)
            box.set_right(candle.body, bar_index + settings.width + t_buffer)
            line.set_x1(candle.wick_up, bar_index+((settings.width)/2) + t_buffer)
            line.set_x2(candle.wick_up, bar_index+((settings.width)/2) + t_buffer)
            line.set_x1(candle.wick_down, bar_index+((settings.width)/2) + t_buffer)
            line.set_x2(candle.wick_down, bar_index+((settings.width)/2) + t_buffer)
    candleSet

    top     = helper.CandlesHigh(candleSet.candles)
    left    = bar_index + offset + ((settings.width+settings.buffer)*(size-1))/2

    if settings.htf_label_show
        var label l = candleSet.tfName

        string lbl = helper.HTFName(candleSet.settings.htf)
        if settings.htf_timer_show
            lbl += "\n"
   
        if not na(l)
            label.set_xy(l, left, top)            
        else
            l := label.new(left, top, lbl, color=color_transparent, textcolor = settings.htf_label_color, style=label.style_label_down, size = settings.htf_label_size, text_font_family=font.family_monospace)

    if settings.htf_timer_show
        string tmr = "(" + helper.RemainingTime(candleSet.settings.htf) + ")"
        if not na(candleSet.tfTimer)
            candleSet.tfTimer.set_xy(left, top)
            candleSet.tfTimer.set_text(tmr)            
        else
            candleSet.tfTimer := label.new(left, top, tmr, color=color_transparent, textcolor = settings.htf_timer_color, style=label.style_label_down, size = settings.htf_timer_size, text_font_family=font.family_monospace)

    if candleSet.htf_starts.size() > 0
        for i = 0 to candleSet.htf_starts.size() - 1
            line l = candleSet.htf_starts.get(i)
            if not na(l)
                t_buffer = offset + ((settings.width+settings.buffer)*(i))
                line.set_x1(l, bar_index + t_buffer)
                line.set_x2(l, bar_index + t_buffer)

    candleSet








// Function to calculate logarithmic midpoint
method calculateLogMidpoint(float high, float low, float open, float close) =>
    float log_high = math.log(high)
    float log_low = math.log(low)
    float log_open = math.log(open)
    float log_close = math.log(close)
    
    float body_size = math.abs(log_close - log_open)
    float upper_wick = log_high - math.max(log_open, log_close)
    float lower_wick = math.min(log_open, log_close) - log_low
    
    float log_mid_level = if math.max(upper_wick, lower_wick) > body_size
        if upper_wick > lower_wick
            log_high - upper_wick/2
        else
            log_low + lower_wick/2
    else
        (log_high + log_low)/2
    
    math.exp(log_mid_level)



method FindImbalance(CandleSet candleSet) =>
    if barstate.isrealtime or barstate.islast
        // Clean up old midpoint lines
        if midpoint_lines.size() > 0
            for i = midpoint_lines.size()-1 to 0
                line.delete(midpoint_lines.pop())
        
        // Clean up old sweep lines
        if sweep_lines.size() > 0
            for i = sweep_lines.size()-1 to 0
                line.delete(sweep_lines.pop())
        
        if candleSet.imbalances.size() > 0
            for i = candleSet.imbalances.size()-1 to 0
                Imbalance del = candleSet.imbalances.get(i)
                box.delete(del.b)
                candleSet.imbalances.pop()

        if candleSet.candles.size() > 3 and settings.fvg_show
            // Draw midpoint lines for each closed candle
            for i = 0 to candleSet.candles.size() - 2
                current_candle = candleSet.candles.get(i)  // This is actually the newer candle
                prev_candle = candleSet.candles.get(i + 1)  // This is the older candle
                
                // Calculate midpoint using our function
                float mid_level = calculateLogMidpoint(prev_candle.h, prev_candle.l, prev_candle.o, prev_candle.c)
                
                // Draw dotted line from older candle to newer candle and store it
                midpoint_line = line.new(box.get_left(prev_candle.body) + settings.width/2, mid_level,box.get_left(current_candle.body) + settings.width/2, mid_level,color=TEMPLATE_COLOR, width=1, style=line.style_dotted)
                midpoint_lines.unshift(midpoint_line)
            // Original sweep checks
            for i = 0 to candleSet.candles.size() - 2
                current_candle = candleSet.candles.get(i)
                next_candle = candleSet.candles.get(i + 1)
                
                // High sweep - current candle's high breaks next candle's high but closes below it
                if current_candle.h > next_candle.h and current_candle.c < next_candle.h
                    sweep_line = line.new(box.get_left(next_candle.body) + settings.width/2, next_candle.h, box.get_right(current_candle.body) + settings.width, next_candle.h, color=TEMPLATE_COLOR, width=1)
                    sweep_lines.unshift(sweep_line)
                
                // Low sweep - current candle's low breaks next candle's low but closes above it
                if current_candle.l < next_candle.l and current_candle.c > next_candle.l
                    sweep_line = line.new(box.get_left(next_candle.body) + settings.width/2, next_candle.l, box.get_right(current_candle.body) + settings.width, next_candle.l, color=TEMPLATE_COLOR, width=1)
                    sweep_lines.unshift(sweep_line)


            // Process imbalances
            for i = 0 to candleSet.candles.size() -3
                candle1 = candleSet.candles.get(i)
                candle2 = candleSet.candles.get(i+2)
                candle3 = candleSet.candles.get(i+1)

                if (candle1.l > candle2.h and math.min(candle1.o, candle1.c) > math.max(candle2.o, candle2.c))
                    Imbalance imb = Imbalance.new()
                    imb.b := box.new(box.get_left(candle2.body), candle2.h, box.get_right(candle1.body), candle1.l, bgcolor=settings.fvg_color, border_color = color_transparent, xloc=xloc.bar_index)
                    candleSet.imbalances.push(imb)
                if (candle1.h < candle2.l and math.max(candle1.o, candle1.c) < math.min(candle2.o, candle2.c))
                    Imbalance imb = Imbalance.new()
                    imb.b := box.new(box.get_right(candle1.body), candle1.h, box.get_left(candle2.body), candle2.l, bgcolor=settings.fvg_color, border_color = color_transparent)
                    candleSet.imbalances.push(imb)
                box temp = box.copy(candle3.body)
                box.delete(candle3.body)
                candle3.body := temp

        if candleSet.candles.size() > 2 and settings.vi_show
            for i = 0 to candleSet.candles.size() -2
                candle1 = candleSet.candles.get(i)
                candle2 = candleSet.candles.get(i+1)
                if (candle1.l < candle2.h and math.min(candle1.o, candle1.c) > math.max(candle2.o, candle2.c))
                    Imbalance imb = Imbalance.new()
                    imb.b := box.new(box.get_left(candle2.body), math.min(candle1.o, candle1.c), box.get_right(candle1.body), math.max(candle2.o, candle2.c), bgcolor=settings.vi_color, border_color = color_transparent)
                    candleSet.imbalances.push(imb)
                if (candle1.h > candle2.l and math.max(candle1.o, candle1.c) < math.min(candle2.o, candle2.c))
                    Imbalance imb = Imbalance.new()
                    imb.b := box.new(box.get_right(candle1.body), math.min(candle2.o, candle2.c), box.get_left(candle2.body), math.max(candle1.o, candle1.c), bgcolor=settings.vi_color, border_color = color_transparent)
                    candleSet.imbalances.push(imb)
        
        // Clean up excess sweep lines to respect max_display setting
        if sweep_lines.size() > candleSet.settings.max_display
            while sweep_lines.size() > candleSet.settings.max_display
                line.delete(sweep_lines.pop())
        
        // Clean up excess midpoint lines to respect max_display setting  
        if midpoint_lines.size() > candleSet.settings.max_display
            while midpoint_lines.size() > candleSet.settings.max_display
                line.delete(midpoint_lines.pop())
    candleSet


// Get the current bar's hour using exchange time
barHour = hour(time)

// Compute 4H candle block (1 to 6)
candleOfDay = math.floor(barHour / 4) + 1

label_atr = ta.atr(200) / 2

method Monitor(CandleSet candleSet) =>
    HTFBarTime = time(candleSet.settings.htf)
    
    // Use actual day change for daily HTF if toggle is enabled
    isNewHTFCandle = if use_actual_day_change and candleSet.settings.htf == "1D"
        dayofmonth(time) != dayofmonth(time[1])
    else
        ta.change(HTFBarTime) != 0

    // Add label on the current chart bar showing latest T-spot information
    if tspot_boxes.size() > 0
        latest_tspot = tspot_boxes.get(0)
        latest_tspot_mid_line = tspot_midlines.get(0)
        tspot_top = box.get_top(latest_tspot)
        tspot_bottom = box.get_bottom(latest_tspot)
        

        
        if extend_latest_tspot
            box_left = box.get_left(latest_tspot)
            if bar_index - box_left < 499
                box.set_right(latest_tspot, bar_index)
                line.set_x2(latest_tspot_mid_line, bar_index)
                if tspot_closelines.size() > 0
                    latest_tspot_close_line = tspot_closelines.get(0)
                    line.set_x2(latest_tspot_close_line, bar_index)
        
        // T-spot touch detection and confirmation is handled in main execution flow

    // Extend latest projections if enabled
    if extend_latest_projections and projection_lines.size() > 0
        // Calculate projection count from user input
        levels = str.split(projection_levels, ",")
        projection_count = 0
        for i = 0 to array.size(levels) - 1
            level_str = str.trim(array.get(levels, i))
            if level_str != ""
                projection_count += 1
        
        // Extend only the latest group of projections (latest projection_count lines)
        for i = 0 to math.min(projection_count - 1, array.size(projection_lines) - 1)
            proj_line = array.get(projection_lines, i)
            line.set_x2(proj_line, bar_index)

    // Clean up old projection lines and labels (run every bar)
    levels = str.split(projection_levels, ",")
    projection_count = 0
    for i = 0 to array.size(levels) - 1
        level_str = str.trim(array.get(levels, i))
        if level_str != ""
            projection_count += 1
    
    if projection_lines.size() > candleSet.settings.max_display * projection_count
        old_proj_line = projection_lines.pop()
        old_proj_label = projection_labels.pop()
        line.delete(old_proj_line)
        label.delete(old_proj_label)

    if isNewHTFCandle 
        lineStyle = settings.htf_line_style
        if settings.htf_line_show
            line newLine = line.new(x1=bar_index, y1=0, x2=bar_index, y2=1, color=settings.htf_line_color, style=lineStyle, width=settings.htf_line_width, extend=extend.both)
            htf_lines.unshift(newLine)

        // Remove excess lines
        if htf_lines.size() > candleSet.settings.max_display
            line oldLine = htf_lines.pop()
            line.delete(oldLine)


        // Create new candle
        Candle candle = Candle.new()
        candle.o        := open
        candle.c        := close
        candle.h        := high
        candle.l        := low
        candle.o_idx    := bar_index
        candle.c_idx    := bar_index
        candle.h_idx    := bar_index
        candle.l_idx    := bar_index

        bull = candle.c > candle.o

        candle.body         := box.new(bar_index, math.max(candle.o, candle.c), bar_index+2, math.min(candle.o, candle.c), 
             bull ? settings.bull_border : settings.bear_border, 1, bgcolor = bull ? settings.bull_body : settings.bear_body)
        candle.wick_up      := line.new(bar_index+1, candle.h, bar_index, math.max(candle.o, candle.c), 
             color=bull ? settings.bull_wick : settings.bear_wick)
        candle.wick_down    := line.new(bar_index+1, math.min(candle.o, candle.c), bar_index, candle.l, 
             color=bull ? settings.bull_wick : settings.bear_wick)

        candleSet.candles.unshift(candle)

        if candleSet.candles.size() > candleSet.settings.max_display
            Candle delCandle = array.pop(candleSet.candles)
            box.delete(delCandle.body)
            line.delete(delCandle.wick_up)
            line.delete(delCandle.wick_down)

        // T-spot monitoring logic
        if candleSet.candles.size() >= 4 and show_tspot
            current_candle = candleSet.candles.get(0)   
            last_closed = candleSet.candles.get(1)      
            prev_closed = candleSet.candles.get(2)      
            prev_prev_closed = candleSet.candles.get(3)
            
            latest_line = array.get(htf_lines, 0)
            start_bar = line.get_x2(latest_line)

            oktspot_bearish_bias = tspot_bias == "Bearish" or tspot_bias == "None"
            oktspot_bullish_bias = tspot_bias == "Bullish" or tspot_bias == "None"
            
            if not na(start_bar)
                htf_bars = int(timeframe.in_seconds(htf1.settings.htf) / timeframe.in_seconds())
                end_bar = int(start_bar + htf_bars)
                
                float mid_level = calculateLogMidpoint(prev_closed.h, prev_closed.l, prev_closed.o, prev_closed.c)
                var bool tspot_created = false
                var int last_htf_candle_bar = 0
                
                // Only reset tspot_created when a new HTF candle starts
                if start_bar != last_htf_candle_bar
                    tspot_created := false
                    last_htf_candle_bar := start_bar




                // Bearish T-spot
                if not tspot_created and last_closed.h > prev_closed.h and last_closed.c < prev_closed.h and oktspot_bearish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h)
                    if settings.show_chart_sweeps
                        sweep_line = line.new(box.get_left(prev_closed.body) + settings.width/2, prev_closed.h, box.get_right(current_candle.body), prev_closed.h, color=TEMPLATE_COLOR, width=1)
                        sweep_lines.unshift(sweep_line)
                    
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    if last_closed.c < sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, 
                             bgcolor=color.new(settings.bear_body, tspot_transparency), 
                             border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid, 
                             color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c, 
                             color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na
                        
                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c < prev_prev_closed.l and last_closed.c < prev_closed.l
                                CAL.customAlert(alertFormat, "Silver Bearish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, high, "Silver T-Spot", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)

                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        
                        // C2 and C3 Labels for Bearish T-spot
                        // C2 Label
                        c2_level = last_closed.h  // Protected level from last_closed
                        c2_bar = findLevelBarIndex(last_closed.h, 100)  // Find where this level was formed
                        
                        // CISD Detection for Normal Bearish T-spot
                        if c2_bar > 0
                            detectCISDAndProjections(c2_bar, last_closed.h, false, end_bar)
                        
                        if c2_bar > 0 and show_ttfm_labels
                            c2_label = label.new(c2_bar, c2_level + label_atr, "C2", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c2_labels, c2_label)
                            
                            // C3 Label for normal bearish
                            c3_label = label.new(bar_index, sweep_mid + label_atr, "C3", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c3_labels, c3_label)

                        
                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())

                // Bullish T-spot
                if not tspot_created and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and oktspot_bullish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h)
                    if settings.show_chart_sweeps
                        sweep_line = line.new(box.get_left(prev_closed.body) + settings.width/2, prev_closed.l, box.get_right(current_candle.body), prev_closed.l, color=TEMPLATE_COLOR, width=1)
                        sweep_lines.unshift(sweep_line)
                    
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    if last_closed.c > sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, 
                             bgcolor=color.new(settings.bull_body, tspot_transparency), 
                             border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid,
                             color=settings.htf_label_color,
                             style=tspot_line_style,
                             width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c,
                             color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na


                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c > prev_prev_closed.h and last_closed.c > prev_closed.h
                                CAL.customAlert(alertFormat, "Silver Bullish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, low, "Silver T-Spot", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)
                        
                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        
                        // C2 and C3 Labels for Bullish T-spot
                        
                        // C2 Label
                        c2_level = last_closed.l  // Protected level from last_closed
                        c2_bar = findLevelBarIndex(last_closed.l, 100)  // Find where this level was formed
                        
                        // CISD Detection for Normal Bullish T-spot
                        if c2_bar > 0
                            detectCISDAndProjections(c2_bar, last_closed.l, true, end_bar)
                        
                        if c2_bar > 0 and show_ttfm_labels
                            c2_label = label.new(c2_bar, c2_level - label_atr, "C2", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c2_labels, c2_label)
                            
                            // C3 Label for normal bullish
                            c3_label = label.new(bar_index, sweep_mid - label_atr, "C3", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c3_labels, c3_label)

                        
                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())

                // Expansive Bearish T-spot - previous candle swept high, last candle expands and closes below high sweep low
                if not tspot_created and prev_closed.h > prev_prev_closed.h and last_closed.c < math.max(prev_closed.o, prev_closed.c) and oktspot_bearish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h) and ((prev_closed.c >= calculateLogMidpoint(prev_closed.h, prev_closed.l, prev_closed.o, prev_closed.c)) or (prev_closed.c >= prev_prev_closed.h) or (prev_closed.h > prev_prev_closed.h and prev_closed.l < prev_prev_closed.l and prev_closed.c > prev_prev_closed.l and prev_closed.c < prev_prev_closed.h))
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    if last_closed.c < sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, bgcolor=color.new(settings.bear_body, tspot_transparency), border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid, color=settings.htf_label_color, style=tspot_line_style, width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c, color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na
                        
                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c < prev_prev_closed.l and last_closed.c < prev_closed.l
                                CAL.customAlert(alertFormat, "Silver Expansive Bearish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, high, "Silver Expansive T-Spot", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)

                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        
                        // C2 and C3 Labels for Expansive Bearish T-spot

                            // C2 Label
                        c2_level = prev_closed.h  // Protected level from prev_closed
                        c2_bar = findLevelBarIndex(prev_closed.h, 100)  // Find where this level was formed
                        
                        // CISD Detection for Expansive Bearish T-spot
                        if c2_bar > 0
                            detectCISDAndProjections(c2_bar, prev_closed.h, false, end_bar)

                        if c2_bar > 0 and show_ttfm_labels
                            c2_label = label.new(c2_bar, c2_level + label_atr, "C2", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c2_labels, c2_label)
                            
                            // C4 Label for expansive bearish
                            c4_label = label.new(bar_index, sweep_mid + label_atr, "C4", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c3_labels, c4_label)

                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())


                // Expansive Bullish T-spot - previous candle swept low, last candle expands and closes above low sweep high
                if not tspot_created and prev_closed.l < prev_prev_closed.l and last_closed.c > math.min(prev_closed.o, prev_closed.c) and oktspot_bullish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h) and ((prev_closed.c <= calculateLogMidpoint(prev_closed.h, prev_closed.l, prev_closed.o, prev_closed.c)) or (prev_closed.c <= prev_prev_closed.l) or (prev_closed.h > prev_prev_closed.h and prev_closed.l < prev_prev_closed.l and prev_closed.c > prev_prev_closed.l and prev_closed.c < prev_prev_closed.h))
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    
                    if last_closed.c > sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, bgcolor=color.new(settings.bull_body, tspot_transparency), border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid, color=settings.htf_label_color, style=tspot_line_style, width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c, color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na
                        
                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c > prev_prev_closed.h and last_closed.c > prev_closed.h
                                CAL.customAlert(alertFormat, "Silver Expansive Bullish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, low, "Silver Expansive T-Spot", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_up, size=tspot_sweep_label_size, text_font_family=font.family_monospace)

                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        
                        // C2 and C3 Labels for Expansive Bullish T-spot
                        
                        // C2 Label
                        c2_level = prev_closed.l  // Protected level from prev_closed
                        c2_bar = findLevelBarIndex(prev_closed.l, 100)  // Find where this level was formed
                        
                        // CISD Detection for Expansive Bullish T-spot
                        if c2_bar > 0
                            detectCISDAndProjections(c2_bar, prev_closed.l, true, end_bar)
                        
                        if c2_bar > 0 and show_ttfm_labels
                            c2_label = label.new(c2_bar, c2_level - label_atr, "C2", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c2_labels, c2_label)
                            
                            // C4 Label for expansive bullish
                            c4_label = label.new(bar_index, sweep_mid - label_atr, "C4", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_down, size=ttfm_label_size, text_font_family=font.family_monospace)
                            array.unshift(c3_labels, c4_label)

                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())


                // Pro-trend bullish midpoint sweep T-spot - simplified
                if not tspot_created and last_closed.l < mid_level and last_closed.l > prev_closed.o and last_closed.c > prev_closed.h and oktspot_bullish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h)
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    if last_closed.c > sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, 
                             bgcolor=color.new(settings.bull_body, tspot_transparency), 
                             border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid,
                             color=settings.htf_label_color,
                             style=tspot_line_style,
                             width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c,
                             color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na
                        
                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c > prev_prev_closed.h and last_closed.c > prev_closed.h
                                CAL.customAlert(alertFormat, "Silver Bullish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, low, "Silver T-Spot", color=color.new(color.white, 100), textcolor=settings.bull_body, style=label.style_label_up, size=tspot_sweep_label_size, text_font_family=font.family_monospace)


                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        

                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())

                // Pro-trend bearish midpoint sweep T-spot - simplified
                if not tspot_created and last_closed.h > mid_level and last_closed.h < prev_closed.o and last_closed.c < prev_closed.l and oktspot_bearish_bias and not (last_closed.h > prev_closed.h and last_closed.l < prev_closed.l and last_closed.c > prev_closed.l and last_closed.c < prev_closed.h)
                    sweep_mid = calculateLogMidpoint(last_closed.h, last_closed.l, last_closed.o, last_closed.c)
                    if last_closed.c < sweep_mid
                        box newBox = box.new(start_bar, sweep_mid, end_bar, last_closed.c, 
                             bgcolor=color.new(settings.bear_body, tspot_transparency), 
                             border_color=color.new(color.white, 100))
                        line newMidline = line.new(start_bar, sweep_mid, end_bar, sweep_mid,
                             color=settings.htf_label_color,
                             style=tspot_line_style,
                             width=settings.midline_width)
                        line newCloseline = show_tspot_close_line ? line.new(start_bar, last_closed.c, end_bar, last_closed.c,
                             color=settings.htf_label_color, 
                             style=tspot_line_style,
                             width=settings.midline_width) : na
                        
                        if show_silver_tspot
                            if (candleOfDay == 5 or (candleOfDay == 4 and hour(time, "America/New_York") >= 13)) and last_closed.c < prev_prev_closed.l and last_closed.c < prev_closed.l
                                CAL.customAlert(alertFormat, "Silver Bearish T-Spot on " + syminfo.ticker + " (" + candleSet.settings.htf + ")")
                                label.new(bar_index, high, "Silver T-Spot", color=color.new(color.white, 100), textcolor=settings.bear_body, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)

                        tspot_boxes.unshift(newBox)
                        tspot_midlines.unshift(newMidline)
                        if not na(newCloseline)
                            tspot_closelines.unshift(newCloseline)
                        tspot_created := true
                        

                        if tspot_boxes.size() > candleSet.settings.max_display
                            box.delete(tspot_boxes.pop())
                            line.delete(tspot_midlines.pop())
                            if tspot_closelines.size() > 0
                                line.delete(tspot_closelines.pop())



                // Clear previous T-spots if we only want to show the latest
                if show_only_latest_tspot and tspot_created
                    while tspot_boxes.size() > 1
                        box.delete(tspot_boxes.pop())
                    while tspot_midlines.size() > 1
                        line.delete(tspot_midlines.pop())
                    while tspot_closelines.size() > 1
                        line.delete(tspot_closelines.pop())
                
                // Clean up old TTFM labels if we exceed max_display
                if c2_labels.size() > candleSet.settings.max_display
                    while c2_labels.size() > candleSet.settings.max_display
                        label.delete(c2_labels.pop())
                        label.delete(c3_labels.pop())
                
                // Clear previous TTFM labels if we only want to show the latest
                if show_only_latest_tspot and (c2_labels.size() > 0 or c3_labels.size() > 0)
                    while c2_labels.size() > 1
                        label.delete(c2_labels.pop())
                    while c3_labels.size() > 1
                        label.delete(c3_labels.pop())
                
                // Sweep lines are now cleaned up in FindImbalance method
                
                // Midpoint lines are now cleaned up in FindImbalance method
    candleSet

method Update(CandleSet candleSet, int offset, bool showTrace) =>
    if candleSet.candles.size() > 0
        Candle candle   = candleSet.candles.first()
        candle.h_idx    := high > candle.h ? bar_index : candle.h_idx
        candle.h        := high > candle.h ? high : candle.h
        candle.l_idx    := low < candle.l ? bar_index : candle.l_idx
        candle.l        := low < candle.l ? low : candle.l
        candle.c        := close
        candle.c_idx   := bar_index

        bull = candle.c > candle.o

        box.set_top(candle.body, candle.o)
        box.set_bottom(candle.body, candle.c)
        box.set_bgcolor(candle.body, bull ? settings.bull_body : settings.bear_body)
        box.set_border_color(candle.body, bull ? settings.bull_border : settings.bear_border)
        line.set_color(candle.wick_up, bull ? settings.bull_wick : settings.bear_wick)
        line.set_color(candle.wick_down, bull ? settings.bull_wick : settings.bear_wick)
        line.set_y1(candle.wick_up, candle.h)
        line.set_y2(candle.wick_up, math.max(candle.o, candle.c))
        line.set_y1(candle.wick_down, candle.l)
        line.set_y2(candle.wick_down, math.min(candle.o, candle.c))

        if barstate.isrealtime or barstate.islast
            candleSet.Reorder(offset)
            if settings.trace_show and showTrace
                if bar_index - candle.o_idx < 5000
                    if na(trace.o)
                        trace.o := line.new(candle.o_idx, candle.o, box.get_left(candle.body), candle.o, xloc= xloc.bar_index, color=settings.trace_o_color, style= helper.LineStyle(settings.trace_o_style), width=settings.trace_o_size)
                    else
                        line.set_xy1(trace.o, candle.o_idx, candle.o)
                        line.set_xy2(trace.o, box.get_left(candle.body), candle.o)

                    if settings.label_show
                        if na(trace.o_l)
                            trace.o_l := label.new(box.get_right(candle.body), candle.o, str.tostring(candle.o),  textalign = text.align_center, style=label.style_label_left, size=settings.label_size, color=color_transparent, textcolor=settings.label_color, text_font_family=font.family_monospace)
                        else
                            label.set_xy(trace.o_l, box.get_right(candle.body), candle.o)
                            label.set_text(trace.o_l, str.tostring(candle.o))
    
                if bar_index - candle.c_idx < 5000
                    if na(trace.c)
                        trace.c := line.new(candle.c_idx, candle.c, box.get_left(candle.body), candle.c, xloc= xloc.bar_index, color=settings.trace_c_color, style=helper.LineStyle(settings.trace_c_style), width=settings.trace_c_size)
                    else
                        line.set_xy1(trace.c, candle.c_idx, candle.c)
                        line.set_xy2(trace.c, box.get_left(candle.body), candle.c)

                    if settings.label_show
                        if na(trace.c_l)
                            trace.c_l := label.new(box.get_right(candle.body), candle.c, str.tostring(candle.c),  textalign = text.align_center, style=label.style_label_left, size=settings.label_size, color=color_transparent, textcolor=settings.label_color, text_font_family=font.family_monospace)
                        else
                            label.set_xy(trace.c_l, box.get_right(candle.body), candle.c)
                            label.set_text(trace.c_l, str.tostring(candle.c))

                if bar_index - candle.h_idx < 5000
                    if na(trace.h)
                        trace.h := line.new(candle.h_idx, candle.h, line.get_x1(candle.wick_up), candle.h, xloc= xloc.bar_index, color=settings.trace_h_color, style=helper.LineStyle(settings.trace_h_style), width=settings.trace_h_size)
                    else
                        line.set_xy1(trace.h, candle.h_idx, candle.h)
                        line.set_xy2(trace.h, line.get_x1(candle.wick_up), candle.h)

                    if settings.label_show
                        if na(trace.h_l)
                            trace.h_l := label.new(box.get_right(candle.body), candle.h, str.tostring(candle.h),  textalign = text.align_center, style=label.style_label_left, size=settings.label_size, color=color_transparent, textcolor=settings.label_color, text_font_family=font.family_monospace)
                        else
                            label.set_xy(trace.h_l, box.get_right(candle.body), candle.h)
                            label.set_text(trace.h_l, str.tostring(candle.h))

                if bar_index - candle.l_idx < 5000
                    if na(trace.l)
                        trace.l := line.new(candle.l_idx, candle.l, line.get_x1(candle.wick_down), candle.l, xloc= xloc.bar_index, color=settings.trace_l_color, style=helper.LineStyle(settings.trace_l_style), width=settings.trace_l_size)
                    else
                        line.set_xy1(trace.l, candle.l_idx, candle.l)
                        line.set_xy2(trace.l, line.get_x1(candle.wick_down), candle.l)

                    if settings.label_show
                        if na(trace.l_l)
                            trace.l_l := label.new(box.get_right(candle.body), candle.l, str.tostring(candle.l),  textalign = text.align_center, style=label.style_label_left, size=settings.label_size, color=color_transparent, textcolor=settings.label_color, text_font_family=font.family_monospace)
                        else
                            label.set_xy(trace.l_l, box.get_right(candle.body), candle.l)
                            label.set_text(trace.l_l, str.tostring(candle.l))
    candleSet



int cnt = 0
int last = helper.HTFEnabled()

// Pivot detection for T-spot sweep confirmation
pivot_high_open = ta.pivothigh(open, 2, 2)
pivot_low_open = ta.pivotlow(open, 2, 2)
pivot_high_close = ta.pivothigh(close, 2, 2)
pivot_low_close = ta.pivotlow(close, 2, 2)
pivot_high_high = ta.pivothigh(high, 2, 2)
pivot_low_low = ta.pivotlow(low, 2, 2)

pivot_high = usebody_for_confirmation ? not na(pivot_high_open) or not na(pivot_high_close) and not na(pivot_high_high) ? (not na(pivot_high_open) ? pivot_high_open : pivot_high_close) : na : pivot_high_high
pivot_low = usebody_for_confirmation ? not na(pivot_low_open) or not na(pivot_low_close) and not na(pivot_low_low) ? (not na(pivot_low_open) ? pivot_low_open : pivot_low_close) : na : pivot_low_low

// Update latest pivot levels and bars
if not na(pivot_high)
    last_pivot_high := usebody_for_confirmation ? math.max(open[2], close[2]) : pivot_high
    last_pivot_high_bar := bar_index[2]

if not na(pivot_low)
    last_pivot_low := usebody_for_confirmation ? math.min(open[2], close[2]) : pivot_low
    last_pivot_low_bar := bar_index[2]

// T-spot touch detection and confirmation
tspot_start_bar = 0
if tspot_boxes.size() > 0
    latest_tspot = tspot_boxes.get(0)
    latest_midline = tspot_midlines.get(0)
    tspot_top = box.get_top(latest_tspot)
    tspot_bottom = box.get_bottom(latest_tspot)
    tspot_start_bar := box.get_left(latest_tspot)
    
    // Reset touch state if T-spot start bar has changed (new T-spot created)
    if tspot_start_bar != last_tspot_start_bar
        tspot_touched := false
    
    // Always update the last T-spot start bar
    last_tspot_start_bar := tspot_start_bar
    
    // Get the midline level from the T-spot
    tspot_midline = line.get_y1(latest_midline)
    
    // Determine T-spot type based on midline position
    tspot_type := tspot_midline >= math.max(tspot_top, tspot_bottom) ? "bearish" : "bullish"
    
    // Touch level depends on T-spot type
    tspot_touch_level = tspot_type == "bearish" ? math.min(tspot_top, tspot_bottom) : math.max(tspot_top, tspot_bottom)
    
    // Check if price touched the appropriate level based on T-spot type
    if tspot_type == "bearish"
        // Bearish T-spot: touch bottom level
        if (high > tspot_touch_level or open > tspot_touch_level) and close < tspot_touch_level
            tspot_touched := true
            tspot_level := tspot_touch_level
            touch_bar := bar_index
    else
        // Bullish T-spot: touch top level
        if (low < tspot_touch_level or open < tspot_touch_level) and close > tspot_touch_level
            tspot_touched := true
            tspot_level := tspot_touch_level
            touch_bar := bar_index






// Check for confirmation sweeps
if tspot_touched and show_tspot_sweeps

    // Bullish T-spot sweep: only for bullish T-spots, pivot high before touch, close above pivot, pivot above touch level
    if tspot_type == "bullish" and not na(last_pivot_high) and last_pivot_high_bar < touch_bar and close > last_pivot_high and  open < last_pivot_high and last_pivot_high > tspot_level
        // Find lowest low between pivot candle and confirmation candle
        lowest_low = low[0]
        for i = 0 to (bar_index - last_pivot_high_bar)
            if low[i] < lowest_low
                lowest_low := low[i]
        
        if show_confirmation_labels
            label.new(bar_index, lowest_low - label_atr, "🔺\nT-Spot\nSweep", color=color.new(color.white, 100), textcolor=tspot_sweep_label_color, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)
        CAL.customAlert(alertFormat, "Bullish T-Spot Sweep Confirmed on " + syminfo.ticker + " (" + htf1.settings.htf + ")")
        
        if show_confirmation_lines
            // Clear previous confirmation lines
            if confirmation_lines.size() > 0
                for i = 0 to confirmation_lines.size() - 1
                    line.delete(confirmation_lines.get(i))
                array.clear(confirmation_lines)
            
            // Create new confirmation line
            new_confirmation_line = line.new(last_pivot_high_bar, last_pivot_high, bar_index, last_pivot_high, color=settings.bull_body, width=1, style=confirmation_line_style)
            array.push(confirmation_lines, new_confirmation_line)
        
        // Calculate position size for latest signal
        if showPositionSize
            latest_entry_price := close  // Entry is close of confirmation candle
            latest_stop_price := lowest_low  // Stop is lowest low of confirmation
            latest_signal_type := "bullish"
            
            // Calculate risk amount
            riskUsd = riskType == 'Percentage' ? accountBalance * riskAmount / 100 : riskAmount
            
            // Calculate position size
            latest_position_size := calculatePositionSize(latest_entry_price, latest_stop_price, riskUsd)
        
        // Reset touch tracking
        tspot_touched := false
    

    // Bearish T-spot sweep: only for bearish T-spots, pivot low before touch, close below pivot, pivot below touch level
    if tspot_type == "bearish" and not na(last_pivot_low) and last_pivot_low_bar < touch_bar and close < last_pivot_low and open > last_pivot_low and last_pivot_low < tspot_level
        // Find highest high between pivot candle and confirmation candle
        highest_high = high[0]
        for i = 0 to (bar_index - last_pivot_low_bar)
            if high[i] > highest_high
                highest_high := high[i]
        
        if show_confirmation_labels
            label.new(bar_index, highest_high + label_atr, "🔻\nT-Spot\nSweep", color=color.new(color.white, 100), textcolor=tspot_sweep_label_color, style=label.style_label_down, size=tspot_sweep_label_size, text_font_family=font.family_monospace)
        CAL.customAlert(alertFormat, "Bearish T-Spot Sweep Confirmed on " + syminfo.ticker + " (" + htf1.settings.htf + ")")
        
        if show_confirmation_lines
            // Clear previous confirmation lines
            if confirmation_lines.size() > 0
                for i = 0 to confirmation_lines.size() - 1
                    line.delete(confirmation_lines.get(i))
                array.clear(confirmation_lines)
            
            // Create new confirmation line
            new_confirmation_line = line.new(last_pivot_low_bar, last_pivot_low, bar_index, last_pivot_low, color=settings.bear_body, width=1, style=confirmation_line_style)
            array.push(confirmation_lines, new_confirmation_line)
        
        // Calculate position size for latest signal
        if showPositionSize
            latest_entry_price := close  // Entry is close of confirmation candle
            latest_stop_price := highest_high  // Stop is highest high of confirmation
            latest_signal_type := "bearish"
            
            // Calculate risk amount
            riskUsd = riskType == 'Percentage' ? accountBalance * riskAmount / 100 : riskAmount
            
            // Calculate position size
            latest_position_size := calculatePositionSize(latest_entry_price, latest_stop_price, riskUsd)
        
        // Reset touch tracking
        tspot_touched := false

// Hide T-spots when trading against them
if hide_against_tspots and tspot_boxes.size() > 0
    for i = 0 to tspot_boxes.size() - 1
        current_box = tspot_boxes.get(i)
        current_midline = tspot_midlines.get(i)
        current_closeline = i < tspot_closelines.size() ? tspot_closelines.get(i) : na
        box_top = box.get_top(current_box)
        box_bottom = box.get_bottom(current_box)
        midline_price = line.get_y1(current_midline)
        
        // If bearish T-spot (top > bottom) and price closes above midline
        if box_top > box_bottom and close > midline_price
            box.set_bgcolor(current_box, color.new(color.white, 100))
            box.set_border_color(current_box, color.new(color.white, 100))
            line.set_color(current_midline, color.new(settings.htf_label_color, 100))
            if not na(current_closeline)
                line.set_color(current_closeline, color.new(settings.htf_label_color, 100))
        // If bullish T-spot (bottom > top) and price closes below midline
        else if box_bottom > box_top and close < midline_price
            box.set_bgcolor(current_box, color.new(color.white, 100))
            box.set_border_color(current_box, color.new(color.white, 100))
            line.set_color(current_midline, color.new(settings.htf_label_color, 100))
            if not na(current_closeline)
                line.set_color(current_closeline, color.new(settings.htf_label_color, 100))
        // Restore original colors when not trading against
        else
            // Determine if it's bearish or bullish T-spot
            if box_top > box_bottom
                // Bearish T-spot - restore bearish colors
                box.set_bgcolor(current_box, color.new(settings.bear_body, tspot_transparency))
                box.set_border_color(current_box, color.new(color.white, 100))
                line.set_color(current_midline, settings.htf_label_color)
                if not na(current_closeline)
                    line.set_color(current_closeline, settings.htf_label_color)
            else
                // Bullish T-spot - restore bullish colors
                box.set_bgcolor(current_box, color.new(settings.bull_body, tspot_transparency))
                box.set_border_color(current_box, color.new(color.white, 100))
                line.set_color(current_midline, settings.htf_label_color)
                if not na(current_closeline)
                    line.set_color(current_closeline, settings.htf_label_color)

// Delete T-spots that have been traded against (keep only latest in each direction)
if delete_against_tspots and tspot_boxes.size() > 1
    var bool[] bearish_traded_against = array.new<bool>()
    var bool[] bullish_traded_against = array.new<bool>()
    
    // Initialize arrays if needed
    if array.size(bearish_traded_against) != tspot_boxes.size()
        array.clear(bearish_traded_against)
        array.clear(bullish_traded_against)
        for i = 0 to tspot_boxes.size() - 1
            array.push(bearish_traded_against, false)
            array.push(bullish_traded_against, false)
    
    // Check which T-spots have been traded against
    for i = 0 to tspot_boxes.size() - 1
        current_box = tspot_boxes.get(i)
        current_midline = tspot_midlines.get(i)
        box_top = box.get_top(current_box)
        box_bottom = box.get_bottom(current_box)
        midline_price = line.get_y1(current_midline)
        
        // Check if bearish T-spot has been traded against
        if box_top > box_bottom
            // Look back through historical data to see if price ever closed above midline
            traded_against = false
            for j = 0 to 100  // Check last 100 bars
                if bar_index - j >= box.get_left(current_box)
                    if close[j] > midline_price
                        traded_against := true
                        break
            array.set(bearish_traded_against, i, traded_against)
        
        // Check if bullish T-spot has been traded against
        else if box_bottom > box_top
            // Look back through historical data to see if price ever closed below midline
            traded_against = false
            for j = 0 to 100  // Check last 100 bars
                if bar_index - j >= box.get_left(current_box)
                    if close[j] < midline_price
                        traded_against := true
                        break
            array.set(bullish_traded_against, i, traded_against)
    
    // Find latest bearish and bullish T-spots
    latest_bearish_index = -1
    latest_bullish_index = -1
    
    for i = 0 to tspot_boxes.size() - 1
        current_box = tspot_boxes.get(i)
        box_top = box.get_top(current_box)
        box_bottom = box.get_bottom(current_box)
        
        if box_top > box_bottom  // Bearish T-spot
            if latest_bearish_index == -1 or box.get_left(tspot_boxes.get(latest_bearish_index)) < box.get_left(current_box)
                latest_bearish_index := i
        else if box_bottom > box_top  // Bullish T-spot
            if latest_bullish_index == -1 or box.get_left(tspot_boxes.get(latest_bullish_index)) < box.get_left(current_box)
                latest_bullish_index := i
    
    // Delete older T-spots that have been traded against
    for i = tspot_boxes.size() - 1 to 0
        current_box = tspot_boxes.get(i)
        box_top = box.get_top(current_box)
        box_bottom = box.get_bottom(current_box)
        
        should_delete = false
        
        if box_top > box_bottom  // Bearish T-spot
            if i != latest_bearish_index and array.get(bearish_traded_against, i)
                should_delete := true
        else if box_bottom > box_top  // Bullish T-spot
            if i != latest_bullish_index and array.get(bullish_traded_against, i)
                should_delete := true
        
        if should_delete
            // Delete the T-spot and its associated elements
            box.delete(tspot_boxes.get(i))
            line.delete(tspot_midlines.get(i))
            if i < tspot_closelines.size()
                line.delete(tspot_closelines.get(i))
            
            // Remove from arrays
            array.remove(tspot_boxes, i)
            array.remove(tspot_midlines, i)
            if i < tspot_closelines.size()
                array.remove(tspot_closelines, i)
            array.remove(bearish_traded_against, i)
            array.remove(bullish_traded_against, i)

int htf_offset = settings.offset
if htf1.settings.show and helper.ValidTimeframe(htf1.settings.htf) and bar_index > last_bar_index - 1000
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe"
        showTrace := true
    if settings.trace_anchor == "Last Timeframe" and settings.max_sets == 1
        showTrace := true
    htf1.Monitor().Update(htf_offset, showTrace).FindImbalance()
    cnt +=1
    htf_offset += cnt > 0 ? (htf1.candles.size() * settings.width) + (htf1.candles.size() > 0 ? (htf1.candles.size()-1) * settings.buffer : 0) +  settings.htf_buffer : 0
if htf2.settings.show and helper.ValidTimeframe(htf2.settings.htf) and cnt < last
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe" and cnt == 0
        showTrace := true
    if settings.trace_anchor == "Last Timeframe" and cnt == last-1
        showTrace := true
    htf2.Monitor().Update(htf_offset, showTrace).FindImbalance()
    cnt+=1
    htf_offset += cnt > 0 ? (htf2.candles.size() * settings.width) + (htf2.candles.size() > 0 ? (htf2.candles.size()-1) * settings.buffer : 0) +  settings.htf_buffer : 0
if htf3.settings.show and helper.ValidTimeframe(htf3.settings.htf) and cnt < last
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe" and cnt == 0
        showTrace := true
    if settings.trace_anchor == "Last Timeframe" and cnt == last-1
        showTrace := true
    htf3.Monitor().Update(htf_offset, showTrace).FindImbalance()
    cnt+=1
    htf_offset += cnt > 0 ? (htf3.candles.size() * settings.width) + (htf3.candles.size() > 0 ? (htf3.candles.size()-1) * settings.buffer : 0) +  settings.htf_buffer : 0
if htf4.settings.show and helper.ValidTimeframe(htf4.settings.htf) and cnt < last
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe" and cnt == 0
        showTrace := true
    if settings.trace_anchor == "Last Timeframe" and cnt == last-1
        showTrace := true
    htf4.Monitor().Update(htf_offset, showTrace).FindImbalance()
    cnt+=1
    htf_offset += cnt > 0 ? (htf4.candles.size() * settings.width) + (htf4.candles.size() > 0 ? (htf4.candles.size()-1) * settings.buffer : 0) +  settings.htf_buffer : 0
if htf5.settings.show and helper.ValidTimeframe(htf5.settings.htf) and cnt < last
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe" and cnt == 0
        showTrace := true
    if settings.trace_anchor == "Last Timeframe" and cnt == last-1
        showTrace := true
    htf5.Monitor().Update(htf_offset, showTrace).FindImbalance()
    cnt+=1
    htf_offset += cnt > 0 ? (htf5.candles.size() * settings.width) + (htf5.candles.size() > 0 ? (htf5.candles.size()-1) * settings.buffer : 0) +  settings.htf_buffer : 0
if htf6.settings.show and helper.ValidTimeframe(htf6.settings.htf) and cnt < last
    bool showTrace = false
    if settings.trace_anchor == "First Timeframe" and cnt == 0
        showTrace := true
    if settings.trace_anchor == "Last Timeframe"
        showTrace := true
    htf6.Monitor().Update(htf_offset, showTrace).FindImbalance()

// Create info table if enabled
if show_table and htf1.candles.size() > 0
    // Check if stop has been hit (before determining table dimensions)
    if showPositionSize and not na(latest_position_size)
        stop_hit = false
        if latest_signal_type == "bullish" and low <= latest_stop_price
            stop_hit := true
        else if latest_signal_type == "bearish" and high >= latest_stop_price
            stop_hit := true
        
        if stop_hit
            // Clear position size data when stop is hit
            latest_position_size := na
            latest_entry_price := na
            latest_stop_price := na
            latest_signal_type := ""
    
    // Determine table dimensions based on position sizing data
    has_position_data = showPositionSize and not na(latest_position_size)
    table_rows = has_position_data ? 4 : 3
    
    // Create or recreate table if dimensions changed
    if na(info_table)
        info_table := table.new(helper.GetTablePosition(table_position), 1, table_rows, 
                               bgcolor=table_bg_color, 
                               border_width=table_border_width, 
                               border_color=table_border_color,
                               frame_width=table_frame_width,
                               frame_color=table_frame_color)
        current_table_rows := table_rows
    else
        // Check if we need to recreate table due to dimension change
        if current_table_rows != table_rows
            table.delete(info_table)
            info_table := table.new(helper.GetTablePosition(table_position), 1, table_rows, 
                                   bgcolor=table_bg_color, 
                                   border_width=table_border_width, 
                                   border_color=table_border_color,
                                   frame_width=table_frame_width,
                                   frame_color=table_frame_color)
            current_table_rows := table_rows
    
    // Table content
    title_text = helper.GetCurrentTimeframe() + "-" + helper.HTFName(htf1.settings.htf) + " Model"
    timer_text = helper.RemainingTime(htf1.settings.htf)
    
    // Get HTF bias using the last two closed candles
    bias_text = "Bias: Neutral"
    if htf1.candles.size() >= 3
        last_closed = htf1.candles.get(1)
        prev_closed = htf1.candles.get(2)
        bias_text := helper.GetHTFBias(last_closed, prev_closed)
    
    bias_and_time_text = bias_text
    
    // Set table cell content
    table.cell(info_table, 0, 0, title_text, 
               text_color=table_text_color, 
               text_size=table_size, 
               text_font_family=font.family_monospace,
               bgcolor=table_bg_color)
    
    table.cell(info_table, 0, 1, timer_text, 
               text_color=table_text_color, 
               text_size=table_size, 
               text_font_family=font.family_monospace,
               bgcolor=table_bg_color)
    
    table.cell(info_table, 0, 2, bias_and_time_text, 
               text_color=table_text_color, 
               text_size=table_size, 
               text_font_family=font.family_monospace,
               bgcolor=table_bg_color)
    
    // Add position size information if enabled and available
    if showPositionSize and not na(latest_position_size)
        // Show position size information
        position_text = latest_signal_type == "bullish" ? "🔺 " : "🔻 "
        position_text := position_text + (abbreviatePositionInfo ? "Sz: " : "Size: ") + formatPositionSize(latest_position_size)
        string precisionString = getPrecisionString()
        position_text := position_text + "\n" + (abbreviatePositionInfo ? "En: " : "Entry: ") + str.tostring(latest_entry_price, '#.' + precisionString)
        position_text := position_text + "\n" + (abbreviatePositionInfo ? "St: " : "Stop: ") + str.tostring(latest_stop_price, '#.' + precisionString)
        
        table.cell(info_table, 0, 3, position_text, 
                   text_color=table_text_color, 
                   text_size=table_size, 
                   text_font_family=font.family_monospace,
                   bgcolor=table_bg_color)

//HTF Candle End

Overview

The higher timeframe is chosen automatically or set manually, with a bias setting, remaining-time timer, labels and an info table. Fair value gaps, volume imbalances and optional trace lines of the higher-timeframe open, close and high can be shown. Custom alert messages are supported through an imported alert library. The script builds on an existing HTF Candles indicator by another author.

Details

Published by

abhinaynatraj on GitHub

Code credit

mindyourbuisness, as written in the script's header

Type

Indicator, Pine Script v6

Draws

On the price chart

Category

SMC / ICT / price action, Support & resistance

Markets

Any symbol

Timeframes

Any timeframe

Risk Notes

No repainting, lookahead or stacked entries were found in its code or README. That says nothing about how well it works: test it first.

Settings

The inputs you can change from the gear icon on the chart. Defaults are the author's.

SettingTypeDefaultVariable
Custom alert message formattext{"ticker":"{ticker}", "interval":"{interval}", "alert":"{defalertFormat
ThemestringAutogTheme
HTF ModestringAutohtf_mode
Custom Timeframetimeframe1Wcustom_htf
T-Spot BiasstringNonetspot_bias
Show HTFbooltrueshow
Max Displayint4max_display
Use Actual Day Change for Daily HTFboolfalseuse_actual_day_change
padding from current candlesint20offset
space between candlesint1buffer
space between Higher Timeframesint5htf_buffer
Candle Widthint1width
Show all 40 settings
SettingTypeDefaultVariable
HTF Label booltruehtf_label_show
htf_label_sizestringSIZE_NORMALhtf_label_size
Remaining time booltruehtf_timer_show
htf_timer_sizestringSIZE_NORMALhtf_timer_size
Show Info Tablebooltrueshow_table
Table PositionstringTop Righttable_position
Table Text SizestringSIZE_SMALLtable_size
Table Border Widthint1table_border_width
Table Frame Widthint1table_frame_width
Fair Value Gap booltruefvg_show
Volume Imbalancebooltruevi_show
Trace linesboolfalsetrace_show
trace_o_stylestringLINE_STYLE_DOTTEDtrace_o_style
trace_o_sizeint1trace_o_size
trace_c_stylestringLINE_STYLE_DOTTEDtrace_c_style
trace_c_sizeint1trace_c_size
trace_h_stylestringLINE_STYLE_DOTTEDtrace_h_style
trace_h_sizeint1trace_h_size
trace_l_stylestringLINE_STYLE_DOTTEDtrace_l_style
trace_l_sizeint1trace_l_size
Anchor tostringFirst Timeframetrace_anchor
Price Label boolfalselabel_show
label_sizestringsize.smalllabel_size
HTF Start Line booltruehtf_line_show
htf_line_widthint1htf_line_width
htf_line_stylestringLINE_STYLE_DOTTEDhtf_line_style
Show Chart Sweepsbooltrueshow_chart_sweeps
Show T-Spotbooltrueshow_tspot
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

ICT Equal Highs and Lows

by ArunKBhaskar
575 stars

This overlay indicator finds pivot highs and lows on the price chart and compares the last two highs and the last two lows. When the percentage difference between a pair is below the Equal HL threshold, it treats them as equal levels and draws a box. Left and

Indicator v5 SMC / ICT / price actionSupport & resistance
Updated 1 year ago · MPL-2.0 GitHub repository of ICT Equal Highs and Lows

DE-tracker

by Allen1212
303 stars

This indicator plots labels on the TradingView price chart to mark ARK Invest's buy and sell trades for the DE stock. Buy transactions appear in green with X1 or X2 multipliers showing the number of ARK ETFs involved on the same day; sells appear in red. The m

Indicator v4 Support & resistanceSMC / ICT / price action
Updated 2 years ago · None stated GitHub repository of DE-tracker

This Pine Script indicator plots a ZigZag on the price chart. It identifies reversal points when price moves more than a set percentage from the last pivot. The script calculates forecasts using zigzag data and labels detected geometric patterns including harm

Indicator v4 TrendSupport & resistance

Golden Bullet Strategy

by TradersPost
218 stars

The Golden Bullet Strategy is an overlay signal indicator based on Smart Money Concepts and time-based liquidity models. It identifies liquidity sweeps of a designated reference session range during a specified hunt window. It then confirms Market Structure Sh

Indicator v6 SMC / ICT / price actionSessions & time
Updated 3 months ago · None stated GitHub repository of Golden Bullet Strategy

Open Source Fractal

by cantolab
183 stars

This indicator implements the Fractal Model concept to plot market structure shifts, liquidity sweeps, and Change in State of Delivery (CISD) levels. It overlays custom higher-timeframe candle previews, open prices, and time markers directly on the price chart

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · MIT GitHub repository of Open Source Fractal

Swing Failure Signals

by Ganador1
154 stars

This indicator calculates pivot highs and pivot lows to detect liquidity sweeps and Swing Failure Patterns. It identifies when price breaks past recent key swing highs or lows within a configurable historical window, then looks for a quick reversal or change i

Indicator v6 SMC / ICT / price actionSupport & resistance
Updated 2 months ago · Apache-2.0 GitHub repository of Swing Failure Signals

More indicators

Trade Your Charts With a Regulated Broker

Compare brokers that connect to TradingView, with spreads, minimum deposits and leverage side by side.

Compare brokers