Indicator · Pine Script v6 · On the chart

BTC Strategy Multi-Factor Engine

by grzresearch

A Bitcoin-focused indicator drawn over the price chart that combines several context modules.

View on GitHub

GitHub stars1on the repository
Pine versionv6current
Lines of code995
Settings40inputs
LicenceMPL-2.0
Last update2 days ago27 Oct 2026

Add It to Your TradingView Chart

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

Source Code

995 lines of Pine Script v6 from BTC_Strategy_Institutional_Multi-Factor_Engine.pine, published under MPL-2.0. Keep the author's credit and licence lines when you reuse it.

BTC_Strategy_Institutional_Multi-Factor_Engine.pine
// ══════════════════════════════════════════════════════════════════════════════
// BTC STRATEGY (Indicator Version - Fixed)
// ══════════════════════════════════════════════════════════════════════════════

//@version=6
indicator("BTC Strategy", overlay=true, max_bars_back=5000)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 1: INPUT PARAMETERS
// ══════════════════════════════════════════════════════════════════════════════

// 1.1 WEEKEND PROTECTION
grp_weekend = "═══ WEEKEND LIQUIDITY PROTECTION ═══"
i_weekendProtection = input.bool(true, "Enable Weekend Protection", group=grp_weekend)
i_weekendReducePct = input.float(50.0, "Position Reduction %", minval=0, maxval=100, step=5, group=grp_weekend)
i_fridayExitHour = input.int(20, "Friday Exit Hour (UTC)", minval=0, maxval=23, group=grp_weekend)
i_sundayEntryHour = input.int(22, "Sunday Re-Entry Hour (UTC)", minval=0, maxval=23, group=grp_weekend)
i_weekendVolThreshold = input.float(0.5, "Weekend Vol Multiplier Threshold", minval=0.1, maxval=2.0, step=0.1, group=grp_weekend)

// 1.2 4-YEAR HALVING CYCLE
grp_cycle = "═══ 4-YEAR HALVING CYCLE ═══"
i_cycleEnabled = input.bool(true, "Enable Cycle Analysis", group=grp_cycle)
i_halvingDate2024 = input.time(timestamp("2024-04-20"), "Last Halving Date", group=grp_cycle)
i_cycleAccumulationEnd = input.int(180, "Accumulation Phase (Days Post-Halving)", group=grp_cycle)
i_cycleBullEnd = input.int(540, "Bull Phase End (Days Post-Halving)", group=grp_cycle)
i_cycleDistributionEnd = input.int(900, "Distribution Phase End (Days)", group=grp_cycle)
i_cycleBias = input.float(1.5, "Cycle Bull Phase Position Multiplier", minval=0.5, maxval=3.0, group=grp_cycle)
i_cycleBearBias = input.float(0.5, "Cycle Bear Phase Position Multiplier", minval=0.1, maxval=1.0, group=grp_cycle)

// 1.3 CROSS-MARKET CORRELATION
grp_markets = "═══ CROSS-MARKET SCREENING ═══"
i_crossMarketEnabled = input.bool(true, "Enable Cross-Market Analysis", group=grp_markets)
i_spxSymbol = input.symbol("OANDA:SPX500USD", "S&P 500 Symbol", group=grp_markets)
i_daxSymbol = input.symbol("TVC:DEU40", "DAX Symbol", group=grp_markets) 
i_hsiSymbol = input.symbol("TVC:HSI", "Hang Seng Symbol", group=grp_markets)
i_dxySymbol = input.symbol("TVC:DXY", "Dollar Index", group=grp_markets)
i_vixSymbol = input.symbol("TVC:VIX", "VIX Symbol", group=grp_markets)
i_corrPeriod = input.int(50, "Correlation Period", minval=10, maxval=200, group=grp_markets)
i_corrThreshold = input.float(0.6, "Correlation Threshold", minval=0.1, maxval=0.95, group=grp_markets)
i_riskOffVIX = input.float(25.0, "VIX Risk-Off Threshold", group=grp_markets)
i_riskOffDXY = input.float(2.0, "DXY Momentum Risk-Off", group=grp_markets)

// 1.4 OPEN INTEREST
grp_oi = "═══ OPEN INTEREST FLOW ═══"
i_oiEnabled = input.bool(true, "Enable OI Analysis", group=grp_oi)
i_oiSymbol = input.symbol("BINANCE:BTCUSDTPERP", "Perp Symbol for OI Proxy", group=grp_oi)
i_oiPeriod = input.int(20, "OI Momentum Period", minval=5, maxval=100, group=grp_oi)
i_oiDivergencePeriod = input.int(14, "OI Divergence Period", group=grp_oi)
i_oiFlipSensitivity = input.float(1.5, "OI Flip Sensitivity (StdDev)", minval=0.5, maxval=3.0, group=grp_oi)

// 1.5 SPOT FLOW
grp_spot = "═══ SPOT FLOW ANALYSIS ═══"
i_spotEnabled = input.bool(true, "Enable Spot Flow Analysis", group=grp_spot)
i_volumeDeltaPeriod = input.int(20, "Volume Delta Period", group=grp_spot)
i_cvdPeriod = input.int(50, "CVD Smoothing Period", group=grp_spot)
i_largeOrderThreshold = input.float(2.0, "Large Order Threshold (StdDev)", group=grp_spot)
i_absorptionPeriod = input.int(10, "Absorption Detection Period", group=grp_spot)

// 1.6 TREASURY
grp_treasury = "═══ TREASURY HOLDINGS PROXY ═══"
i_treasuryEnabled = input.bool(true, "Enable Treasury Analysis", group=grp_treasury)
i_mstrSymbol = input.symbol("NASDAQ:MSTR", "MicroStrategy Symbol", group=grp_treasury)
i_coinSymbol = input.symbol("NASDAQ:COIN", "Coinbase Symbol", group=grp_treasury)
i_grayscaleSymbol = input.symbol("AMEX:GBTC", "GBTC Symbol", group=grp_treasury)
i_mstrCorrPeriod = input.int(30, "MSTR Correlation Period", group=grp_treasury)
i_mstrLeadPeriod = input.int(5, "MSTR Lead/Lag Period", group=grp_treasury)

// 1.7 ML ENGINE
grp_ml = "═══ ML SIGNAL ENGINE ═══"
i_mlEnabled = input.bool(true, "Enable ML Signal Engine", group=grp_ml)
i_ensembleMethod = input.string("Weighted Average", "Ensemble Method", options=["Weighted Average", "Voting", "Stacking"], group=grp_ml)
i_lookbackML = input.int(100, "ML Lookback Period", group=grp_ml)
i_baseSignalThreshold = input.float(0.65, "Base Signal Threshold", minval=0.5, maxval=0.95, group=grp_ml)
i_adaptiveWeights = input.bool(true, "Enable Adaptive Weighting", group=grp_ml)

// 1.8 ★ KALMAN FILTER
grp_kalman = "═══ ★ KALMAN FILTER ═══"
i_kalmanEnabled = input.bool(true, "Enable Kalman Filter", group=grp_kalman)
i_kalmanMeasurementNoise = input.float(0.1, "Measurement Noise (R)", minval=0.01, maxval=1.0, step=0.01, group=grp_kalman)
i_kalmanProcessNoise = input.float(0.01, "Process Noise (Q)", minval=0.001, maxval=0.1, step=0.001, group=grp_kalman)
i_kalmanAdaptive = input.bool(true, "Adaptive Noise Estimation", group=grp_kalman)

// 1.9 ★ DYNAMIC THRESHOLDS
grp_dynThresh = "═══ ★ DYNAMIC THRESHOLDS ═══"
i_dynThreshEnabled = input.bool(true, "Enable Dynamic Thresholds", group=grp_dynThresh)
i_dynThreshVolWeight = input.float(0.1, "Volatility Adjustment Weight", minval=0.01, maxval=0.3, step=0.01, group=grp_dynThresh)
i_dynThreshQualityWeight = input.float(0.05, "Signal Quality Adjustment", minval=0.01, maxval=0.15, step=0.01, group=grp_dynThresh)
i_dynThreshMinThreshold = input.float(0.55, "Minimum Threshold", minval=0.5, maxval=0.7, group=grp_dynThresh)
i_dynThreshMaxThreshold = input.float(0.75, "Maximum Threshold", minval=0.65, maxval=0.9, group=grp_dynThresh)

// 1.10 ★ INFORMATION COEFFICIENT
grp_ic = "═══ ★ INFORMATION COEFFICIENT ═══"
i_icEnabled = input.bool(true, "Enable IC Filtering", group=grp_ic)
i_icPeriod = input.int(50, "IC Calculation Period", minval=20, maxval=200, group=grp_ic)
i_icMinThreshold = input.float(0.05, "Minimum IC Threshold", minval=-0.1, maxval=0.3, step=0.01, group=grp_ic)
i_icDecayFactor = input.float(0.95, "IC Decay Factor", minval=0.8, maxval=0.99, group=grp_ic)
i_icHorizon1 = input.int(1, "IC Horizon 1 (bars)", minval=1, maxval=10, group=grp_ic)
i_icHorizon2 = input.int(5, "IC Horizon 2 (bars)", minval=2, maxval=20, group=grp_ic)

