// 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
Show all 2,098 lines