// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © julzen2

//@version=5
indicator(title="RD.COmbo", shorttitle="RD-Combo", overlay=false,
max_bars_back=2000, // Sufficient history for calculations, especially ForecastOsc
precision=2         // Adjust precision for plots if needed
)

// --- Input Parameters ---
bool DoAlertForEntry = input.bool(false, "Do Alert For Entry")
bool DoAlertForExit = input.bool(false, "Do Alert For Exit")
// HistorySize in MQL4 primarily limits the `start` loop iteration.
// In Pine Script, plots automatically handle historical data.
// We keep it as an input but it doesn't directly restrict plots.
int HistorySize_input = input.int(1000, "History Size", minval=100)
int ColorThreshold = input.int(5, "Color Threshold", minval=0, maxval=5)
float NoiseFilterRVI = input.float(0.03, "RVI Exit Threshold", minval=0.0, step=0.01) // MQL4 uses 0.2 in condition, but input is 0.03. Using input.
bool DebugLogger = input.bool(false, "Debug Logger (Console)")
bool DebugLoggerData = input.bool(false, "Debug Logger Data (Console)")

// --- Global variables for Signal State & Alerts ---
// `var` keyword ensures these variables retain their values across bars.
var int signalstate = 0 // 1 = Long, -1 = Short, 0 = Neutral
var bool didentryalert_pine = false
var bool didexitalert_pine = false

// --- ForecastOscillator Calculation Helper Functions ---

// Helper function to calculate the weighted sum for `wt`
f_calc_wt(src, len) =>
    float sum_val = 0.0
    // Iterate from the current bar's position `len - i` to access past `src` values.
    // MQL4 `Close[shift+length-i]` corresponds to `src[len - i]` for current bar `shift=0`.
    for i = 1 to len
        float tmp_coeff = i - (float(len) + 1.0) / 3.0
        sum_val += tmp_coeff * src[len - i]
    sum_val * 6.0 / (float(len) * (float(len) + 1.0))

// ForecastOscillator logic
f_forecast_osc_series(src_series, regress, t3, b) =>
    float b2 = b * b
    float b3 = b2 * b
    float c1 = -b3
    float c2 = (3 * (b2 + b3))
    float c3 = -3 * (2 * b2 + b + b3)
    float c4 = (1 + 3 * b + b3 + 3 * b2)
    float n = 1 + 0.5 * (float(t3) - 1)
    float w1 = 2 / (n + 1)
    float w2 = 1 - w1

    // Calculate wt for the current bar
    float wt = f_calc_wt(src_series, regress)

    // Ensure `wt` is not too close to zero to avoid division by zero
    float forecastosc = (src_series - wt) / (math.abs(wt) < 1e-10 ? 1e-10 : wt) * 100

    // T3 smoothing chain - these variables must persist across bars
    var float e1 = na
    var float e2 = na
    var float e3 = na
    var float e4 = na
    var float e5 = na
    var float e6 = na
   
    // Using nz() to handle initial `na` values
    e1 := w1 * forecastosc + w2 * nz(e1[1])
    e2 := w1 * e1 + w2 * nz(e2[1])
    e3 := w1 * e2 + w2 * nz(e3[1])
    e4 := w1 * e3 + w2 * nz(e4[1])
    e5 := w1 * e4 + w2 * nz(e5[1])
    e6 := w1 * e5 + w2 * nz(e6[1])
   
    float t3_fosc = c1 * e6 + c2 * e5 + c3 * e4 + c4 * e3
   
    [forecastosc, t3_fosc] // Return both series

// --- Pre-calculate Forecast Oscillator values ---
// Call the function on the 'close' series with MQL4's default parameters (15, 3, 0.7)
[Osc, Osct3] = f_forecast_osc_series(close, 15, 3, 0.7)


// --- Combo Indicator Logic (Translated from MQL4's Combo function) ---

// Calculate all necessary indicator values for the current bar.
// MQL4's `lookupidx` corresponds to `[0]` for the current bar in Pine.
float ma5 = ta.wma(close, 5)
float ma20 = ta.wma(close, 20)
float ma100 = ta.wma(close, 100)
float ma200 = ta.wma(close, 200)

float cci = ta.cci(close, 5)

// --- MANUAL RVI CALCULATION (REPLACEMENT FOR ta.rvi) ---
// RVI Main (period 1 from MQL4) = (Close - Open) / (High - Low)
float rvi_numerator_raw = close - open
float rvi_denominator_raw = high - low
float rvimain = rvi_denominator_raw != 0 ? rvi_numerator_raw / rvi_denominator_raw : 0.0