// 1.11 ★ CVaR
grp_cvar = "═══ ★ CVaR RISK ENGINE ═══"
i_cvarEnabled = input.bool(true, "Enable CVaR Position Sizing", group=grp_cvar)
i_cvarConfidence = input.float(0.95, "CVaR Confidence Level", minval=0.9, maxval=0.99, step=0.01, group=grp_cvar)
i_cvarPeriod = input.int(100, "CVaR Lookback Period", minval=50, maxval=500, group=grp_cvar)
i_cvarMaxPosition = input.float(25.0, "CVaR Max Position %", minval=5, maxval=50, group=grp_cvar)
i_cvarMinPosition = input.float(2.0, "CVaR Min Position %", minval=1, maxval=10, group=grp_cvar)
i_cvarTargetRisk = input.float(2.0, "Target Daily Risk %", minval=0.5, maxval=5.0, step=0.1, group=grp_cvar)

// 1.12 ★ LIQUIDATION CASCADE
grp_liq = "═══ ★ LIQUIDATION CASCADE ═══"
i_liqEnabled = input.bool(true, "Enable Liquidation Detection", group=grp_liq)
i_liqVolSpikeMult = input.float(3.0, "Volume Spike Multiplier", minval=2.0, maxval=5.0, group=grp_liq)
i_liqPriceMovePct = input.float(2.0, "Min Price Move %", minval=1.0, maxval=5.0, step=0.5, group=grp_liq)
i_liqCooldownBars = input.int(5, "Cooldown Bars After Cascade", minval=1, maxval=20, group=grp_liq)
i_liqLookback = input.int(20, "Liquidation Detection Period", minval=10, maxval=50, group=grp_liq)
i_liqCascadeScore = input.float(0.7, "Cascade Score Threshold", minval=0.5, maxval=1.0, group=grp_liq)

// 1.13 RISK MANAGEMENT
grp_risk = "═══ RISK MANAGEMENT ═══"
i_riskEnabled = input.bool(true, "Enable Dynamic Risk Management", group=grp_risk)
i_maxDrawdown = input.float(15.0, "Max Drawdown % (Stop Trading)", minval=5, maxval=50, group=grp_risk)
i_atrPeriod = input.int(14, "ATR Period", group=grp_risk)
i_atrMultiplierSL = input.float(2.0, "ATR Multiplier for Stop Loss", minval=0.5, maxval=5.0, group=grp_risk)
i_atrMultiplierTP = input.float(4.0, "ATR Multiplier for Take Profit", minval=1.0, maxval=10.0, group=grp_risk)
i_trailingEnabled = input.bool(true, "Enable Trailing Stop", group=grp_risk)
i_trailingATR = input.float(1.5, "Trailing Stop ATR Multiplier", group=grp_risk)
i_maxPositionSize = input.float(30.0, "Max Position Size %", minval=5, maxval=100, group=grp_risk)
i_kellyFraction = input.float(0.25, "Kelly Criterion Fraction", minval=0.1, maxval=0.5, group=grp_risk)

// 1.14 REGIME DETECTION
grp_regime = "═══ REGIME DETECTION ═══"
i_regimeEnabled = input.bool(true, "Enable Regime Detection", group=grp_regime)
i_regimePeriod = input.int(50, "Regime Detection Period", group=grp_regime)
i_trendStrength = input.float(25.0, "ADX Trend Threshold", group=grp_regime)
i_volRegimeMultiplier = input.float(1.5, "High Vol Regime Multiplier", group=grp_regime)
i_meanRevPeriod = input.int(20, "Mean Reversion Period", group=grp_regime)

// 1.15 TECHNICAL INDICATORS
grp_tech = "═══ TECHNICAL ANALYSIS ═══"
i_emaFast = input.int(21, "Fast EMA Period", group=grp_tech)
i_emaSlow = input.int(55, "Slow EMA Period", group=grp_tech)
i_ema200 = input.int(200, "Trend EMA Period", group=grp_tech)
i_rsiPeriod = input.int(14, "RSI Period", group=grp_tech)
i_rsiOB = input.float(70.0, "RSI Overbought", group=grp_tech)
i_rsiOS = input.float(30.0, "RSI Oversold", group=grp_tech)
i_macdFast = input.int(12, "MACD Fast", group=grp_tech)
i_macdSlow = input.int(26, "MACD Slow", group=grp_tech)
i_macdSignal = input.int(9, "MACD Signal", group=grp_tech)
i_bbPeriod = input.int(20, "Bollinger Period", group=grp_tech)
i_bbStdDev = input.float(2.0, "Bollinger StdDev", group=grp_tech)

// 1.16 VISUALIZATION
grp_viz = "═══ VISUALIZATION ═══"
i_showDashboard = input.bool(true, "Show Analytics Dashboard", group=grp_viz)
i_showSignals = input.bool(true, "Show Entry/Exit Signals", group=grp_viz)
i_showRegime = input.bool(true, "Show Regime Background", group=grp_viz)
i_showNewFeatures = input.bool(true, "Show New v4.0 Features", group=grp_viz)
i_dashboardPosition = input.string("Top Right", "Dashboard Position", options=["Top Right", "Top Left", "Bottom Right", "Bottom Left"], group=grp_viz)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 2: HELPER FUNCTIONS
// ══════════════════════════════════════════════════════════════════════════════

// Custom sum function (replaces ta.sum which doesn't exist)
f_sum(src, len) =>
    float result = 0.0
    for i = 0 to len - 1
        result += nz(src[i])
    result

f_zscore(src, len) =>
    mean = ta.sma(src, len)
    stdDev = ta.stdev(src, len)
    stdDev == 0 ? 0.0 : (src - mean) / stdDev

f_normalize(src, len) =>
    minVal = ta.lowest(src, len)
    maxVal = ta.highest(src, len)
    range_val = maxVal - minVal
    range_val == 0 ? 0.5 : (src - minVal) / range_val

f_correlation(src1, src2, len) =>
    ta.correlation(src1, src2, len)

f_adaptiveMA(src, len) =>
    change_abs = math.abs(ta.change(src, len))
    sum_abs_change = f_sum(math.abs(ta.change(src)), len)
    er = sum_abs_change == 0 ? 0.0 : change_abs / sum_abs_change
    sc = math.pow(er * (2.0/(2+1) - 2.0/(len+1)) + 2.0/(len+1), 2)
    var float kama = na
    kama := na(kama) ? src : kama + sc * (src - kama)
    kama

// ★ KALMAN FILTER FUNCTION
f_kalmanFilter(src, measurementNoise, processNoise, adaptive) =>
    var float estimate = na
    var float errorEstimate = 1.0
    var float adaptiveR = measurementNoise
    var float adaptiveQ = processNoise
    
    if na(estimate)
        estimate := src
    
    // Pre-calculate stdev outside conditional for consistency
    priceVol = ta.stdev(src, 10)
    avgVol = ta.sma(priceVol, 50)
    
    if adaptive
        innovation = src - estimate
        innovationVar = ta.variance(innovation, 20)
        adaptiveR := math.max(0.01, math.min(1.0, nz(innovationVar, measurementNoise)))
        volRatio = avgVol == 0 ? 1.0 : priceVol / avgVol
        adaptiveQ := processNoise * math.max(0.5, math.min(3.0, volRatio))
    else
        adaptiveR := measurementNoise
        adaptiveQ := processNoise
    
    prediction = estimate
    errorPrediction = errorEstimate + adaptiveQ
    kalmanGain = errorPrediction / (errorPrediction + adaptiveR)
    estimate := prediction + kalmanGain * (src - prediction)
    errorEstimate := (1 - kalmanGain) * errorPrediction
    
    [estimate, kalmanGain, errorEstimate]

// ★ DYNAMIC THRESHOLD FUNCTION
f_dynamicThreshold(signal, vol, volSMA, baseThreshold, volWeight, qualityWeight, minThresh, maxThresh, len) =>
    volNorm = volSMA == 0 ? 1.0 : vol / volSMA
    volAdjust = (volNorm - 1.0) * volWeight
    
    signalStd = ta.stdev(signal, len)
    signalMean = ta.sma(signal, len)
    signalCV = math.abs(signalMean) == 0 ? 0.0 : signalStd / math.abs(signalMean)
    qualityAdjust = math.min(signalCV * qualityWeight, qualityWeight)
    
    dynThreshold = baseThreshold + volAdjust - qualityAdjust
    math.max(minThresh, math.min(maxThresh, dynThreshold))

