[RedK] Stepped Moving Average Channel (SMAC)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © RedKTrader

//@version=4
study(title="Stepped Moving Average Channel", shorttitle="SMAC v2.0", overlay = true) //, resolution = "", resolution_gaps = false)
// ===================================================================================================================
//                              Functions Section
// ===================================================================================================================
// step function
// this function picks a step size based on the price range
// it wil then scale the step up/down based on TF changes
// change and personalize these ranges as needed
f_step(x) =>
    initstep =
      x <     1 ? 0.05  : x <     5 ? 0.20  :
      x <    20 ? 0.50  : x <   100 ? 1.00  :
      x <   300 ? 2.00  : x <   500 ? 5.00  :
      x <  1000 ? 10.0  : x <  5000 ? 20.0  :
      x < 10000 ? 50.0  : x < 20000 ? 100.  :
      200.0
    
    //Adjust step value up or down for different timeframes
    adjstep = 
      timeframe.isweekly    ? initstep * 2 :   
      timeframe.ismonthly   ? initstep * 5 :
      timeframe.isintraday and timeframe.multiplier > 60    ? initstep / 2 :
      timeframe.isintraday and timeframe.multiplier <= 60   ? initstep / 5 :
      initstep
    
    // need to replace the 4's in weekly and minutes with 5's
    // cause we want to track the mental increments of traders - round values are more effective 
    _incstr = tostring(adjstep)
    _newstr = str.replace_all(_incstr,"4","5")
    tonumber(_newstr)  
// =============================================================================
// the rounding function chooses the type of step rounding to apply
// we can use either a regular rounding up/down to nearest step (ex: for step size = 10, a value of 17 becomes 20 and a value of 13 becomes 10)
// or an integer type, which considers only the "fully completed" step level (ex: for step size = 10, both values of 13 and 17 become 10)
f_rounding(_value, _step, _option) =>
    _option == 'Round up/down' ? 
      round(_value / _step) * _step : 
      int(_value / _step) * _step
    
//==============================================================================   
// Compund Ratio Moving Average function
f_CoraWave(_source, _length, _s) =>
    numerator   = 0.0,  denom = 0.0,    c_weight = 0.0
    r_multi     = 2.0
    Start_Wt    = 0.01          // Start Weight & r_multi are set to basic values here.
    End_Wt      = _length        // use length as initial End Weight to calculate base "r"
    r           = pow((End_Wt / Start_Wt),(1 / (_length - 1))) - 1
    base        = 1 + r * r_multi
    
    for i = 0 to _length - 1
        c_weight    := Start_Wt * pow(base, (_length - i))
        numerator   := numerator + _source[i] * c_weight 
        denom       := denom + c_weight
    
    cora_raw    = numerator / denom    
    cora_wave   = wma(cora_raw, _s)
//===================================================================================================================    
//                              inputs
// ===================================================================================================================
length      = input(title = "Length",           defval = 10,    minval = 1,                         inline = 'MA Line')
smooth      = input(title = "Extra Smoothing",  defval = 3,     minval = 1,                         inline = 'MA Line')

s_use       = input(title = "",                             defval = true,                          inline = "Step")
s_size      = input(title = "Step        Size [0 = Auto]", defval = 0.0, minval = 0,                inline = "Step")

r_option    = input(title = "Step Calculation", defval = 'Round up/down', options=['Round up/down', 'Whole Step'])

e_show      = input(title = "", defval = true,                                                      inline = "Pct Envelope")
e_pct       = input(title = "Pct Envelope           %", defval = 2.0, minval = 0, step = 0.25,      inline = "Pct Envelope")

tr_show     = input(title = "", defval = false,                                                     inline = "ATR Envelope")
tr_multi    = input(title = "ATR Envelope      ATR Multi", defval = 2.0, minval = 0, step = 0.25,   inline = "ATR Envelope")

// ===================================================================================================================
//                          Calculation
// ===================================================================================================================
SMAC_u      = f_CoraWave(high, length, smooth)    
SMAC_l      = f_CoraWave(low, length, smooth)    

//Apply Stepping to Channel Lines
s           = s_use ? s_size == 0.0 ? f_step(close) : s_size : 0

SMAC_us     = s_use ? f_rounding(SMAC_u, s, r_option) : SMAC_u
SMAC_ls     = s_use ? f_rounding(SMAC_l, s, r_option) : SMAC_l 