// RVI Signal (period 1 from MQL4 implies a 4-period SMA of the main RVI)
float rvisignal = ta.sma(rvimain, 4)
// --- END MANUAL RVI CALCULATION ---


// --- MANUAL ADX/DI CALCULATION (REPLACEMENT FOR ta.adx, ta.plus_di, ta.minus_di) ---
f_calculate_adx_di(len) =>
    // True Range
    tr1 = high - low
    tr2 = math.abs(high - close[1])
    tr3 = math.abs(low - close[1])
    tr = math.max(tr1, tr2, tr3)

    // Directional Movement
    up = high - high[1]
    down = low[1] - low

    plusDM = up > down and up > 0 ? up : 0
    minusDM = down > up and down > 0 ? down : 0

    // Smoothed True Range and Directional Movements using RMA (Wilders Smoothing)
    // ta.rma is the Pine Script equivalent of Wilders Smoothing
    smoothTR = ta.rma(tr, len)
    smoothPlusDM = ta.rma(plusDM, len)
    smoothMinusDM = ta.rma(minusDM, len)

    // Calculate DI+ and DI-
    plusDI = smoothTR != 0 ? smoothPlusDM / smoothTR * 100 : 0
    minusDI = smoothTR != 0 ? smoothMinusDM / smoothTR * 100 : 0

    // Calculate DX
    sumDI = plusDI + minusDI
    dx = sumDI != 0 ? math.abs(plusDI - minusDI) / sumDI * 100 : 0

    // Calculate ADX (smoothed DX)
    adx = ta.rma(dx, len)
   
    [adx, plusDI, minusDI]

// Apply the manual ADX/DI calculation for period 14
[adxmain, adxplus, adxminus] = f_calculate_adx_di(14)

// For previous bar values, we can simply use the `[1]` operator on the calculated series
float adxmain2 = adxmain[1]
float adxplus2 = adxplus[1]
float adxminus2 = adxminus[1]
// --- END MANUAL ADX/DI CALCULATION ---

// Forecast Oscillator values for current bar (already calculated as series `Osc` and `Osct3`)
float fcblue = Osc
float fcred = Osct3

// Initialize signal components
int maval = 0
int ccival = 0
int rvival = 0
int adxval = 0
int fcval = 0

// MA signal
if ma5 > ma20
    maval := 1
else if ma5 < ma20
    maval := -1

// CCI signal
if cci > 0
    ccival := 1
else if cci < 0
    ccival := -1
// Else ccival remains 0

// Forecast Oscillator signal
if fcblue > 0 and fcred > 0 and fcblue > fcred
    fcval := 1
else if fcblue < 0 and fcred < 0 and fcblue < fcred
    fcval := -1
// Else fcval remains 0

// RVI signal
if rvimain > 0 and rvisignal > 0 and rvimain - rvisignal > 0
    rvival := 1
else if rvimain < 0 and rvisignal < 0 and rvimain - rvisignal < 0
    rvival := -1
// Else rvival remains 0

// ADX signal
if adxmain > adxmain2 and adxplus > adxplus2 and adxmain > 20 and adxplus > 20
    adxval := 1
else if adxmain > adxmain2 and adxminus > adxminus2 and adxmain > 20 and adxminus > 20
    adxval := -1
// Else adxval remains 0

// Calculate the total combined signal strength
float val = maval + ccival + fcval + rvival + adxval

// --- Exit Signal Logic ---
// Note: MQL4 used a constant `0.2` for RVI exit threshold, but `NoiseFilterRVI` input was 0.03.
// We are using the `NoiseFilterRVI` input for consistency.
if signalstate != 0
    if signalstate == 1 and (rvimain < 0 or (rvisignal - rvimain) > NoiseFilterRVI)
        signalstate := 0 // Exit long
    if signalstate == -1 and (rvimain > 0 or (rvimain - rvisignal) > NoiseFilterRVI)
        signalstate := 0 // Exit short