// ★ INFORMATION COEFFICIENT FUNCTION
f_informationCoefficient(predictions, outcomes, len) =>
    ta.correlation(predictions, outcomes, len)

f_calculateIC(signal, horizon, period) =>
    fwdReturn = close[horizon] == 0 ? 0.0 : (close - close[horizon]) / close[horizon]
    laggedSignal = signal[horizon]
    f_informationCoefficient(laggedSignal, fwdReturn, period)

// ★ CVaR CALCULATION FUNCTION
f_calculateCVaR(returns, confidence, len) =>
    varLevel = ta.percentile_linear_interpolation(returns, len, (1 - confidence) * 100)
    
    var float sumBelowVaR = 0.0
    var int countBelowVaR = 0
    
    sumBelowVaR := 0.0
    countBelowVaR := 0
    
    for i = 0 to math.min(len - 1, 99)
        if returns[i] < varLevel
            sumBelowVaR += returns[i]
            countBelowVaR += 1
    
    cvar = countBelowVaR > 0 ? sumBelowVaR / countBelowVaR : varLevel
    [varLevel, cvar]

f_cvarPositionSize(cvar, targetRisk, maxPos, minPos) =>
    if cvar >= 0
        minPos
    else
        rawSize = targetRisk / math.abs(cvar) * 100
        math.max(minPos, math.min(maxPos, rawSize))

// ★ LIQUIDATION CASCADE DETECTION
f_detectLiquidationCascade(volSpikeMult, priceMovePct, lookback) =>
    avgVol = ta.sma(volume, lookback)
    volSpike = volume > avgVol * volSpikeMult
    
    priceMove = close[1] == 0 ? 0.0 : math.abs(close - close[1]) / close[1] * 100
    largePriceMove = priceMove > priceMovePct
    
    cascadeDown = close < close[1]
    cascadeUp = close > close[1]
    
    consecutiveCascadeBars = 0
    for i = 0 to 2
        priceMove_i = close[i+1] == 0 ? 0.0 : math.abs(close[i] - close[i+1]) / close[i+1] * 100
        if volume[i] > avgVol * (volSpikeMult * 0.7) and priceMove_i > priceMovePct * 0.7
            consecutiveCascadeBars += 1
    
    cascadeScore = 0.0
    cascadeScore += volSpike ? 0.35 : 0.0
    cascadeScore += largePriceMove ? 0.35 : 0.0
    cascadeScore += consecutiveCascadeBars >= 2 ? 0.3 : (consecutiveCascadeBars == 1 ? 0.15 : 0.0)
    
    cascadeDirection = cascadeDown ? -1 : (cascadeUp ? 1 : 0)
    [cascadeScore, cascadeDirection, volSpike and largePriceMove]

// VOLATILITY FUNCTIONS
f_historicalVol(src, len) =>
    logReturns = src[1] == 0 ? 0.0 : math.log(src / src[1])
    ta.stdev(logReturns, len) * math.sqrt(365) * 100

// TIME FUNCTIONS
f_isWeekend() =>
    dayofweek == dayofweek.saturday or dayofweek == dayofweek.sunday

f_isFridayEvening(exitHour) =>
    dayofweek == dayofweek.friday and hour >= exitHour

f_isSundayEvening(entryHour) =>
    dayofweek == dayofweek.sunday and hour >= entryHour

f_daysSinceHalving(halvingTime) =>
    math.floor((time - halvingTime) / (24 * 60 * 60 * 1000))

// MARKET MICROSTRUCTURE
f_volumeDelta() =>
    range_hl = high - low
    buyVol = range_hl != 0 ? volume * (close - low) / range_hl : volume * 0.5
    sellVol = range_hl != 0 ? volume * (high - close) / range_hl : volume * 0.5
    buyVol - sellVol

f_cvd(len) =>
    delta = f_volumeDelta()
    ta.cum(delta)

f_orderFlowImbalance(len) =>
    delta = f_volumeDelta()
    bullBars = f_sum(delta > 0 ? delta : 0.0, len)
    bearBars = f_sum(delta < 0 ? math.abs(delta) : 0.0, len)
    total = bullBars + bearBars
    total == 0 ? 0.0 : (bullBars - bearBars) / total

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 3: DATA RETRIEVAL
// ══════════════════════════════════════════════════════════════════════════════

[spxClose, spxHigh, spxLow, spxVolume] = request.security(i_spxSymbol, timeframe.period, [close, high, low, volume], barmerge.gaps_on, barmerge.lookahead_off)
[daxClose, daxHigh, daxLow] = request.security(i_daxSymbol, timeframe.period, [close, high, low], barmerge.gaps_on, barmerge.lookahead_off)
[hsiClose, hsiHigh, hsiLow] = request.security(i_hsiSymbol, timeframe.period, [close, high, low], barmerge.gaps_on, barmerge.lookahead_off)
dxyClose = request.security(i_dxySymbol, timeframe.period, close, barmerge.gaps_on, barmerge.lookahead_off)
vixClose = request.security(i_vixSymbol, timeframe.period, close, barmerge.gaps_on, barmerge.lookahead_off)

[mstrClose, mstrVolume] = request.security(i_mstrSymbol, timeframe.period, [close, volume], barmerge.gaps_on, barmerge.lookahead_off)
[coinClose, coinVolume] = request.security(i_coinSymbol, timeframe.period, [close, volume], barmerge.gaps_on, barmerge.lookahead_off)
gbtcClose = request.security(i_grayscaleSymbol, timeframe.period, close, barmerge.gaps_on, barmerge.lookahead_off)

[perpClose, perpVolume, perpOI] = request.security(i_oiSymbol, timeframe.period, [close, volume, open], barmerge.gaps_on, barmerge.lookahead_off)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 4: MODULE CALCULATIONS
// ══════════════════════════════════════════════════════════════════════════════

// 4.1 WEEKEND PROTECTION
isWeekend = f_isWeekend()
isFridayExit = f_isFridayEvening(i_fridayExitHour)
isSundayEntry = f_isSundayEvening(i_sundayEntryHour)

// Pre-calculate stdev values outside ternary operator for consistency
weekdayVolCalc = ta.stdev(close, 5)
weekendVolCalc = ta.stdev(close, 2)

weekendVolRatio = isWeekend ? (weekdayVolCalc == 0 ? 1.0 : weekendVolCalc / weekdayVolCalc) : 1.0
weekendRiskScore = isWeekend ? math.min(weekendVolRatio / i_weekendVolThreshold * 100, 100) : 0.0
weekendProtectionActive = i_weekendProtection and (isWeekend or isFridayExit) and not isSundayEntry
weekendPositionMult = weekendProtectionActive ? (100 - i_weekendReducePct) / 100 : 1.0

// 4.2 4-YEAR CYCLE
daysSinceHalving = f_daysSinceHalving(i_halvingDate2024)
cycleDay = daysSinceHalving % 1460

cyclePhase = cycleDay <= i_cycleAccumulationEnd ? 1 : cycleDay <= i_cycleBullEnd ? 2 : cycleDay <= i_cycleDistributionEnd ? 3 : 4

cyclePhaseStr = cyclePhase == 1 ? "ACCUMULATION" : cyclePhase == 2 ? "BULL MARKET" : cyclePhase == 3 ? "DISTRIBUTION" : "BEAR MARKET"

cyclePositionMult = i_cycleEnabled ? (cyclePhase == 1 ? 1.0 : cyclePhase == 2 ? i_cycleBias : cyclePhase == 3 ? 0.75 : i_cycleBearBias) : 1.0

// 4.3 CROSS-MARKET CORRELATION
btcReturns = ta.roc(close, 1)
spxReturns = ta.roc(nz(spxClose, close), 1)
daxReturns = ta.roc(nz(daxClose, close), 1)
hsiReturns = ta.roc(nz(hsiClose, close), 1)
dxyReturns = ta.roc(nz(dxyClose, 100), 1)

corrSPX = f_correlation(btcReturns, spxReturns, i_corrPeriod)
corrDAX = f_correlation(btcReturns, daxReturns, i_corrPeriod)
corrHSI = f_correlation(btcReturns, hsiReturns, i_corrPeriod)
corrDXY = f_correlation(btcReturns, dxyReturns, i_corrPeriod)

spxAboveMA = nz(spxClose, 0) > ta.sma(nz(spxClose, close), 50)
daxAboveMA = nz(daxClose, 0) > ta.sma(nz(daxClose, close), 50)
hsiAboveMA = nz(hsiClose, 0) > ta.sma(nz(hsiClose, close), 50)

marketBreadth = (spxAboveMA ? 1 : 0) + (daxAboveMA ? 1 : 0) + (hsiAboveMA ? 1 : 0)
marketBreadthScore = marketBreadth / 3.0 * 100