// Calculate Envelopes
e_upper     = SMAC_us * (1 + e_pct/100)
e_lower     = SMAC_ls * (1 - e_pct/100)

// while the pct envelope is a static absolute value, the ATR will change with each bar
// below we use a new ATR value only when the channel step changes - non-stepping/unrestricted ATR is still avaialble by disabling the stepping option
// maybe in future versions we make this a user option by itself ?
ATR         = atr(length) * tr_multi
ATR_us      = ATR, ATR_ls = ATR 
ATR_us      := change(SMAC_us) != 0 ? ATR : ATR_us[1]
ATR_ls      := change(SMAC_ls) != 0 ? ATR : ATR_ls[1]
ATR_upper   = SMAC_us + ATR_us
ATR_lower   = SMAC_ls - ATR_ls


// ===================================================================================================================
//                              Plot
// ===================================================================================================================
c_upper     = color.new(#55ff00,0)
c_lower     = color.new(#d5180b,0)
c_env       = color.new(color.silver, 50)
c_ATR       = color.new(color.yellow, 50)

PStyle      = plot.style_line
Channel_Up  = plot(SMAC_us, title="Upper Border", style = PStyle, color=c_upper, linewidth=2)
Channel_Lo  = plot(SMAC_ls, title="Lower Border", style = PStyle, color=c_lower, linewidth=2)

fill(Channel_Up, Channel_Lo, color.new(#115500,70), title="Channel Fill")

//PStyle1 = plot.style_stepline
PStyle1     = plot.style_line               //change default plot style to regular line for consistency

plot(e_show ? e_upper : na, "Upper Pct Envelope", style = PStyle1, color = c_env)
plot(e_show ? e_lower : na, "Lower Pct Envelope", style = PStyle1, color = c_env)

plot(tr_show ? ATR_upper : na, "Upper ATR Envelope", style = PStyle1, color = c_ATR)
plot(tr_show ? ATR_lower : na, "Lower ATR Envelope", style = PStyle1, color = c_ATR)

// ===================================================================================================================
//                              table
// ===================================================================================================================
txt_sz      = size.small
tbl_pos     = position.top_right

ctable      = #1111ff
cframe      = #ffeb3b
ctext       = #ffffff
c_vbg       = #2962ff

var QuickTable  = table.new(position = tbl_pos, columns = 2, rows = 9, 
  bgcolor = ctable , frame_color = cframe, frame_width = 2) 

// fill table
// =====================================================================================================================
f_FillRow(_table, _row, _label, _value) =>

    table.cell(_table, 0, _row, _label, bgcolor = ctable, text_color = ctext, 
      text_size = txt_sz, text_halign = text.align_left)
    
    table.cell(_table, 1, _row, tostring(_value), bgcolor = c_vbg, text_color = ctext, 
      text_size = txt_sz)
// =====================================================================================================================

step_state  = s_use ? s_size == 0.0 ? "Auto" : "Manual" : "Not Used"
TF          = timeframe.multiplier 
NoVal       = "-"     
val_pctenv  = e_show ? tostring(e_pct) : NoVal
val_pct     = e_show ? tostring(close * e_pct/100, "##0.00") : NoVal        // note that we simplified the value of % here. in reality the %env of high and low will be slightly different

val_ATRMulti= tr_show ? tostring(tr_multi) : NoVal
val_ATR     = tr_show ? tostring(ATR,"##0.00") : NoVal

i = -1
if barstate.islast
    i += 1, f_FillRow(QuickTable, i, "Step Mode", step_state)
    i += 1, f_FillRow(QuickTable, i, "Step Size", s)
    i += 1, f_FillRow(QuickTable, i, "TF Multi", TF)
    i += 1, f_FillRow(QuickTable, i, "Pct Env", e_show ? "On" : "Off")
    i += 1, f_FillRow(QuickTable, i, "Pct Env %", val_pctenv)
    i += 1, f_FillRow(QuickTable, i, "Pct Env Value", val_pct)
    i += 1, f_FillRow(QuickTable, i, "ATR Env", tr_show ? "On" : "Off")
    i += 1, f_FillRow(QuickTable, i, "ATR Multi", val_ATRMulti)
    i += 1, f_FillRow(QuickTable, i, "ATR Value", val_ATR)