//@version=6
indicator(title="Long Entry Component Analyzer", overlay=false, max_lines_count=500)

import TradingView/ta/10
import NiceOrbit/LibraryUtility/9 as utils
import NiceOrbit/LibraryRSIDivergence/1 as rsiDiv
import NiceOrbit/LibraryGaussianChannel/1 as gcl
import NiceOrbit/LibraryMACD/9 as macdLib
import NiceOrbit/LibraryMoneySupply/7 as libMoneySupply
import NiceOrbit/LibraryStochasticDivergence/3 as libStochDiv
import NiceOrbit/LibraryVWAPDivergence/3 as libVwapDiv
import NiceOrbit/LibraryRealizedPrice/4 as libRealizedPrice

// =====================================================================================================================
// DUPLICATED INPUTS & LOGIC (MUST BE KEPT IN SYNC WITH MainStrategy.pine)
// =====================================================================================================================

//{ Date, Condition switches & Settings  ======================
group_date_range                        = "Back Testing Date Range"
startDate                               = input.time(timestamp("1 January 2018"), "Start", group=group_date_range, inline="date", display=display.none)
endDate                                 = input.time(timestamp("1 July 2024 23:59 +0000"), "End ", group=group_date_range, inline="date", display=display.none)
timeCondition                           = time >= startDate and time <= endDate

group_general_settings                = "General Setings"
i_log_variables_to_list_of_trades       = input.bool(false, title="Log Variables on each trade in List of Trades", group=group_general_settings,display=display.none)
i_enable_timeframe_adjustment           = input.bool(true, title="Enable Time Frame Adjustment", group=group_general_settings,display=display.none, tooltip = "For timeframes shorter than 1 Day, adjusted to use more bars to keep same range of data as 1 day would")
group_LongEntry_Enabled                 = "Long Entry Types Enabled"

timeframe_divisor = utils.f_resInDays()

//}

//{ M3 Indicator Code }
groupM3          = "M3 Settings"
m3_growth_rate_period        = input.int(92, "92D-Daily percent gain period", group=groupM3,display=display.none)
m3_globalSmoothingMethod          = input.string(title="Money Supply Smoothing Method", options=["None", "Simple Moving Average", "Exponential Moving Average", "Hull Moving Average"],  defval="None", group=groupM3, display=display.none)
m3_globalSmoothingPeriod          = input.int(title="Money Supply Smoothing Period",  defval=14, minval=1, group=groupM3, display=display.none)
smoothM3SmoothingMethod           = input.string(title="Smooth M3 Moving Average", options=["None", "Simple Moving Average", "Exponential Moving Average", "Hull Moving Average"], defval="Exponential Moving Average", group=groupM3, display=display.none)
smoothM3SmoothingPeriod           = input.int(title="Smooth M3 Smoothing Period", defval=5, minval=1, group=groupM3, display=display.none)

moneySupplyInputs = libMoneySupply.MoneySupplySettings.new()
moneySupplyInputs.m3_growth_rate_period := m3_growth_rate_period
moneySupplyInputs.m3_globalSmoothingMethod := m3_globalSmoothingMethod
moneySupplyInputs.m3_globalSmoothingPeriod := m3_globalSmoothingPeriod
moneySupplyInputs.smoothM3SmoothingMethod := smoothM3SmoothingMethod
moneySupplyInputs.smoothM3SmoothingPeriod := smoothM3SmoothingPeriod
moneySupplyInputs.i_enable_timeframe_adjustment := i_enable_timeframe_adjustment
moneySupplyInputs.timeframe_divisor := timeframe_divisor

moneySupplyData = libMoneySupply.calculate_money_supply(moneySupplyInputs)
m2_US_EU_CN = moneySupplyData.m2_US_EU_CN
m3_global = moneySupplyData.m3_global
m3_global_smoothed = moneySupplyData.m3_global_smoothed
m3_growth_rate = moneySupplyData.m3_growth_rate
m3_growth_rate_smoothed = moneySupplyData.m3_growth_rate_smoothed    
//}

//{ Hullman Moving Average Indicators, fast moving average on m3_growth_rate }
hma_length                  = input(30, "HMA Length",group=groupM3, display=display.none)
hmaTrend_slope_period       = input.int(2, "hmaTrend_slope_period",group=groupM3, display=display.none)
hma_period_LongEntry        = input(74, "HMA Period LongEntry(72 or 74?)",group=groupM3, display=display.none)
rocHMALongEntryThreshold    = input.float(-4.5,"rocHMALongEntryThreshold", step=0.1,group=groupM3, display=display.none)

if i_enable_timeframe_adjustment
    hma_length                  := int(hma_length / timeframe_divisor) > 5000 ? 5000 : int(hma_length / timeframe_divisor) 
    hmaTrend_slope_period       := int(hmaTrend_slope_period / timeframe_divisor) > 5000 ? 5000 : int(hmaTrend_slope_period / timeframe_divisor) 
    hma_period_LongEntry        := int(hma_period_LongEntry / timeframe_divisor) > 5000 ? 5000 : int(hma_period_LongEntry/ timeframe_divisor) 