vixLevel = nz(vixClose, 20)
dxyMomentum = ta.roc(nz(dxyClose, 100), 10)
riskOffSignal = vixLevel > i_riskOffVIX or dxyMomentum > i_riskOffDXY

crossMarketScore = i_crossMarketEnabled ? (f_normalize(corrSPX, i_corrPeriod) * 0.3 + f_normalize(marketBreadthScore, i_corrPeriod) * 0.3 + (1 - f_normalize(vixLevel, i_corrPeriod)) * 0.2 + (1 - f_normalize(dxyMomentum, i_corrPeriod)) * 0.2) : 0.5

// 4.4 OPEN INTEREST ANALYSIS
oiProxy = ta.sma(perpVolume, i_oiPeriod)
oiChange = ta.change(oiProxy)
oiMomentum = ta.roc(oiProxy, i_oiPeriod)
oiZscore = f_zscore(oiProxy, i_oiPeriod * 2)

oiFlipBullish = ta.crossover(oiZscore, i_oiFlipSensitivity) and close > close[1]
oiFlipBearish = ta.crossunder(oiZscore, -i_oiFlipSensitivity) and close < close[1]

priceUp = close > close[i_oiDivergencePeriod]
oiUp = oiProxy > oiProxy[i_oiDivergencePeriod]
oiBullishDiv = not priceUp and oiUp
oiBearishDiv = priceUp and not oiUp

longBias = f_volumeDelta() > 0
longBiasSum = f_sum(longBias ? volume : 0.0, i_oiPeriod)
shortBiasSum = f_sum(not longBias ? volume : 0.0, i_oiPeriod)
longShortRatioProxy = shortBiasSum == 0 ? 1.0 : longBiasSum / shortBiasSum

oiScore = i_oiEnabled ? f_normalize(oiMomentum, i_oiPeriod * 2) * 0.4 + (oiFlipBullish ? 0.3 : oiFlipBearish ? -0.3 : 0.0) + (oiBullishDiv ? 0.15 : oiBearishDiv ? -0.15 : 0.0) + f_normalize(longShortRatioProxy, i_oiPeriod) * 0.15 : 0.5

// 4.5 SPOT VOLUME ANALYSIS
volumeDelta = f_volumeDelta()
volumeDeltaSMA = ta.sma(volumeDelta, i_volumeDeltaPeriod)
volumeDeltaStdDev = ta.stdev(volumeDelta, i_volumeDeltaPeriod)

cvd = f_cvd(i_cvdPeriod)
cvdSMA = ta.sma(cvd, i_cvdPeriod)
cvdTrend = cvd > cvdSMA

largeOrderThreshold = volumeDeltaStdDev * i_largeOrderThreshold
largeBuyOrder = volumeDelta > largeOrderThreshold
largeSellOrder = volumeDelta < -largeOrderThreshold

absorption = ta.highest(volume, i_absorptionPeriod) == volume and math.abs(close - open) < math.abs(close[1] - open[1])
buyAbsorption = absorption and close < open and volumeDelta > 0
sellAbsorption = absorption and close > open and volumeDelta < 0

orderFlowImbalance = f_orderFlowImbalance(i_volumeDeltaPeriod)

vwapValue = ta.vwap(hlc3)
vwapDev = close - vwapValue
vwapZscore = f_zscore(vwapDev, i_volumeDeltaPeriod)

spotScore = i_spotEnabled ? f_normalize(orderFlowImbalance, i_volumeDeltaPeriod) * 0.35 + (cvdTrend ? 0.25 : -0.25) + f_normalize(-vwapZscore, i_volumeDeltaPeriod) * 0.2 + (largeBuyOrder ? 0.1 : largeSellOrder ? -0.1 : 0.0) + (buyAbsorption ? 0.1 : sellAbsorption ? -0.1 : 0.0) : 0.5

// 4.6 TREASURY ANALYSIS
mstrReturns = ta.roc(nz(mstrClose, close), 1)
mstrBTCCorr = f_correlation(mstrReturns, btcReturns, i_mstrCorrPeriod)
mstrMomentum = ta.roc(nz(mstrClose, close), 20)

// Pre-calculate stdev values for consistency
mstrStdev = ta.stdev(mstrReturns, 20)
btcStdev = ta.stdev(btcReturns, 20)
mstrVolRatio = btcStdev == 0 ? 1.0 : mstrStdev / btcStdev

mstrLeadSignal = ta.crossover(ta.ema(nz(mstrClose, close), 10), ta.ema(nz(mstrClose, close), 20))
coinMomentum = ta.roc(nz(coinClose, close), 20)
coinVolume20 = ta.sma(nz(coinVolume, volume), 20)
coinVolumeSpike = nz(coinVolume, volume) > coinVolume20 * 1.5

gbtcMomentum = ta.roc(nz(gbtcClose, close), 10)

treasuryScore = i_treasuryEnabled ? f_normalize(mstrMomentum, 50) * 0.35 + f_normalize(coinMomentum, 50) * 0.25 + f_normalize(gbtcMomentum, 50) * 0.2 + (mstrLeadSignal ? 0.1 : 0.0) + (coinVolumeSpike ? 0.1 : 0.0) : 0.5

// 4.7 REGIME DETECTION
[diPlus, diMinus, adxValue] = ta.dmi(14, 14)
trendStrength = adxValue
isTrending = adxValue > i_trendStrength
trendDirection = diPlus > diMinus ? 1 : -1

hvol = f_historicalVol(close, i_regimePeriod)
hvolSMA = ta.sma(hvol, i_regimePeriod * 2)
highVolRegime = hvol > hvolSMA * i_volRegimeMultiplier
lowVolRegime = hvol < hvolSMA / i_volRegimeMultiplier

meanPrice = ta.sma(close, i_meanRevPeriod)
meanDeviation = meanPrice == 0 ? 0.0 : (close - meanPrice) / meanPrice * 100
isMeanReverting = not isTrending and math.abs(meanDeviation) > 5

regime = isTrending and trendDirection == 1 ? 1 : isTrending and trendDirection == -1 ? 2 : isMeanReverting and meanDeviation > 0 ? 3 : isMeanReverting and meanDeviation < 0 ? 4 : 5

regimeStr = regime == 1 ? "BULL TREND" : regime == 2 ? "BEAR TREND" : regime == 3 ? "MEAN REV HIGH" : regime == 4 ? "MEAN REV LOW" : "RANGING"

// 4.8 TECHNICAL INDICATORS
emaFast = ta.ema(close, i_emaFast)
emaSlow = ta.ema(close, i_emaSlow)
ema200 = ta.ema(close, i_ema200)
kamaValue = f_adaptiveMA(close, i_emaSlow)

rsiValue = ta.rsi(close, i_rsiPeriod)
rsiMA = ta.sma(rsiValue, 14)
rsiBullish = rsiValue > rsiMA and rsiValue > 50
rsiBearish = rsiValue < rsiMA and rsiValue < 50

[macdLine, signalLine, histLine] = ta.macd(close, i_macdFast, i_macdSlow, i_macdSignal)
macdBullish = macdLine > signalLine and histLine > histLine[1]
macdBearish = macdLine < signalLine and histLine < histLine[1]

[bbUpper, bbMiddle, bbLower] = ta.bb(close, i_bbPeriod, i_bbStdDev)
bbWidth = bbMiddle == 0 ? 0.0 : (bbUpper - bbLower) / bbMiddle * 100
bbSqueeze = bbWidth < ta.sma(bbWidth, 50) * 0.75
bbBreakoutUp = close > bbUpper
bbBreakoutDown = close < bbLower

stochRSIK = ta.stoch(rsiValue, rsiValue, rsiValue, 14)
stochRSID = ta.sma(stochRSIK, 3)
stochOversold = stochRSIK < 20 and stochRSID < 20
stochOverbought = stochRSIK > 80 and stochRSID > 80

atrValue = ta.atr(i_atrPeriod)
atrNormalized = close == 0 ? 0.0 : atrValue / close * 100

tenkan = (ta.highest(high, 9) + ta.lowest(low, 9)) / 2
kijun = (ta.highest(high, 26) + ta.lowest(low, 26)) / 2
senkouA = (tenkan + kijun) / 2
senkouB = (ta.highest(high, 52) + ta.lowest(low, 52)) / 2
ichimokuBullish = close > senkouA and close > senkouB and tenkan > kijun
ichimokuBearish = close < senkouA and close < senkouB and tenkan < kijun