// --- Debugging Output (to Pine Script console) ---
if DebugLogger or DebugLoggerData
    // Check for sufficient historical bars before logging to avoid errors with `[1]` etc.
    if bar_index >= math.max(14, 200) // At least 200 bars for MAs, 14 for ADX. Min 31+regress=46 for FO.
        if DebugLoggerData
            log.info("--------------------------------------------------------------------------")
            log.info("Time: {0}, Bar: {1}, MA 5/20/100/200: {2}/{3}/{4}/{5}",
                 timenow, bar_index, ma5, ma20, ma100, ma200)
            log.info("Time: {0}, Bar: {1}, CCI: {2}, RVI: {3}/{4}",
                 timenow, bar_index, cci, rvimain, rvisignal)
            log.info("Time: {0}, Bar: {1}, ADX(now): {2}/{3}/{4}",
                 timenow, bar_index, adxmain, adxplus, adxminus)
            log.info("Time: {0}, Bar: {1}, ADX(prev): {2}/{3}/{4}",
                 timenow, bar_index, adxmain2, adxplus2, adxminus2)
            log.info("Time: {0}, Bar: {1}, Forecast blue/red: {2}/{3}",
                 timenow, bar_index, fcblue, fcred)

        log.info("Time: {0}, Bar: {1}, MAtrend({2}) + CCI({3}) + FC({4}) + RVItrend({5}) + ADXtrend({6}) => {7} # trade state = {8}",
             timenow, bar_index, maval, ccival, fcval, rvival, adxval, val, signalstate)


// --- Determine Plot Values ---
// These are the buffers from MQL4 (Neutral, Short, Long, Signal)
float NeutralBuffer_val = 0.0
float LongSignalBuffer_val = 0.0
float ShortSignalBuffer_val = 0.0

if val >= ColorThreshold
    LongSignalBuffer_val := val
    signalstate := 1 // Set signalstate to long if conditions met
else if val <= -ColorThreshold
    ShortSignalBuffer_val := val
    signalstate := -1 // Set signalstate to short if conditions met
else
    NeutralBuffer_val := val // Neutral, between thresholds
    // If we're neutral, but previously in a signal, it might be an exit condition.
    // The `signalstate` is managed by the exit logic above.
    // So if the current `val` is neutral, but `signalstate` was set by an exit condition, it remains 0.
    // If `val` is neutral and there was no exit, `signalstate` also becomes 0.
    if math.abs(val) < ColorThreshold and signalstate != 0
        signalstate := 0 // If signal goes neutral and no specific RVI exit occurred, also reset.


// SignalBuffer: MQL4 uses `2 * signalstate`
float SignalBuffer_val = 2 * signalstate

// --- Plotting ---
// Removed minval and maxval as they are not recognized in your environment.
// The indicator will now auto-scale based on the values plotted.
plot(NeutralBuffer_val, title="Neutral", color=color.rgb(128, 128, 128), style=plot.style_columns, linewidth=2, histbase=0) // Gray
plot(ShortSignalBuffer_val, title="Short Signal", color=color.rgb(255, 69, 0), style=plot.style_columns, linewidth=2, histbase=0) // OrangeRed
plot(LongSignalBuffer_val, title="Long Signal", color=color.rgb(124, 252, 0), style=plot.style_columns, linewidth=2, histbase=0) // LawnGreen

plot(SignalBuffer_val, title="Signal Line", color=color.rgb(255, 215, 0), style=plot.style_line, linewidth=1) // Gold

// Plotting MQL4 levels (4 and -4) - these will still help visually define a range.
hline(4, "Level +4", color.rgb(100, 100, 100), linestyle=hline.style_dashed)
hline(-4, "Level -4", color.rgb(100, 100, 100), linestyle=hline.style_dashed)


// --- Alerting Logic ---
// MQL4: `SignalBuffer[0]!=0 && SignalBuffer[1]==0` for entry
// Pine: `SignalBuffer_val != 0 and SignalBuffer_val[1] == 0` for entry
if DoAlertForEntry and SignalBuffer_val != 0 and nz(SignalBuffer_val[1]) == 0
    if not didentryalert_pine
        alert("RD signals trade entry on " + syminfo.ticker + "/" + timeframe.period, alert.freq_once_per_bar)
        didentryalert_pine := true
else
    didentryalert_pine := false // Reset alert flag when no entry signal

// MQL4: `SignalBuffer[0]==0 && SignalBuffer[1]!=0` for exit
// Pine: `SignalBuffer_val == 0 and SignalBuffer_val[1] != 0` for exit
if DoAlertForExit and SignalBuffer_val == 0 and nz(SignalBuffer_val[1]) != 0
    if not didexitalert_pine
        alert("RD signals trade exit on " + syminfo.ticker + "/" + timeframe.period, alert.freq_once_per_bar)
        didexitalert_pine := true
else
    didexitalert_pine := false // Reset alert flag when no exit signal
    
    