hma = ta.hma(m3_growth_rate_smoothed, hma_length)
hmaTrend                 = hma - hma[hmaTrend_slope_period]
denominator = hma[hma_period_LongEntry]
rocHMALongEntry_raw = (hma - denominator) / denominator
//}

//{ MACD Setup }
macd_settings = macdLib.MACDSettings.new()
fast_length_1D                      = input.int(12,"Fast Length (1D)", group="MACD", display=display.none)
fast_length_12H                     = input.int(15,"Fast Length (12H)", group="MACD", display=display.none) // Tuned from 12
macd_settings.fast_length           := utils.f_getParamForTimeframe_int(fast_length_1D, fast_length_12H, fast_length_1D)
slow_length_1D                      = input.int(26,"Slow Length (1D)", group="MACD", display=display.none)
slow_length_12H                     = input.int(35,"Slow Length (12H)", group="MACD", display=display.none) // Tuned from 26
macd_settings.slow_length           := utils.f_getParamForTimeframe_int(slow_length_1D, slow_length_12H, slow_length_1D)
signal_length_1D                    = input.int(9,"Signal Smoothing (1D)",minval=1,maxval=50,group="MACD", display=display.none)
signal_length_12H                   = input.int(9,"Signal Smoothing (12H)",minval=1,maxval=50,group="MACD", display=display.none)
macd_settings.signal_length         := utils.f_getParamForTimeframe_int(signal_length_1D, signal_length_12H, signal_length_1D)
macd_settings.sma_source            := input.string("EMA","Oscillator MA Type",options=["SMA","EMA"],group="MACD", display=display.none)
macd_settings.sma_signal            := input.string("EMA","Signal Line MA Type",options=["SMA","EMA"],group="MACD", display=display.none)
macd_settings.bullishFlipSignalBars := input.int(1, "Stability in bars before bullish flip(1D=1)",group="MACD", display=display.none)
macd_settings.bearishFlipSignalBars := input.int(2, "Stability in bars before bearish flip",group="MACD", display=display.none)
macd_settings.i_macd_slope_threshold_percent := input(-9.0, "MACD Slope Threshold (%)", group="MACD", display=display.none)
macd_settings.nBarsOut              := input.int(5,"N bars in the future to predict crossover(1D=5)",group="MACD", display=display.none)

macd_source  = input(title="Source",defval=close, group="MACD", display=display.none)
macd_results = macdLib.calculate_macd(macd_source, macd_settings)

macd = macd_results.macd
signal = macd_results.signal
hist = macd_results.hist
isMacdHistRising = macd_results.isMacdHistRising
isMacdHistFalling = macd_results.isMacdHistFalling
macd_slope_above_threshold = macd_results.macd_slope_above_threshold
macd_is_increasing = macd_results.macd_is_increasing
macd_is_decreasing = macd_results.macd_is_decreasing
macd_prediction = macd_results.macd_prediction
macd_flipped_bullish = macd_results.macd_flipped_bullish
macd_flipped_bearish = macd_results.macd_flipped_bearish
macdLongEntryCondition = macd_results.macdLongEntryCondition
macd_slope_current = macd_results.macd_slope_current
//}

//{ Gaussian Channel Inputs }
groupGAUSSIAN = "Gaussian Settings"
poles           = input.int(defval=1, title="Poles", minval=1, maxval=9, group=groupGAUSSIAN, display=display.none)
per             = input.int(defval=146, title="Sampling Period", minval=2, group=groupGAUSSIAN, display=display.none)
mult            = input.float(defval=1.15, title="Filtered True Range Multiplier", minval=0, group=groupGAUSSIAN, display=display.none)
modeLag         = input.bool(defval=false, title="Reduced Lag Mode", group=groupGAUSSIAN, display=display.none)
modeFast        = input.bool(defval=false, title="Fast Response Mode", group=groupGAUSSIAN, display=display.none)
gaussian_src    = input(low, title="Gaussian Source(low)", group=groupGAUSSIAN, display=display.none)

groupAMA        = "Adaptive MA Settings"
use_ama         = input.bool(true, "Use Adaptive MA", group=groupAMA, display=display.none)
ama_length      = input.int(3, "AMA Length(3)", minval=1, group=groupAMA, display=display.none)
ama_fast        = input.int(3, "AMA Fast Period(3)", minval=1, group=groupAMA, display=display.none)
ama_slow        = input.int(30, "AMA Slow Period(30)", minval=1, group=groupAMA, display=display.none)

if i_enable_timeframe_adjustment
    ama_length  := int(ama_length / timeframe_divisor) > 5000 ? 5000 : int(ama_length / timeframe_divisor)
    ama_fast    := int(ama_fast / timeframe_divisor) > 5000 ? 5000 : int(ama_fast / timeframe_divisor) 
    ama_slow    := int(ama_slow / timeframe_divisor) > 5000 ? 5000 : int(ama_slow / timeframe_divisor) 

settings = gcl.GaussianSettings.new(
     poles = poles, 
     per = per, 
     mult = mult, 
     modeLag = modeLag, 
     modeFast = modeFast, 
     useAma = use_ama, 
     amaLength = ama_length, 
     amaFast = ama_fast, 
     amaSlow = ama_slow, 
     useSimpleCalc = false
 )

[hband, lband, ma, gaussianGreen] = gcl.calc( gaussian_src, settings)
//} ========================