techScore = (emaFast > emaSlow ? 0.15 : -0.15) + (close > ema200 ? 0.1 : -0.1) + (rsiBullish ? 0.1 : rsiBearish ? -0.1 : 0.0) + (macdBullish ? 0.15 : macdBearish ? -0.15 : 0.0) + f_normalize(100 - rsiValue, 100) * 0.1 + (ichimokuBullish ? 0.1 : ichimokuBearish ? -0.1 : 0.0) + (bbSqueeze ? 0.05 : 0.0) + (stochOversold ? 0.1 : stochOverbought ? -0.1 : 0.0) + f_normalize(-meanDeviation, 20) * 0.15

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 5: ★ MODULE
// ══════════════════════════════════════════════════════════════════════════════

// 5.1 ★ LIQUIDATION CASCADE DETECTION
[cascadeScore, cascadeDirection, cascadeActive] = f_detectLiquidationCascade(i_liqVolSpikeMult, i_liqPriceMovePct, i_liqLookback)

var int cascadeCooldown = 0
if cascadeActive and cascadeScore >= i_liqCascadeScore
    cascadeCooldown := i_liqCooldownBars

if cascadeCooldown > 0
    cascadeCooldown -= 1

liquidationFilter = i_liqEnabled ? (cascadeScore < i_liqCascadeScore and cascadeCooldown == 0) : true
liquidationWarning = cascadeScore >= i_liqCascadeScore or cascadeCooldown > 0

// 5.2 ★ CVaR CALCULATION
dailyReturns = ta.roc(close, 1) / 100
[varLevel, cvarLevel] = f_calculateCVaR(dailyReturns, i_cvarConfidence, i_cvarPeriod)

cvarPositionSize = i_cvarEnabled ? f_cvarPositionSize(cvarLevel, i_cvarTargetRisk / 100, i_cvarMaxPosition, i_cvarMinPosition) : 10.0

// 5.3 ML SIGNAL ENGINE
feat_momentum1 = ta.roc(close, 5)
feat_momentum2 = ta.roc(close, 10)
feat_momentum3 = ta.roc(close, 20)
feat_momentumComposite = (feat_momentum1 + feat_momentum2 * 2 + feat_momentum3 * 3) / 6

feat_vol1 = f_historicalVol(close, 10)
feat_vol2 = f_historicalVol(close, 30)
feat_volRatio = feat_vol2 == 0 ? 1.0 : feat_vol1 / feat_vol2

feat_meanRev1 = f_zscore(close, 20)
feat_meanRev2 = f_zscore(close, 50)
feat_bbPosition = (bbUpper - bbLower) == 0 ? 0.5 : (close - bbLower) / (bbUpper - bbLower)

feat_trendStrength = adxValue / 100
feat_trendDirection = diPlus > diMinus ? 1 : -1
feat_emaSlope = emaFast[5] == 0 ? 0.0 : (emaFast - emaFast[5]) / emaFast[5] * 100

// [FIX] Calculate SMA outside of ternary for consistency
volSMA20 = ta.sma(volume, 20)
feat_volumeRatio = volSMA20 == 0 ? 1.0 : volume / volSMA20
feat_volumeTrend = ta.roc(ta.sma(volume, 10), 10)

feat_corrComposite = (nz(corrSPX, 0) + nz(corrDAX, 0) + nz(corrHSI, 0)) / 3
feat_riskSentiment = riskOffSignal ? -1 : 1

feat_orderFlow = orderFlowImbalance
feat_cvdTrend = ta.roc(cvd, 10)

// Adaptive weights
var float[] recentReturns = array.new_float(100, 0)
currentReturn = ta.roc(close, 1)
array.push(recentReturns, currentReturn)
if array.size(recentReturns) > 100
    array.shift(recentReturns)

avgReturn = array.avg(recentReturns)
returnStdDev = array.stdev(recentReturns)

w_momentum = isTrending ? 0.25 : 0.1
w_meanRev = isMeanReverting ? 0.25 : 0.1
w_volume = highVolRegime ? 0.2 : 0.15
w_crossMarket = math.abs(nz(corrSPX, 0)) > i_corrThreshold ? 0.2 : 0.1
w_technical = 0.15
w_cycle = i_cycleEnabled ? 0.1 : 0.0
w_oi = i_oiEnabled ? 0.15 : 0.0
w_spot = i_spotEnabled ? 0.1 : 0.0
w_treasury = i_treasuryEnabled ? 0.05 : 0.0

totalWeight = w_momentum + w_meanRev + w_volume + w_crossMarket + w_technical + w_cycle + w_oi + w_spot + w_treasury
w_momentum := w_momentum / totalWeight
w_meanRev := w_meanRev / totalWeight
w_volume := w_volume / totalWeight
w_crossMarket := w_crossMarket / totalWeight
w_technical := w_technical / totalWeight
w_cycle := w_cycle / totalWeight
w_oi := w_oi / totalWeight
w_spot := w_spot / totalWeight
w_treasury := w_treasury / totalWeight

norm_momentum = f_normalize(feat_momentumComposite, i_lookbackML)
norm_meanRev = 1 - f_normalize(math.abs(feat_meanRev1), i_lookbackML)
norm_volume = f_normalize(feat_volumeRatio, i_lookbackML)
norm_crossMarket = crossMarketScore
norm_technical = (techScore + 1) / 2
norm_cycle = cyclePhase <= 2 ? 0.7 : 0.3
norm_oi = oiScore
norm_spot = (spotScore + 1) / 2
norm_treasury = treasuryScore

var float mlSignalRaw = 0.5

if i_ensembleMethod == "Weighted Average"
    mlSignalRaw := norm_momentum * w_momentum + norm_meanRev * w_meanRev + norm_volume * w_volume + norm_crossMarket * w_crossMarket + norm_technical * w_technical + norm_cycle * w_cycle + norm_oi * w_oi + norm_spot * w_spot + norm_treasury * w_treasury

else if i_ensembleMethod == "Voting"
    votes = 0
    votes += norm_momentum > 0.5 ? 1 : -1
    votes += norm_meanRev > 0.5 ? 1 : -1
    votes += norm_crossMarket > 0.5 ? 1 : -1
    votes += norm_technical > 0.5 ? 1 : -1
    votes += norm_oi > 0.5 ? 1 : -1
    votes += norm_spot > 0.5 ? 1 : -1
    mlSignalRaw := (votes + 6) / 12.0

else
    layer1_trend = (norm_momentum + norm_technical) / 2
    layer1_flow = (norm_oi + norm_spot + norm_volume) / 3
    layer1_macro = (norm_crossMarket + norm_cycle + norm_treasury) / 3
    mlSignalRaw := layer1_trend * 0.4 + layer1_flow * 0.35 + layer1_macro * 0.25

// 5.4 ★ KALMAN FILTER SMOOTHING
[mlSignalKalman, kalmanGain, kalmanError] = f_kalmanFilter(mlSignalRaw, i_kalmanMeasurementNoise, i_kalmanProcessNoise, i_kalmanAdaptive)

mlSignalSmooth = i_kalmanEnabled ? mlSignalKalman : ta.ema(mlSignalRaw, 5)
signalStrength = math.abs(mlSignalSmooth - 0.5) * 2

// 5.5 ★ INFORMATION COEFFICIENT TRACKING
ic1 = f_calculateIC(mlSignalSmooth, i_icHorizon1, i_icPeriod)
ic2 = f_calculateIC(mlSignalSmooth, i_icHorizon2, i_icPeriod)

icComposite = (nz(ic1, 0) + nz(ic2, 0)) / 2

var float icEMA = 0.0
icEMA := i_icDecayFactor * icEMA + (1 - i_icDecayFactor) * icComposite

icQuality = icEMA > 0.1 ? "EXCELLENT" : icEMA > i_icMinThreshold ? "GOOD" : icEMA > 0 ? "MARGINAL" : "POOR"

icFilterPassed = i_icEnabled ? icEMA >= i_icMinThreshold : true

// 5.6 ★ DYNAMIC THRESHOLDS
dynamicUpperThreshold = i_dynThreshEnabled ? f_dynamicThreshold(mlSignalSmooth, hvol, hvolSMA, i_baseSignalThreshold, i_dynThreshVolWeight, i_dynThreshQualityWeight, i_dynThreshMinThreshold, i_dynThreshMaxThreshold, i_lookbackML) : i_baseSignalThreshold

dynamicLowerThreshold = i_dynThreshEnabled ? f_dynamicThreshold(mlSignalSmooth, hvol, hvolSMA, 1 - i_baseSignalThreshold, -i_dynThreshVolWeight, i_dynThreshQualityWeight, 1 - i_dynThreshMaxThreshold, 1 - i_dynThreshMinThreshold, i_lookbackML) : 1 - i_baseSignalThreshold

// 5.7 CONFIDENCE CALCULATION
baseConfidence = signalStrength * (1 - returnStdDev / 10)
icBoost = icEMA > 0.1 ? 0.1 : (icEMA > 0.05 ? 0.05 : 0.0)
confidence = math.min(1.0, baseConfidence + icBoost)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 6: TRADE SIGNALS
// ══════════════════════════════════════════════════════════════════════════════