//{ Stochastic RSI Calculation }
groupStochRSI       = "Stochastic RSI Settings"
stochMasterSwitch   = input.string("Enabled", title="Stochastic RSI Master Switch", options=["Enabled","Disabled"], group= groupStochRSI, display=display.none)
smoothK_1D          = input.int(4,  "K (1D)", minval=1, group= groupStochRSI, display=display.none)
smoothK_12H         = input.int(4,  "K (12H)", minval=1, group= groupStochRSI, display=display.none)
smoothK             = utils.f_getParamForTimeframe_int(smoothK_1D, smoothK_12H, smoothK_1D)
smoothD             = input.int(3,  "D", minval=1, group= groupStochRSI, display=display.none)
lengthRSI           = input.int(10, "RSI Length", minval=1, group= groupStochRSI, display=display.none)
lengthStoch_1D      = input.int(17, "Stochastic Length (1D)", minval=1, group= groupStochRSI, display=display.none)
lengthStoch_12H     = input.int(17, "Stochastic Length (12H)", minval=1, group= groupStochRSI, display=display.none)
lengthStoch         = utils.f_getParamForTimeframe_int(lengthStoch_1D, lengthStoch_12H, lengthStoch_1D)
src_stoch           = input(close,  title="RSI Source", group= groupStochRSI, display=display.none)
stoch_tf            = input.timeframe("", "Stoch RSI Timeframe", group= groupStochRSI, display=display.none)
stoch_high_limit    = input.int(87, title="Stochastic High Limit", group= groupStochRSI, display=display.none)
stoch_low_limit     = input.int(20, title="Stochastic Low Limit", group= groupStochRSI, display=display.none)
calcStoch()         => ta.sma(ta.stoch(ta.rsi(src_stoch, lengthRSI), ta.rsi(src_stoch, lengthRSI), ta.rsi(src_stoch, lengthRSI), lengthStoch), smoothK)
stoch_value         = stoch_tf == "" ? calcStoch() : request.security(syminfo.tickerid, stoch_tf, calcStoch(), gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)
bool stochCondition = stochMasterSwitch == "Enabled" ? (stoch_value > stoch_high_limit or stoch_value < stoch_low_limit) : true
osc_high_limit      = input.int(67, title="Maximum RSI allowed on regular long entry condition(67)", group= groupStochRSI, display=display.none)
osc_src             = input(low, "OSC source(low)", group= groupStochRSI, display=display.none)

if i_enable_timeframe_adjustment
    lengthRSI      := int(lengthRSI / timeframe_divisor) > 5000 ? 5000 : int(lengthRSI / timeframe_divisor)
    lengthStoch    := int(lengthStoch / timeframe_divisor) > 5000 ? 5000 : int(lengthStoch / timeframe_divisor)
//} ========================

//{ Stochastic Divergence}
groupStochDiv = "Stochastic Divergence"
stochDivSettings = libStochDiv.StochasticDivergenceSettings.new()
stochDivSettings.stochLength := input.int(14, title="Stochastic Length", minval=1, group=groupStochDiv, display=display.none)
stochDivSettings.smoothK := input.int(3, title="K Smoothing", minval=1, group=groupStochDiv, display=display.none)
stochDivSettings.smoothD := input.int(3, title="D Smoothing", minval=1, group=groupStochDiv, display=display.none)
stochDivSettings.maTypeK := input.string("Simple Moving Average", "MA Type K", options=["Simple Moving Average", "Exponential Moving Average", "Weighted Moving Average", "Hull Moving Average", "Volume Weighted Moving Average", "Volume Weighted Average Price", "None"], group=groupStochDiv, display=display.none)
stochDivSettings.maTypeD := input.string("Exponential Moving Average", "MA Type D", options=["Simple Moving Average", "Exponential Moving Average", "Weighted Moving Average", "Hull Moving Average", "Volume Weighted Moving Average", "Volume Weighted Average Price", "None"], group=groupStochDiv, display=display.none)
stochDivSettings.pivotLookback := input.int(5, title="Pivot Lookback Bars", minval=1, group=groupStochDiv, display=display.none)
stochDivSettings.divergenceStrength := input.int(2, title="Divergence Strength (bars away)", minval=1, group=groupStochDiv, display=display.none)

if i_enable_timeframe_adjustment
    stochDivSettings.stochLength   := int(stochDivSettings.stochLength / timeframe_divisor) > 5000 ? 5000 : int(stochDivSettings.stochLength / timeframe_divisor)
    stochDivSettings.pivotLookback := int(stochDivSettings.pivotLookback / timeframe_divisor) > 5000 ? 5000 : int(stochDivSettings.pivotLookback / timeframe_divisor)

[stoch_bullishDivergence, stoch_bearishDivergence] = libStochDiv.f_calc(stochDivSettings)
//} ========================

//{ VWAP Divergence }
groupVwapDiv = "VWAP Divergence"
vwapDivSettings = libVwapDiv.VWAPDivergenceSettings.new()
vwapDivSettings.pivotLookbackLeft := input.int(1, title="Pivot Lookback Bars Left", minval=1, group=groupVwapDiv, display=display.none)
vwapDivSettings.pivotLookbackRight := input.int(1, title="Pivot Lookback Bars Right", minval=1, group=groupVwapDiv, display=display.none)
vwapDivSettings.divergenceStrength := input.int(2, title="Divergence Strength (bars away)", minval=1, group=groupVwapDiv, display=display.none)

[vwap_bullishDivergence, vwap_bearishDivergence] = libVwapDiv.f_calc(vwapDivSettings)
//} ========================

//{ M3 Indicator }
groupM3ROC                      = "M3 Growth Rate Rate of Change"
m3ROCEnabledEntry               = input.string("Enabled", "Include Rate of Change of M3 on Long Entry", options=["Disabled","Enabled"], group=groupM3ROC, display=display.none)
m3_growth_rate_smoothed_rocLongEntryThreshold  = input.float(40, "m3_growth_rate_smoothed_rocLongEntryThreshold(40)", step=1.0, group=groupM3ROC, display=display.none)
roc_period                      = input.int(2, "ROC Period", group=groupM3ROC, display=display.none)
rocLongEntrySource              = input.string("smoothed_roc", "ROC Source for Long Entry", options=["m3_growthRateRocPercentage","smoothed_roc"], group=groupM3ROC, display=display.none)

if i_enable_timeframe_adjustment
    roc_period      := int(roc_period / timeframe_divisor) > 5000 ? 5000 : int(roc_period / timeframe_divisor) 

m3_growth_rate_roc  = ((m3_growth_rate_smoothed - nz(m3_growth_rate_smoothed[roc_period])) / nz(m3_growth_rate_smoothed[roc_period]))
m3_growthRateRocPercentage             = ((m3_growth_rate_smoothed - nz(m3_growth_rate_smoothed[roc_period])) / nz(m3_growth_rate_smoothed[roc_period])) * 100

float entryROcM3    = rocLongEntrySource == "m3_growthRateRocPercentage" ? m3_growthRateRocPercentage : m3_growth_rate_roc

m3_growthRateRocPercentage_flipped_bullish          = macdLib.getSequenceReversal_M3(-m3_growthRateRocPercentage, 3, 2, i_enable_timeframe_adjustment, timeframe_divisor)
m3_growthRateRocPercentage_flipped_bearish          = macdLib.getSequenceReversal_M3(m3_growthRateRocPercentage, 3, 2, i_enable_timeframe_adjustment, timeframe_divisor)
//}

//{ M2LeadingIndicator Indicator }
group_m2LeadingIndicator_calculations  = "M2 Leading Indicator Calculations"
m2LeadingIndicator_scalingLookback     = input.int(150, title="Scaling Lookback Period(150)", minval=2, group=group_m2LeadingIndicator_calculations, tooltip="Number of bars used to find the min/max values for scaling the M2 plots.", display=display.none)
i_m2LeadingIndicator_smoothingMethod   = input.string("Hull Moving Average", title="M2 Smoothing Method", options=["None", "Simple Moving Average", "Exponential Moving Average", "Hull Moving Average"], group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_barsToRightOfShortLength = input.int(10,"Bars from short length for diff/slope calculations(10)", group=group_general_settings, display=display.none, tooltip = "Value used in multiple entry and exit calculations, huge effect on performance")
i_m2LeadingIndicator_microOffset       = input.int(3, title= "Micro offset, orange line(3)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_miniOffset        = input.int(10, title="Mini offset, orange line(10)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_tinyOffset        = input.int(48, "Tiny offset, orange line(48)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_shortOffset       = input.int(64, "Short offset, orange line(64)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_shortOffset_smoothingLength = input.int(13, "Short Offset Smoothing Length(13)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_mediumOffset      = input.int(78, "Medium offset, red line", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_mediumOffset_smoothingLength = input.int(16, "Medium Offset Smoothing Length", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_longOffset        = input.int(92, "Long offset, yellow line(92)", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_longOffset_smoothingLength = input.int(20, "Long Offset Smoothing Length", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_riseFallLength    = input.int(3, "Length to use for calculating Rise/Fall", group=group_m2LeadingIndicator_calculations, display=display.none)
i_m2LeadingIndicator_oneDayOffset      = input.int(1, "Adjustment factor for one day, unclear if scaling it is needed", group=group_m2LeadingIndicator_calculations, display=display.none)  

if i_enable_timeframe_adjustment
    m2LeadingIndicator_scalingLookback                := int(m2LeadingIndicator_scalingLookback / timeframe_divisor) > 5000 ? 5000 : int(m2LeadingIndicator_scalingLookback/ timeframe_divisor)
    i_m2LeadingIndicator_barsToRightOfShortLength     := int(i_m2LeadingIndicator_barsToRightOfShortLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_barsToRightOfShortLength/ timeframe_divisor)
    i_m2LeadingIndicator_tinyOffset                   := int(i_m2LeadingIndicator_tinyOffset / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_tinyOffset/ timeframe_divisor)
    i_m2LeadingIndicator_shortOffset                  := int(i_m2LeadingIndicator_shortOffset / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_shortOffset / timeframe_divisor)
    i_m2LeadingIndicator_shortOffset_smoothingLength  := int(i_m2LeadingIndicator_shortOffset_smoothingLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_shortOffset_smoothingLength/ timeframe_divisor)
    i_m2LeadingIndicator_mediumOffset                 := int(i_m2LeadingIndicator_mediumOffset / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_mediumOffset / timeframe_divisor)
    i_m2LeadingIndicator_mediumOffset_smoothingLength := int(i_m2LeadingIndicator_mediumOffset_smoothingLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_mediumOffset_smoothingLength/ timeframe_divisor)
    i_m2LeadingIndicator_longOffset                   := int(i_m2LeadingIndicator_longOffset / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_longOffset / timeframe_divisor)
    i_m2LeadingIndicator_longOffset_smoothingLength   := int(i_m2LeadingIndicator_longOffset_smoothingLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_longOffset_smoothingLength/ timeframe_divisor)
    i_m2LeadingIndicator_riseFallLength               := int(i_m2LeadingIndicator_riseFallLength  / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_riseFallLength / timeframe_divisor)
    i_m2LeadingIndicator_oneDayOffset                 := int(i_m2LeadingIndicator_oneDayOffset  / timeframe_divisor) > 5000 ? 5000 : int(i_m2LeadingIndicator_oneDayOffset / timeframe_divisor)

m2_smoothed_microOffset     = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_shortOffset_smoothingLength)
m2_smoothed_miniOffset      = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_shortOffset_smoothingLength)
m2_smoothed_tinyOffset      = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_shortOffset_smoothingLength)
m2_smoothed_shortOffset     = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_shortOffset_smoothingLength)
m2_smoothed_mediumOffset    = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_mediumOffset_smoothingLength)
m2_smoothed_longOffset      = utils.f_ma(m2_US_EU_CN, i_m2LeadingIndicator_smoothingMethod, i_m2LeadingIndicator_longOffset_smoothingLength)