bullishSignal = mlSignalSmooth > dynamicUpperThreshold and confidence > 0.3 and not riskOffSignal and not weekendProtectionActive

bearishSignal = mlSignalSmooth < dynamicLowerThreshold and confidence > 0.3 and not weekendProtectionActive

allFiltersPass = liquidationFilter and icFilterPassed

bullConfirm = bullishSignal and allFiltersPass and (emaFast > emaSlow or regime == 4) and (cyclePhase <= 2 or not i_cycleEnabled)

bearConfirm = bearishSignal and allFiltersPass and (emaFast < emaSlow or regime == 3) and (cyclePhase >= 3 or not i_cycleEnabled)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 7: RISK MANAGEMENT & POSITION SIZING (SIMULATION)
// ══════════════════════════════════════════════════════════════════════════════

// --- PAPER TRADING SIMULATION ENGINE (NEW FOR INDICATOR) ---
// Initialize variables using the `var` keyword so they persist across bars
var float sim_equity = 100000.0  // Simulated Equity
var float sim_peak_equity = 100000.0 // Peak Equity tracking
var float sim_entry_price = 0.0 // Entry Price
var int sim_pos_state = 0       // 0=Flat, 1=Long, -1=Short
var int sim_wins = 0            // Win Counter
var int sim_losses = 0          // Loss Counter
var float sim_gross_profit = 0.0
var float sim_gross_loss = 0.0

// ATR Logic from original code
stopLossDistance = atrValue * i_atrMultiplierSL
takeProfitDistance = atrValue * i_atrMultiplierTP

// EXIT LOGIC FIRST (Check if previous trade closed on this bar)
// Logic: If in position, check against High/Low for TP/SL or Close for Signal Exit
bool sim_exit_long = false
bool sim_exit_short = false

if sim_pos_state == 1 // Currently Long
    // Check Stop Loss
    if low <= (sim_entry_price - stopLossDistance)
        sim_losses := sim_losses + 1
        loss_amt = math.abs(stopLossDistance) * 10 
        sim_gross_loss := sim_gross_loss + loss_amt
        sim_equity := sim_equity - loss_amt
        sim_pos_state := 0
        sim_exit_long := true
    // Check Take Profit
    else if high >= (sim_entry_price + takeProfitDistance)
        sim_wins := sim_wins + 1
        win_amt = takeProfitDistance * 10
        sim_gross_profit := sim_gross_profit + win_amt
        sim_equity := sim_equity + win_amt
        sim_pos_state := 0
        sim_exit_long := true
    // Check Signal Exit (bearishSignal or RiskOff)
    else if bearishSignal or riskOffSignal or liquidationWarning
        pnl = close - sim_entry_price
        if pnl > 0
            sim_wins := sim_wins + 1
            sim_gross_profit := sim_gross_profit + (pnl * 10)
            sim_equity := sim_equity + (pnl * 10)
        else
            sim_losses := sim_losses + 1
            sim_gross_loss := sim_gross_loss + (math.abs(pnl) * 10)
            sim_equity := sim_equity - (math.abs(pnl) * 10)
        sim_pos_state := 0
        sim_exit_long := true

if sim_pos_state == -1 // Currently Short
    // Check Stop Loss
    if high >= (sim_entry_price + stopLossDistance)
        sim_losses := sim_losses + 1
        loss_amt = math.abs(stopLossDistance) * 10
        sim_gross_loss := sim_gross_loss + loss_amt
        sim_equity := sim_equity - loss_amt
        sim_pos_state := 0
        sim_exit_short := true
    // Check Take Profit
    else if low <= (sim_entry_price - takeProfitDistance)
        sim_wins := sim_wins + 1
        win_amt = takeProfitDistance * 10
        sim_gross_profit := sim_gross_profit + win_amt
        sim_equity := sim_equity + win_amt
        sim_pos_state := 0
        sim_exit_short := true
    // Check Signal Exit
    else if bullishSignal or liquidationWarning
        pnl = sim_entry_price - close
        if pnl > 0
            sim_wins := sim_wins + 1
            sim_gross_profit := sim_gross_profit + (pnl * 10)
            sim_equity := sim_equity + (pnl * 10)
        else
            sim_losses := sim_losses + 1
            sim_gross_loss := sim_gross_loss + (math.abs(pnl) * 10)
            sim_equity := sim_equity - (math.abs(pnl) * 10)
        sim_pos_state := 0
        sim_exit_short := true

// UPDATE DRAWDOWN STATS
if sim_equity > sim_peak_equity
    sim_peak_equity := sim_equity

currentDrawdown = sim_peak_equity == 0 ? 0.0 : (sim_peak_equity - sim_equity) / sim_peak_equity * 100
maxDrawdownBreached = currentDrawdown >= i_maxDrawdown

// CALCULATE POSITION SIZING (Using simulated stats)
// Win Rate for Kelly
total_trades = sim_wins + sim_losses
sim_winrate = total_trades > 0 ? sim_wins / total_trades : 0.0
// Profit Factor
sim_pf = sim_gross_loss > 0 ? sim_gross_profit / sim_gross_loss : 0.0
// Avg Win/Loss for Kelly
sim_avg_win = sim_wins > 0 ? sim_gross_profit / sim_wins : 0.0
sim_avg_loss = sim_losses > 0 ? sim_gross_loss / sim_losses : 1.0 // Avoid div by zero
payoffRatio = sim_avg_win / math.max(sim_avg_loss, 1.0)

kellyPercent = sim_winrate - ((1 - sim_winrate) / payoffRatio)
kellyPosition = math.max(0, math.min(kellyPercent * i_kellyFraction * 100, i_maxPositionSize))

// Base Position Calculation logic restored from Strategy
basePosition = i_riskEnabled ? kellyPosition * (hvol == 0 ? 1.0 : math.max(0.5, math.min(2.0, hvolSMA / hvol))) : 10.0
cvarAdjustedPosition = i_cvarEnabled ? math.min(basePosition, cvarPositionSize) : basePosition
adjustedPosition = cvarAdjustedPosition * cyclePositionMult * weekendPositionMult

icPositionMult = icEMA > 0.1 ? 1.2 : (icEMA > 0.05 ? 1.0 : 0.8)
adjustedPosition := adjustedPosition * icPositionMult

finalPosition = math.min(adjustedPosition, i_maxPositionSize)

// ENTRY LOGIC (Check for new trades if flat)
bool sim_new_long = false
bool sim_new_short = false

// Use "bullConfirm" and "bearConfirm" from Section 6
// IMPORTANT: Only enter if currently flat (sim_pos_state == 0)
if sim_pos_state == 0
    if bullConfirm and not maxDrawdownBreached
        sim_pos_state := 1
        sim_entry_price := close
        sim_new_long := true
    
    if bearConfirm and not maxDrawdownBreached
        sim_pos_state := -1
        sim_entry_price := close
        sim_new_short := true

// Map variables to old names for compatibility with plotting code below
longCondition = sim_new_long
shortCondition = sim_new_short
exitLongCondition = sim_exit_long
exitShortCondition = sim_exit_short

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 9: VISUALIZATION
// ══════════════════════════════════════════════════════════════════════════════

plot(emaFast, "EMA Fast", color=color.new(color.blue, 0), linewidth=1)
plot(emaSlow, "EMA Slow", color=color.new(color.orange, 0), linewidth=1)
plot(ema200, "EMA 200", color=color.new(color.purple, 0), linewidth=2)
plot(vwapValue, "VWAP", color=color.new(color.yellow, 50), linewidth=1, style=plot.style_circles)

p_bbUpper = plot(bbUpper, "BB Upper", color=color.new(color.gray, 70))
p_bbLower = plot(bbLower, "BB Lower", color=color.new(color.gray, 70))
fill(p_bbUpper, p_bbLower, color=color.new(color.blue, 95))

plotshape(i_showSignals and longCondition, "Long Signal", shape.triangleup, location.belowbar, color.new(color.green, 0), size=size.normal)
plotshape(i_showSignals and shortCondition, "Short Signal", shape.triangledown, location.abovebar, color.new(color.red, 0), size=size.normal)
plotshape(i_showSignals and exitLongCondition, "Exit Long", shape.xcross, location.abovebar, color.new(color.orange, 0), size=size.small)
plotshape(i_showSignals and exitShortCondition, "Exit Short", shape.xcross, location.belowbar, color.new(color.orange, 0), size=size.small)

plotshape(oiFlipBullish, "OI Flip Bullish", shape.circle, location.belowbar, color.new(color.lime, 30), size=size.tiny)
plotshape(oiFlipBearish, "OI Flip Bearish", shape.circle, location.abovebar, color.new(color.fuchsia, 30), size=size.tiny)

plotshape(i_showNewFeatures and liquidationWarning, "Liquidation Warning", shape.labeldown, location.abovebar, color.new(color.red, 0), size=size.tiny, text="LIQ", textcolor=color.white)

regimeColor = regime == 1 ? color.new(color.green, 90) :
              regime == 2 ? color.new(color.red, 90) :
              regime == 3 ? color.new(color.orange, 90) :
              regime == 4 ? color.new(color.teal, 90) :
              color.new(color.gray, 95)

bgcolor(i_showRegime ? regimeColor : na, title="Regime Background")
bgcolor(weekendProtectionActive ? color.new(color.yellow, 85) : na, title="Weekend Warning")
bgcolor(liquidationWarning ? color.new(color.red, 90) : na, title="Liquidation Warning BG")

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 10: ANALYTICS DASHBOARD
// ══════════════════════════════════════════════════════════════════════════════

// [FIX] Define dashboard position logic outside table.new to prevent syntax errors
dashPos = i_dashboardPosition == "Top Right" ? position.top_right : i_dashboardPosition == "Top Left" ? position.top_left : i_dashboardPosition == "Bottom Right" ? position.bottom_right : position.bottom_left

var table dashboard = table.new(dashPos, 4, 24, bgcolor=color.new(color.black, 20), border_width=1, border_color=color.gray)