m2_smoothedTinyOffsetSlope     = m2_smoothed_shortOffset[i_m2LeadingIndicator_tinyOffset] - m2_smoothed_shortOffset[(i_m2LeadingIndicator_tinyOffset + i_m2LeadingIndicator_oneDayOffset) > 5000 ? 5000 : (i_m2LeadingIndicator_tinyOffset + i_m2LeadingIndicator_oneDayOffset)]
m2_smoothedShortOffsetSlope    = m2_smoothed_shortOffset[i_m2LeadingIndicator_shortOffset] - m2_smoothed_shortOffset[(i_m2LeadingIndicator_shortOffset + i_m2LeadingIndicator_oneDayOffset) > 5000 ? 5000 : (i_m2LeadingIndicator_shortOffset + i_m2LeadingIndicator_oneDayOffset)]
m2_smoothedMediumOffsetSlope   = m2_smoothed_mediumOffset[i_m2LeadingIndicator_mediumOffset] - m2_smoothed_mediumOffset[(i_m2LeadingIndicator_mediumOffset + i_m2LeadingIndicator_oneDayOffset) > 5000 ? 5000 : (i_m2LeadingIndicator_mediumOffset + i_m2LeadingIndicator_oneDayOffset) ]
m2_smoothedLongOffsetSlope     = m2_smoothed_longOffset[i_m2LeadingIndicator_longOffset] - m2_smoothed_longOffset[(i_m2LeadingIndicator_longOffset + i_m2LeadingIndicator_oneDayOffset) > 5000 ? 5000 : (i_m2LeadingIndicator_longOffset + i_m2LeadingIndicator_oneDayOffset)]