if i_showDashboard and barstate.islast
    table.cell(dashboard, 0, 0, "BTC Strategy", text_color=color.white, text_size=size.normal, bgcolor=color.new(color.blue, 30), text_halign=text.align_center)
    table.merge_cells(dashboard, 0, 0, 3, 0)
    
    table.cell(dashboard, 0, 1, "═══ ★ FEATURES ═══", text_color=color.lime, text_size=size.small, bgcolor=color.new(color.green, 70))
    table.merge_cells(dashboard, 0, 1, 3, 1)
    
    kalmanStatus = i_kalmanEnabled ? "ON" : "OFF"
    table.cell(dashboard, 0, 2, "Kalman Filter:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 2, kalmanStatus, text_color=i_kalmanEnabled ? color.green : color.gray, text_size=size.tiny)
    table.cell(dashboard, 2, 2, "Gain:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 2, str.tostring(kalmanGain, "#.###"), text_color=color.aqua, text_size=size.tiny)
    
    table.cell(dashboard, 0, 3, "Dyn Thresh:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 3, str.tostring(dynamicUpperThreshold, "#.##") + "/" + str.tostring(dynamicLowerThreshold, "#.##"), text_color=color.yellow, text_size=size.tiny)
    table.cell(dashboard, 2, 3, "Base:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 3, str.tostring(i_baseSignalThreshold, "#.##"), text_color=color.gray, text_size=size.tiny)
    
    icColor = icEMA > 0.1 ? color.green : icEMA > 0.05 ? color.yellow : icEMA > 0 ? color.orange : color.red
    table.cell(dashboard, 0, 4, "IC Score:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 4, str.tostring(icEMA, "#.###"), text_color=icColor, text_size=size.tiny)
    table.cell(dashboard, 2, 4, "Quality:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 4, icQuality, text_color=icColor, text_size=size.tiny)
    
    table.cell(dashboard, 0, 5, "CVaR (95%):", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 5, str.tostring(cvarLevel * 100, "#.##") + "%", text_color=cvarLevel < -0.02 ? color.red : color.green, text_size=size.tiny)
    table.cell(dashboard, 2, 5, "CVaR Pos:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 5, str.tostring(cvarPositionSize, "#.#") + "%", text_color=color.aqua, text_size=size.tiny)
    
    liqColor = liquidationWarning ? color.red : color.green
    table.cell(dashboard, 0, 6, "Liq Cascade:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 6, str.tostring(cascadeScore, "#.##"), text_color=liqColor, text_size=size.tiny)
    table.cell(dashboard, 2, 6, "Status:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 6, liquidationWarning ? "WARNING" : "CLEAR", text_color=liqColor, text_size=size.tiny)
    
    table.cell(dashboard, 0, 7, "═══ ML SIGNAL ENGINE ═══", text_color=color.aqua, text_size=size.small, bgcolor=color.new(color.gray, 80))
    table.merge_cells(dashboard, 0, 7, 3, 7)
    
    signalColor = mlSignalSmooth > dynamicUpperThreshold ? color.green : mlSignalSmooth < dynamicLowerThreshold ? color.red : color.yellow
    table.cell(dashboard, 0, 8, "ML Signal:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 8, str.tostring(mlSignalSmooth, "#.###"), text_color=signalColor, text_size=size.small)
    table.cell(dashboard, 2, 8, "Confidence:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 3, 8, str.tostring(confidence * 100, "#.#") + "%", text_color=color.white, text_size=size.small)
    
    table.cell(dashboard, 0, 9, "Momentum:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 1, 9, str.tostring(norm_momentum, "#.##"), text_color=norm_momentum > 0.5 ? color.green : color.red, text_size=size.tiny)
    table.cell(dashboard, 2, 9, "Technical:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 3, 9, str.tostring(norm_technical, "#.##"), text_color=norm_technical > 0.5 ? color.green : color.red, text_size=size.tiny)
    
    table.cell(dashboard, 0, 10, "OI Score:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 1, 10, str.tostring(norm_oi, "#.##"), text_color=norm_oi > 0.5 ? color.green : color.red, text_size=size.tiny)
    table.cell(dashboard, 2, 10, "Spot Flow:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 3, 10, str.tostring(norm_spot, "#.##"), text_color=norm_spot > 0.5 ? color.green : color.red, text_size=size.tiny)
    
    table.cell(dashboard, 0, 11, "═══ MARKET CONTEXT ═══", text_color=color.aqua, text_size=size.small, bgcolor=color.new(color.gray, 80))
    table.merge_cells(dashboard, 0, 11, 3, 11)
    
    table.cell(dashboard, 0, 12, "Regime:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 12, regimeStr, text_color=regime <= 2 ? (regime == 1 ? color.green : color.red) : color.yellow, text_size=size.small)
    table.cell(dashboard, 2, 12, "ADX:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 3, 12, str.tostring(adxValue, "#.#"), text_color=adxValue > 25 ? color.green : color.gray, text_size=size.small)
    
    table.cell(dashboard, 0, 13, "Cycle:", text_color=color.white, text_size=size.small)
    cycleColor = cyclePhase == 1 ? color.teal : cyclePhase == 2 ? color.green : cyclePhase == 3 ? color.orange : color.red
    table.cell(dashboard, 1, 13, cyclePhaseStr, text_color=cycleColor, text_size=size.small)
    table.cell(dashboard, 2, 13, "Day:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 3, 13, str.tostring(cycleDay), text_color=color.white, text_size=size.small)
    
    table.cell(dashboard, 0, 14, "SPX Corr:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 1, 14, str.tostring(nz(corrSPX, 0), "#.##"), text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 2, 14, "VIX:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 3, 14, str.tostring(vixLevel, "#.#"), text_color=vixLevel > 25 ? color.red : color.green, text_size=size.tiny)
    
    table.cell(dashboard, 0, 15, "Breadth:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 1, 15, str.tostring(marketBreadth) + "/3", text_color=marketBreadth >= 2 ? color.green : color.red, text_size=size.tiny)
    table.cell(dashboard, 2, 15, "Risk-Off:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 3, 15, riskOffSignal ? "YES" : "NO", text_color=riskOffSignal ? color.red : color.green, text_size=size.tiny)
    
    table.cell(dashboard, 0, 16, "═══ RISK MANAGEMENT ═══", text_color=color.aqua, text_size=size.small, bgcolor=color.new(color.gray, 80))
    table.merge_cells(dashboard, 0, 16, 3, 16)
    
    table.cell(dashboard, 0, 17, "Position:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 17, str.tostring(finalPosition, "#.#") + "%", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 2, 17, "Drawdown:", text_color=color.white, text_size=size.small)
    ddColor = currentDrawdown > 10 ? color.red : currentDrawdown > 5 ? color.orange : color.green
    table.cell(dashboard, 3, 17, str.tostring(currentDrawdown, "#.#") + "%", text_color=ddColor, text_size=size.small)
    
    table.cell(dashboard, 0, 18, "Kelly %:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 1, 18, str.tostring(kellyPosition, "#.#") + "%", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 2, 18, "ATR:", text_color=color.silver, text_size=size.tiny)
    table.cell(dashboard, 3, 18, str.tostring(atrValue, "#.##"), text_color=color.white, text_size=size.tiny)
    
    table.cell(dashboard, 0, 19, "═══ FILTER STATUS ═══", text_color=color.aqua, text_size=size.small, bgcolor=color.new(color.gray, 80))
    table.merge_cells(dashboard, 0, 19, 3, 19)
    
    table.cell(dashboard, 0, 20, "IC Filter:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 20, icFilterPassed ? "PASS" : "BLOCK", text_color=icFilterPassed ? color.green : color.red, text_size=size.tiny)
    table.cell(dashboard, 2, 20, "Liq Filter:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 20, liquidationFilter ? "PASS" : "BLOCK", text_color=liquidationFilter ? color.green : color.red, text_size=size.tiny)
    
    table.cell(dashboard, 0, 21, "Weekend:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 1, 21, weekendProtectionActive ? "ACTIVE" : "OFF", text_color=weekendProtectionActive ? color.yellow : color.green, text_size=size.tiny)
    table.cell(dashboard, 2, 21, "All Pass:", text_color=color.white, text_size=size.tiny)
    table.cell(dashboard, 3, 21, allFiltersPass ? "YES" : "NO", text_color=allFiltersPass ? color.green : color.red, text_size=size.tiny)
    
    table.cell(dashboard, 0, 22, "═══ PERFORMANCE ═══", text_color=color.aqua, text_size=size.small, bgcolor=color.new(color.gray, 80))
    table.merge_cells(dashboard, 0, 22, 3, 22)
    
    table.cell(dashboard, 0, 23, "Win Rate:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 1, 23, str.tostring(sim_winrate * 100, "#.#") + "%", text_color=sim_winrate > 0.5 ? color.green : color.red, text_size=size.small)
    table.cell(dashboard, 2, 23, "Profit Factor:", text_color=color.white, text_size=size.small)
    table.cell(dashboard, 3, 23, str.tostring(sim_pf, "#.##"), text_color=sim_pf > 1 ? color.green : color.red, text_size=size.small)

// ══════════════════════════════════════════════════════════════════════════════
// SECTION 11: ALERTS
// ══════════════════════════════════════════════════════════════════════════════

alertcondition(longCondition, "Long Entry Signal", "BTC Strategy: LONG Entry")
alertcondition(shortCondition, "Short Entry Signal", "BTC Strategy: SHORT Entry")
alertcondition(exitLongCondition, "Exit Long Signal", "BTC Strategy: EXIT LONG")
alertcondition(exitShortCondition, "Exit Short Signal", "BTC Strategy: EXIT SHORT")
alertcondition(oiFlipBullish, "OI Flip Bullish", "BTC: OI Flip BULLISH")
alertcondition(oiFlipBearish, "OI Flip Bearish", "BTC: OI Flip BEARISH")
alertcondition(weekendProtectionActive and not weekendProtectionActive[1], "Weekend Protection", "BTC: Weekend Protection ACTIVATED")
alertcondition(maxDrawdownBreached, "Max Drawdown", "BTC ALERT: Max Drawdown Breached")
alertcondition(riskOffSignal and not riskOffSignal[1], "Risk-Off Signal", "BTC: Risk-Off Detected")
alertcondition(liquidationWarning and not liquidationWarning[1], "Liquidation Cascade", "BTC Strategy: Liquidation Cascade Detected!")
alertcondition(icEMA < i_icMinThreshold and icEMA[1] >= i_icMinThreshold, "IC Degraded", "BTC Strategy: IC Degraded - Signal Quality Poor")

// ══════════════════════════════════════════════════════════════════════════════
// END OF INDICATOR
// ══════════════════════════════════════════════════════════════════════════════

Overview

These include weekend liquidity rules that reduce suggested position size, a four-year halving cycle phase classifier, correlation and risk-off checks against the S&P 500, DAX, Hang Seng, VIX and Dollar Index, an open interest proxy from a perpetual futures symbol, and volume delta and cumulative delta flow. Settings control weekend hours, cycle phase lengths, position multipliers and lookback periods. The README describes a simulated trade log on a dashboard, which is hypothetical.

Details

Published by

grzresearch on GitHub

Draws

On the price chart

Markets

Crypto

Timeframes

Any timeframe

Risk Notes

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

Settings

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

SettingTypeDefaultVariable
Enable Weekend Protectionbooltruei_weekendProtection
Position Reduction %float50.0i_weekendReducePct
Friday Exit Hour (UTC)int20i_fridayExitHour
Sunday Re-Entry Hour (UTC)int22i_sundayEntryHour
Weekend Vol Multiplier Thresholdfloat0.5i_weekendVolThreshold
Enable Cycle Analysisbooltruei_cycleEnabled
Last Halving Datetimetimestamp("2024-04-20")i_halvingDate2024
Accumulation Phase (Days Post-Halving)int180i_cycleAccumulationEnd
Bull Phase End (Days Post-Halving)int540i_cycleBullEnd
Distribution Phase End (Days)int900i_cycleDistributionEnd
Cycle Bull Phase Position Multiplierfloat1.5i_cycleBias
Cycle Bear Phase Position Multiplierfloat0.5i_cycleBearBias
Show all 40 settings
SettingTypeDefaultVariable
Enable Cross-Market Analysisbooltruei_crossMarketEnabled
S&P 500 SymbolsymbolOANDA:SPX500USDi_spxSymbol
DAX SymbolsymbolTVC:DEU40i_daxSymbol
Hang Seng SymbolsymbolTVC:HSIi_hsiSymbol
Dollar IndexsymbolTVC:DXYi_dxySymbol
VIX SymbolsymbolTVC:VIXi_vixSymbol
Correlation Periodint50i_corrPeriod
Correlation Thresholdfloat0.6i_corrThreshold
VIX Risk-Off Thresholdfloat25.0i_riskOffVIX
DXY Momentum Risk-Offfloat2.0i_riskOffDXY
Enable OI Analysisbooltruei_oiEnabled
Perp Symbol for OI ProxysymbolBINANCE:BTCUSDTPERPi_oiSymbol
OI Momentum Periodint20i_oiPeriod
OI Divergence Periodint14i_oiDivergencePeriod
OI Flip Sensitivity (StdDev)float1.5i_oiFlipSensitivity
Enable Spot Flow Analysisbooltruei_spotEnabled
Volume Delta Periodint20i_volumeDeltaPeriod
CVD Smoothing Periodint50i_cvdPeriod
Large Order Threshold (StdDev)float2.0i_largeOrderThreshold
Absorption Detection Periodint10i_absorptionPeriod
Enable Treasury Analysisbooltruei_treasuryEnabled
MicroStrategy SymbolsymbolNASDAQ:MSTRi_mstrSymbol
Coinbase SymbolsymbolNASDAQ:COINi_coinSymbol
GBTC SymbolsymbolAMEX:GBTCi_grayscaleSymbol
MSTR Correlation Periodint30i_mstrCorrPeriod
MSTR Lead/Lag Periodint5i_mstrLeadPeriod
Enable ML Signal Enginebooltruei_mlEnabled
Ensemble MethodstringWeighted Averagei_ensembleMethod
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.

Similar Pine Scripts

This overlay indicator screens a pasted list of up to forty symbols and draws a table on the price chart. It classifies bullish or bearish setups from the percent change between the current candle and the candle 250 bars back, then shows RSI with a simple movi

Indicator v5 Oscillators & momentumVolume
Updated 1 year ago · MPL-2.0 GitHub repository of Momentum Setup - Ankush Bajaj Momentum Investing Setup

Daily Volume

by benso87
24 stars

Daily Volume is an overlay indicator that displays current daily volume statistics inside an on-chart table in the top-right corner. It fetches higher-timeframe daily data to report real-time current daily volume, yesterday's total volume, and a simple moving

Indicator v5 VolumeScreeners & dashboards
Updated 4 years ago · MPL-2.0 GitHub repository of Daily Volume

Equity Intraday Analyst

by chris-c-thomas
14 stars

Equity Intraday Analyst is a comprehensive intraday overlay indicator for US equities and ETFs. It evaluates multi-EMA ribbons, session-anchored VWAP bands, OBV trends, momentum oscillators, TTM Squeeze, and optional market breadth indicators like NYSE TICK an

Indicator v6 TrendVolume
Updated 6 months ago · MIT GitHub repository of Equity Intraday Analyst

This daily-timeframe indicator provides futures market context by evaluating open interest and term structure without altering chart price scales. It requests front-month and next-month futures contracts to measure spread relationships like contango or backwar

Indicator v6 VolumeScreeners & dashboards
Updated 2 days ago · MIT GitHub repository of Futures Context Module [WavesUnchained]

Nexural QWAP

by JasonTeixeira
9 stars

Nexural QWAP calculates a volume-weighted average price combined with a cumulative volume delta order flow engine directly on the main price chart. It analyzes market structure by detecting volume absorption, liquidity sweeps, price-CVD divergence, stacked imb

Indicator v6 SMC / ICT / price actionVolume
Updated 13 days ago · Custom licence GitHub repository of Nexural QWAP

More Screeners & dashboards scripts

Related on ForexR

Trade Your Charts With a Regulated Broker

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

Compare brokers