isM2SmoothedMicroOffsetRising    = ta.rising(m2_smoothed_microOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedMiniOffsetRising     = ta.rising(m2_smoothed_miniOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedTinyOffsetRising     = ta.rising(m2_smoothed_shortOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedShortOffsetRising    = ta.rising(m2_smoothed_shortOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedMediumOffsetRising   = ta.rising(m2_smoothed_mediumOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedLongOffsetRising     = ta.rising(m2_smoothed_longOffset, i_m2LeadingIndicator_riseFallLength)

isRising_smoothedM3_no_offset = ta.rising (m3_global_smoothed, i_m2LeadingIndicator_riseFallLength)
isFalling_smoothedM3_no_offset= ta.falling(m3_global_smoothed, i_m2LeadingIndicator_riseFallLength)

isRising_smoothedM3_short_offset     = ta.rising(m3_growth_rate_smoothed[i_m2LeadingIndicator_shortOffset], i_m2LeadingIndicator_shortOffset_smoothingLength)
isFalling_smoothedM3_short_offset    = ta.falling(m3_growth_rate_smoothed[i_m2LeadingIndicator_shortOffset], i_m2LeadingIndicator_shortOffset_smoothingLength)

isM2SmoothedMicroOffsetFalling   = ta.falling(m2_smoothed_microOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedMiniOffsetFalling    = ta.falling(m2_smoothed_miniOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedTinyOffsetFalling    = ta.falling(m2_smoothed_shortOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedShortOffsetFalling   = ta.falling(m2_smoothed_shortOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedMediumOffsetFalling  = ta.falling(m2_smoothed_mediumOffset, i_m2LeadingIndicator_riseFallLength)
isM2SmoothedLongOffsetFalling    = ta.falling(m2_smoothed_longOffset, i_m2LeadingIndicator_riseFallLength)

isM2SmoothedTinyOffsetFlat       = not isM2SmoothedTinyOffsetRising   and not isM2SmoothedTinyOffsetFalling
isM2SmoothedShortOffsetFlat      = not isM2SmoothedShortOffsetRising  and not isM2SmoothedShortOffsetFalling
isM2SmoothedMediumOffsetFlat     = not isM2SmoothedMediumOffsetRising and not isM2SmoothedMediumOffsetFalling
isM2SmoothedLongOffsetFlat       = not isM2SmoothedLongOffsetRising   and not isM2SmoothedLongOffsetFalling

m2_smoothed_tinyOffset   := utils.f_scale_ToRange(m2_smoothed_tinyOffset,  m2LeadingIndicator_scalingLookback, m2LeadingIndicator_scalingLookback)
m2_smoothed_shortOffset  := utils.f_scale_ToRange(m2_smoothed_shortOffset,  m2LeadingIndicator_scalingLookback, m2LeadingIndicator_scalingLookback)
m2_smoothed_mediumOffset := utils.f_scale_ToRange(m2_smoothed_mediumOffset, m2LeadingIndicator_scalingLookback, m2LeadingIndicator_scalingLookback)
m2_smoothed_longOffset   := utils.f_scale_ToRange(m2_smoothed_longOffset,   m2LeadingIndicator_scalingLookback, m2LeadingIndicator_scalingLookback)

m2_smoothedShort_N_bars_out        = m2_smoothed_shortOffset[i_m2LeadingIndicator_shortOffset - i_m2LeadingIndicator_barsToRightOfShortLength]
m2_tinyOffsetDiffToNbarsOut        = m2_smoothed_tinyOffset[i_m2LeadingIndicator_tinyOffset - i_m2LeadingIndicator_barsToRightOfShortLength] - m2_smoothed_tinyOffset[i_m2LeadingIndicator_tinyOffset]
m2_shortOffsetDiffToNbarsOut       = m2_smoothedShort_N_bars_out - m2_smoothed_shortOffset[i_m2LeadingIndicator_shortOffset]
m2_mediumOffsetDiffTo12barsOut     = m2_smoothedShort_N_bars_out - m2_smoothed_shortOffset[i_m2LeadingIndicator_mediumOffset]
m2_shortOffsetSlopeToNbarsOut      = m2_shortOffsetDiffToNbarsOut / nz(i_m2LeadingIndicator_barsToRightOfShortLength)
//} ========================

//{ M2 MACD Calculations
group_m2_macd       = "M2 MACD Settings"
i_m2Macd_fastLength     = input.int(50, title="Fast Length", minval=1,  group=group_m2_macd, tooltip="Fast Moving Average length for MACD calculation on *Original* Total M2.", display=display.none)
i_m2Macd_slowLength     = input.int(200, title="Slow Length", minval=1, group=group_m2_macd, tooltip="Slow Moving Average length for MACD calculation on *Original* Total M2.", display=display.none)
i_m2Macd_signalLength   = input.int(9, title="Signal Length", minval=1, group=group_m2_macd, tooltip="Signal Line smoothing length for MACD.", display=display.none)

if i_enable_timeframe_adjustment
    i_m2Macd_fastLength   := int(i_m2Macd_fastLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2Macd_fastLength / timeframe_divisor)
    i_m2Macd_slowLength   := int(i_m2Macd_slowLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2Macd_slowLength / timeframe_divisor)
    i_m2Macd_signalLength := int(i_m2Macd_signalLength / timeframe_divisor) > 5000 ? 5000 : int(i_m2Macd_signalLength / timeframe_divisor)

m2Macd_fastMA       = ta.ema(m2_US_EU_CN, i_m2Macd_fastLength)
m2Macd_slowMA       = ta.ema(m2_US_EU_CN, i_m2Macd_slowLength)
m2Macd_line     = m2Macd_fastMA - m2Macd_slowMA
m2Macd_signalLine   = ta.ema(m2Macd_line, i_m2Macd_signalLength)

isM2MacdShortCrossBullish = ta.crossover(m2Macd_line[i_m2LeadingIndicator_shortOffset], m2Macd_signalLine[i_m2LeadingIndicator_shortOffset])
m2Macd_shortOffsetDifference = m2Macd_line[i_m2LeadingIndicator_shortOffset] - m2Macd_signalLine[i_m2LeadingIndicator_shortOffset]
isM2MacdLongCrossBullish = ta.crossover(m2Macd_line[i_m2LeadingIndicator_longOffset], m2Macd_signalLine[i_m2LeadingIndicator_longOffset])
//} ========================

//{ RSI Divergence }
rsid_label = "RSI Divergence"
i_enable_timeframe_adjustment_rsi = input.bool(true, title="Apply Time Frame adjustment, ", group=rsid_label, display=display.none, inline="rsid_adj")
i_timeframe_adjustment_weight = input.float(1.0, title="w/ weight", minval=0.1, step=0.1, group=rsid_label, display=display.none, inline="rsid_adj")
i_rsid_src            = input(close, title="RSID Source", group=rsid_label, display=display.none)
i_rsid_lookback_1D    = input.int(14, title="Look-back (1D)",  minval=1, step=1,group=rsid_label, display=display.none)
i_rsid_lookback_12H   = input.int(9, title="Look-back (12H)",  minval=1, step=1,group=rsid_label, display=display.none)
i_rsid_lookback       = utils.f_getParamForTimeframe_int(i_rsid_lookback_1D, i_rsid_lookback_12H, i_rsid_lookback_1D)
i_rsid_overbought_1D  = input.int(70, title="Overbought (1D)", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_overbought_12H = input.int(75, title="Overbought (12H)", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_overbought     = utils.f_getParamForTimeframe_int(i_rsid_overbought_1D, i_rsid_overbought_12H, i_rsid_overbought_1D)
i_rsid_oversold_1D    = input.int(30, title="Oversold (1D)", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_oversold_12H   = input.int(20, title="Oversold (12H)", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_oversold       = utils.f_getParamForTimeframe_int(i_rsid_oversold_1D, i_rsid_oversold_12H, i_rsid_oversold_1D)
i_rsid_minBars        = input.int(5, "Min Bars Between Peaks", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_maxBars        = input.int(50, "Max Bars Between Peaks", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_res            = input.timeframe("", title="Oscillator resolution", group=rsid_label, display=display.none)
i_realTime_lookbackPeriod = input.int(20, "Lookback Period for Real-Time Div", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_len            = input.int(14, minval=1, title="Length", group=rsid_label, display=display.none)
i_rsid_bbLength       = input.int(20, "Bollinger Length", minval=1, step=1, group=rsid_label, display=display.none)
i_rsid_bbMultiplier   = input.float(2.0, "Bollinger Multiplier", minval=0.1, step=0.1, group=rsid_label, display=display.none)

if i_enable_timeframe_adjustment and i_enable_timeframe_adjustment_rsi
    // i_rsid_lookback is now handled by f_getParamForTimeframe_int
    i_realTime_lookbackPeriod := int(i_realTime_lookbackPeriod  * i_timeframe_adjustment_weight / timeframe_divisor) > 5000 ? 5000 : int(i_realTime_lookbackPeriod  * i_timeframe_adjustment_weight / timeframe_divisor)
    i_rsid_len  := int(i_rsid_len  * i_timeframe_adjustment_weight / timeframe_divisor) > 5000 ? 5000 : int(i_rsid_len  * i_timeframe_adjustment_weight / timeframe_divisor)
    i_rsid_bbLength := int(i_rsid_bbLength * i_timeframe_adjustment_weight / timeframe_divisor) > 5000 ? 5000 : int(i_rsid_bbLength * i_timeframe_adjustment_weight / timeframe_divisor)

rsid_lenUp            = ta.ema(math.max(ta.change(i_rsid_src), 0), i_rsid_len)
rsid_lenDn            = ta.ema(-math.min(ta.change(i_rsid_src), 0), i_rsid_len)
rsi = rsid_lenDn == 0 ? 100 : rsid_lenUp == 0 ? 0 : 100 - (100 / (1 + rsid_lenUp / rsid_lenDn))
rsid_osc = request.security(syminfo.tickerid, i_rsid_res, rsi)

rsid_phFound = rsiDiv.phFound(rsid_osc, i_rsid_lookback, i_rsid_lookback)
rsid_plFound = rsiDiv.plFound(rsid_osc, i_rsid_lookback, i_rsid_lookback)
rsid_overbought = rsid_osc > i_rsid_overbought
rsid_oversold   = rsid_osc < i_rsid_oversold

_rsiRegularBullishVal = rsiDiv.regularBullish(rsid_osc, low, 0, 5, 100, rsid_plFound)
_rsiSlowingBullishVal = rsiDiv.slowingBullish(rsid_osc, i_rsid_oversold)
_rsiDelayedBullishVal = rsiDiv.delayedDipRSI(rsid_osc, i_rsid_oversold, 0)
_rsiHiddenBullishVal = rsiDiv.hiddenBullish(rsid_osc, close, 0, 5, 100, rsid_plFound)
_rsiRealTimeBullishVal = rsiDiv.realTimeBullishDiv(rsid_osc, low, i_rsid_maxBars, i_rsid_oversold, i_rsid_bbLength, i_rsid_bbMultiplier)
_rsiConfirmedBullishVal = rsiDiv.confirmedBullishDiv(rsid_osc, low, i_rsid_minBars, i_rsid_maxBars, i_rsid_bbLength, i_rsid_bbMultiplier)
_rsiHiddenBearishVal = rsiDiv.hiddenBearish(rsid_osc, high, 0, 5, 100, rsid_phFound)
//} ========================

//{ Stoch and RSI range filter on Long entry}
groupStochAndRSI               = "Stoch and RSI range filter Long Entry"
stochRSI_len                   = input.int(14, "Stoch and RSI length(14)", minval=1, group=groupStochAndRSI, display=display.none)
rsi_lowpass_filter             = input.int(30, "RSI Threshold for Low(30)", minval=1, group=groupStochAndRSI, display=display.none)
rsi_highpass_filter            = input.int(60, "RSI Threshold for High(60)", minval=1, group=groupStochAndRSI, display=display.none)
stochvalue_lowpass_filter      = input.int(37, "Stoch Value Threshold for Low(?)", minval=1, group=groupStochAndRSI, display=display.none)
stochvalue_highpass_filter     = input.int(70, "Stoch Value Threshold for High(?)", minval=1, group=groupStochAndRSI, display=display.none)

if i_enable_timeframe_adjustment
    stochRSI_len  := int(stochRSI_len  / timeframe_divisor) > 5000 ? 5000 : int(stochRSI_len  / timeframe_divisor)

rsi_low_range_cond          = false
rsi_medium_range_cond       = false
rsi_high_range_cond         = false

osc = ta.rsi(osc_src, stochRSI_len)

if rsid_osc > rsi_highpass_filter
    rsi_high_range_cond     := true
if rsid_osc < rsi_lowpass_filter
    rsi_low_range_cond      := true
if rsid_osc > rsi_lowpass_filter and osc < rsi_highpass_filter
    rsi_medium_range_cond   := true

stoch_low_range_cond        = false
stoch_high_range_cond       = false

if stoch_value > stochvalue_highpass_filter
    stoch_high_range_cond   := true
if stoch_value < stochvalue_lowpass_filter
    stoch_low_range_cond    := true
//} ========================

//{ Realized Price & Divergence}
groupRealizedPrice = "Realized Price"
realizedPriceSettings = libRealizedPrice.RealizedPriceSettings.new()
realizedPriceSettings.realizedPriceComparisonSource := input(title="Source for price to compare to(close)",defval=close, display=display.none, group=groupRealizedPrice)
realizedPriceSettings.smoothingLength := input.int(2, title="Smoothing Length(10)", minval=1, display=display.none, group=groupRealizedPrice)
realizedPriceSettings.smoothingMA := input.string("Simple Moving Average", "Smoothing MA Type", options=["Simple Moving Average", "Exponential Moving Average", "Weighted Moving Average", "Hull Moving Average", "Volume Weighted Moving Average", "Volume Weighted Average Price", "None"], group=groupRealizedPrice, display=display.none)
realizedPriceSettings.realizedPriceLength := input.int(5, title="Realized Price Length(80)", minval=1, display=display.none, group=groupRealizedPrice)
realizedPriceSettings.pivotLeft := input.int(5, title="Divergence Pivot Left(15)", minval=1, display=display.none, group=groupRealizedPrice)
realizedPriceSettings.pivotRight := input.int(1, title="Divergence Pivot Right(5)", minval=1, display=display.none, group=groupRealizedPrice)
realizedPriceSettings.gaussianLen := input.int(15, title="Gaussian Bands Length(50)", group=groupRealizedPrice, display=display.none)
realizedPriceSettings.gaussianMult := input.float(0.65, title="Gaussian Bands StdDev Mult(2)", group=groupRealizedPrice, display=display.none)
realizedPriceSettings.bullNuplThreshold := input.float(0, title="Filter for Bull NUPL Threshold(0)", display=display.none, group=groupRealizedPrice)
realizedPriceSettings.bearNuplThreshold := input.float(2.0, title="Filter for Bear NUPL Threshold(2.0)", display=display.none, group=groupRealizedPrice)

if i_enable_timeframe_adjustment
    realizedPriceSettings.smoothingLength := int(realizedPriceSettings.smoothingLength  / timeframe_divisor) > 5000 ? 5000 : int(realizedPriceSettings.smoothingLength  / timeframe_divisor)
    realizedPriceSettings.pivotLeft       := int(realizedPriceSettings.pivotLeft / timeframe_divisor) > 5000 ? 5000 : int(realizedPriceSettings.pivotLeft / timeframe_divisor)
    realizedPriceSettings.gaussianLen     := int(realizedPriceSettings.gaussianLen / timeframe_divisor) > 5000 ? 5000 : int(realizedPriceSettings.gaussianLen / timeframe_divisor)

[realized_price_bull_div, realized_price_bear_div, realized_price_bull_cond, realized_price_bear_cond, filtered_realized_price_bull_cond, filtered_realized_price_bear_cond, realized_price_smoothed_nupl, realized_price_gaussian_upper, realized_price_nupl] = libRealizedPrice.f_calc(realizedPriceSettings)
//} --------------------------------

atr1_dividedby_close = ta.atr(1) / close

// =====================================================================================================================
// COMPONENT PLOTTING
// =====================================================================================================================

scaled_macd_slope = utils.f_scale_ToRange(macd_slope_current, m2LeadingIndicator_scalingLookback, m2LeadingIndicator_scalingLookback)

plot(rsid_osc, title="RSI Divergence Oscillator", color=color.new(color.orange, 0))
plot(stoch_value, title="Stochastic Value", color=color.new(color.blue, 0))
plot(m2_shortOffsetDiffToNbarsOut, title="M2 Short Offset Diff", color=color.new(color.purple, 0))
plot(scaled_macd_slope, title="MACD Slope (Scaled)", color=color.new(color.teal, 0))
plot(macd_slope_current, title="MACD Slope (Raw)", display = display.data_window)

// Add reference lines
hline(100, "100", color=color.gray, linestyle=hline.style_dotted)
hline(80, "80", color=color.gray, linestyle=hline.style_dotted)
hline(50, "50", color=color.gray, linestyle=hline.style_dashed)
hline(20, "20", color=color.gray, linestyle=hline.style_dotted)
hline(0, "Zero Line", color=color.gray, linestyle=hline.style_solid)
