// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/ // REUSING THIS CODE: You are welcome to reuse this code without permission, including in closed-source publications, as long as proper credits are given :) // Author: ©fareidzulkifli // Description : // Mean Reversion Channel objective, based on Mean Reversion theory (everything has a tendency to revert back to its mean), is to help visualizing: // Inner Channel -> Dynamic Support and Resistance // Outer Channel -> Overbought/Oversold Zone which may signal consolidation phase or potential reversal due to unsustainable move // The concept of this indicator oriiginally derived from Keltner Channel (The Keltner Channel was first introduced by Chester Keltner in the 1960s. The original formula used simple moving averages (SMA) and the high-low price range to calculate the bands. In the 1980s, a new formula was introduced that used ATR.) // Instead if using SMA/EMA, this indicator used SuperSmoother MA as it's mean with longer lookback period (default to 200) to get more stable channel line. i also added second level so the indicator will have inner and outer channel // Details of each filtering type used for mean calculation can be read in Ehlers Technical Papers: "Swiss Army Knife Indicator" and/or his book "Cybernetics Analysis for Stock and Futures" // Disclaimer: // Past performance is not an indicator of future results. // My opinions are my own and do not constitute financial advice in any way whatsoever. // Nothing published by me constitutes an investment/trading recommendation, nor should any data or Content published by me be relied upon for any investment/trading activities. // I strongly recommends that you perform your own independent research and/or speak with a qualified investment professional before making any financial decisions. // Any ideas to further improve this indicator are welcome :) //@version=4 study("Mean Reversion Channel - MRI Variant", shorttitle="MRC", overlay=true, format=format.inherit) //************************************************************************************************************ // Parameter //************************************************************************************************************ indiSet = input(false, "═════════ MRC Parameter ════════") source = input(hlc3, title="Price Source", type=input.source) type = input("SuperSmoother", title="Filter Type", options=["SuperSmoother", "Ehlers EMA", "Gaussian", "Butterworth", "BandStop", "SMA", "EMA", "RMA"]) length = input(200, title="Lookback Period",minval=1) innermult = input(1.0, title="Inner Channel Size Multiplier", minval = 0.1) outermult = input(2.415, title="Outer Channel Size Multiplier", minval = 0.1) ChartSet = input(false, "═════════ Chart Setting ════════") drawchannel = input(true, title="Draw Channel") displayzone = input(true, title="Draw Zone (With Channel)") zonetransp = input(60, title="Zone Transparency", minval=0, maxval=100) displayline = input(true, title="Display Line Extension") MTFSet = input(false, "═════════ MTF Setting ════════") enable_mtf = input(true, title="Enable Multiple TimeFrame Analysis") mtf_disp_typ= input("On Hover", title="MTF Display Type", options=["Always Display", "On Hover"]) mtf_typ = input("Auto", title="Multiple TimeFrame Type", options=["Auto", "Custom"]) mtf_lvl1 = input("D", title="Custom MTF Level 1", type=input.resolution) mtf_lvl2 = input("W", title="Custom MTF Level 2", type=input.resolution) //************************************************************************************************************ // Functions Start { //************************************************************************************************************ var pi = 2 * asin(1) var mult = pi * innermult var mult2 = pi * outermult var gradsize = 0.5 var gradtransp = zonetransp //----------------------- // Ehler SwissArmyKnife Function //----------------------- SAK_smoothing(_type, _src, _length) => c0 = 1.0 c1 = 0.0 b0 = 1.0 b1 = 0.0 b2 = 0.0 a1 = 0.0 a2 = 0.0 alpha = 0.0 beta = 0.0 gamma = 0.0 cycle = 2 * pi / _length if _type == "Ehlers EMA" alpha := (cos(cycle) + sin(cycle) - 1) / cos(cycle) b0 := alpha a1 := 1 - alpha if _type == "Gaussian" beta := 2.415 * (1 - cos(cycle)) alpha := -beta + sqrt((beta * beta) + (2 * beta)) c0 := alpha * alpha a1 := 2 * (1 - alpha) a2 := -(1 - alpha) * (1 - alpha) if _type == "Butterworth" beta := 2.415 * (1 - cos(cycle)) alpha := -beta + sqrt((beta * beta) + (2 * beta)) c0 := alpha * alpha / 4 b1 := 2 b2 := 1 a1 := 2 * (1 - alpha) a2 := -(1 - alpha) * (1 - alpha) if _type == "BandStop" beta := cos(cycle) gamma := 1 / cos(cycle*2*0.1) // delta default to 0.1. Acceptable delta -- 0.05 s_a1 = exp(-sqrt(2) * pi / _length) s_b1 = 2 * s_a1 * cos(sqrt(2) * pi / _length) s_c3 = -pow(s_a1, 2) s_c2 = s_b1 s_c1 = 1 - s_c2 - s_c3 ss = 0.0 ss := s_c1 * _src + s_c2 * nz(ss[1], _src[1]) + s_c3 * nz(ss[2], _src[2]) //----------------------- // Auto TimeFrame Function //----------------------- // ————— Converts current chart resolution into a float minutes value. f_resInMinutes() => _resInMinutes = timeframe.multiplier * ( timeframe.isseconds ? 1. / 60 : timeframe.isminutes ? 1. : timeframe.isdaily ? 60. * 24 : timeframe.isweekly ? 60. * 24 * 7 : timeframe.ismonthly ? 60. * 24 * 30.4375 : na) get_tf(_lvl)=> y = f_resInMinutes() z = timeframe.period if(mtf_typ=="Auto") if y < 1 z := _lvl == 1 ? "1" : _lvl == 2 ? "5" : z else if y <= 3 z := _lvl == 1 ? "5" : _lvl == 2 ? "15" : z else if y <= 10 z := _lvl == 1 ? "15" : _lvl == 2 ? "60" : z else if y <= 30 z := _lvl == 1 ? "60" : _lvl == 2 ? "240" : z else if y <= 120 z := _lvl == 1 ? "240" : _lvl == 2 ? "D" : z else if y <= 240 z := _lvl == 1 ? "D" : _lvl == 2 ? "W" : z else if y <= 1440 z := _lvl == 1 ? "W" : _lvl == 2 ? "M" : z else if y <= 10080 z := _lvl == 1 ? "M" : z else z := z else z := _lvl == 1 ? mtf_lvl1 : _lvl == 2 ? mtf_lvl2 : z z //----------------------- // Mean Reversion Channel Function //----------------------- get_mrc()=> v_condition = 0 v_meanline = source v_meanrange = supersmoother(tr, length) //-- Get Line value if(type == "SuperSmoother") v_meanline := supersmoother(source, length) if(type != "SuperSmoother") v_meanline := SAK_smoothing(type, source, length) v_upband1 = v_meanline+(v_meanrange*mult) v_loband1 = v_meanline-(v_meanrange*mult) v_upband2 = v_meanline+(v_meanrange*mult2) v_loband2 = v_meanline-(v_meanrange*mult2) //-- Check Condition if(close > v_meanline) v_upband2_1 = v_upband2 + (v_meanrange * gradsize * 4) v_upband2_9 = v_upband2 + (v_meanrange * gradsize * -4) if(high >= v_upband2_9 and high < v_upband2) v_condition := 1 else if(high >= v_upband2 and high < v_upband2_1) v_condition := 2 else if(high >= v_upband2_1) v_condition := 3 else if(close <= v_meanline+v_meanrange) v_condition := 4 else v_condition := 5 if(close < v_meanline) v_loband2_1 = v_loband2 - (v_meanrange * gradsize * 4) v_loband2_9 = v_loband2 - (v_meanrange * gradsize * -4) if(low <= v_loband2_9 and low > v_loband2) v_condition := -1 else if(low <= v_loband2 and low > v_loband2_1) v_condition := -2 else if(low <= v_loband2_1) v_condition := -3 else if(close >= v_meanline+v_meanrange) v_condition := -4 else v_condition := -5 [v_meanline, v_meanrange, v_upband1, v_loband1, v_upband2, v_loband2, v_condition] //----------------------- // MTF Analysis //----------------------- get_stat(_cond) => ret = "Price at Mean Line\n" if (_cond == 1) ret := "Overbought (Weak)\n" else if (_cond == 2) ret := "Overbought\n" else if (_cond == 3) ret := "Overbought (Strong)\n" else if (_cond == 4) ret := "Price Near Mean\n" else if (_cond == 5) ret := "Price Above Mean\n" else if (_cond == -1) ret := "Oversold (Weak)\n" else if (_cond == -2) ret := "Oversold\n" else if (_cond == -3) ret := "Oversold (Strong)\n" else if (_cond == -4) ret := "Price Near Mean\n" else if (_cond == -5) ret := "Price Below Mean\n" ret //----------------------- // Chart Drawing Function //----------------------- format_price(x) => y = tostring(x, "0.00000") if(x>10) y := tostring(x, "0.000") if(x>1000) y := tostring(x, "0.00") y f_PriceLine(_ref, linecol) => line.new( x1 = bar_index, x2 = bar_index - 1, y1 = _ref, y2 = _ref, extend = extend.left, color = linecol) f_MTFLabel(_txt, _yloc) => label.new( x = time + round(change(time)*20), y = _yloc, xloc = xloc.bar_time, text = mtf_disp_typ == "Always Display" ? _txt : "Check MTF", tooltip = mtf_disp_typ == "Always Display" ? "" : _txt, color = color.black, textcolor = color.white, size = size.normal, style = mtf_disp_typ == "On Hover" and displayline ? label.style_label_lower_left : label.style_label_left, textalign = text.align_left) //} Function End //************************************************************************************************************ // Calculate Channel //************************************************************************************************************ var tf_0 = timeframe.period var tf_1 = get_tf(1) var tf_2 = get_tf(2) [meanline, meanrange, upband1, loband1, upband2, loband2, condition] = get_mrc() [mtf1_meanline, mtf1_meanrange, mtf1_upband1, mtf1_loband1, mtf1_upband2, mtf1_loband2, mtf1_condition] = security(syminfo.tickerid, tf_1, get_mrc()) [mtf2_meanline, mtf2_meanrange, mtf2_upband1, mtf2_loband1, mtf2_upband2, mtf2_loband2, mtf2_condition] = security(syminfo.tickerid, tf_2, get_mrc()) //************************************************************************************************************ // Drawing Start { //************************************************************************************************************ float p_meanline = drawchannel ? meanline : na float p_upband1 = drawchannel ? upband1 : na float p_loband1 = drawchannel ? loband1 : na float p_upband2 = drawchannel ? upband2 : na float p_loband2 = drawchannel ? loband2 : na z = plot(p_meanline, color=#FFCD00, style=plot.style_line, title=" Mean", linewidth=2) x1 = plot(p_upband1, color=color.green, style=plot.style_circles, title=" R1", linewidth=1, transp=50) x2 = plot(p_loband1, color=color.green, style=plot.style_circles, title=" S1", linewidth=1, transp=50) y1 = plot(p_upband2, color=color.red, style=plot.style_line, title=" R2", linewidth=1, transp=50) y2 = plot(p_loband2, color=color.red, style=plot.style_line, title=" S2", linewidth=1, transp=50) //----------------------- // Draw zone //----------------------- //--- var color1 = #FF0000, var color2 = #FF4200, var color3 = #FF5D00, var color4 = #FF7400, var color5 = #FF9700, var color6 = #FFAE00, var color7 = #FFC500, var color8 = #FFCD00 //--- float upband2_1 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * 4) : na, float loband2_1 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * 4) : na float upband2_2 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * 3) : na, float loband2_2 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * 3) : na float upband2_3 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * 2) : na, float loband2_3 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * 2) : na float upband2_4 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * 1) : na, float loband2_4 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * 1) : na float upband2_5 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * 0) : na, float loband2_5 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * 0) : na float upband2_6 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * -1) : na, float loband2_6 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * -1) : na float upband2_7 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * -2) : na, float loband2_7 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * -2) : na float upband2_8 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * -3) : na, float loband2_8 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * -3) : na float upband2_9 = drawchannel and displayzone ? upband2 + (meanrange * gradsize * -4) : na, float loband2_9 = drawchannel and displayzone ? loband2 - (meanrange * gradsize * -4) : na //--- plot_upband2_1 = plot(upband2_1, color=na, transp=100, display=display.none), plot_loband2_1 = plot(loband2_1, color=na, transp=100, display=display.none) plot_upband2_2 = plot(upband2_2, color=na, transp=100, display=display.none), plot_loband2_2 = plot(loband2_2, color=na, transp=100, display=display.none) plot_upband2_3 = plot(upband2_3, color=na, transp=100, display=display.none), plot_loband2_3 = plot(loband2_3, color=na, transp=100, display=display.none) plot_upband2_4 = plot(upband2_4, color=na, transp=100, display=display.none), plot_loband2_4 = plot(loband2_4, color=na, transp=100, display=display.none) plot_upband2_5 = plot(upband2_5, color=na, transp=100, display=display.none), plot_loband2_5 = plot(loband2_5, color=na, transp=100, display=display.none) plot_upband2_6 = plot(upband2_6, color=na, transp=100, display=display.none), plot_loband2_6 = plot(loband2_6, color=na, transp=100, display=display.none) plot_upband2_7 = plot(upband2_7, color=na, transp=100, display=display.none), plot_loband2_7 = plot(loband2_7, color=na, transp=100, display=display.none) plot_upband2_8 = plot(upband2_8, color=na, transp=100, display=display.none), plot_loband2_8 = plot(loband2_8, color=na, transp=100, display=display.none) plot_upband2_9 = plot(upband2_9, color=na, transp=100, display=display.none), plot_loband2_9 = plot(loband2_9, color=na, transp=100, display=display.none) //--- fill(plot_upband2_1,plot_upband2_2, color=color1, transp=gradtransp), fill(plot_loband2_1,plot_loband2_2, color=color1, transp=gradtransp) fill(plot_upband2_2,plot_upband2_3, color=color2, transp=gradtransp), fill(plot_loband2_2,plot_loband2_3, color=color2, transp=gradtransp) fill(plot_upband2_3,plot_upband2_4, color=color3, transp=gradtransp), fill(plot_loband2_3,plot_loband2_4, color=color3, transp=gradtransp) fill(plot_upband2_4,plot_upband2_5, color=color4, transp=gradtransp), fill(plot_loband2_4,plot_loband2_5, color=color4, transp=gradtransp) fill(plot_upband2_5,plot_upband2_6, color=color5, transp=gradtransp), fill(plot_loband2_5,plot_loband2_6, color=color5, transp=gradtransp) fill(plot_upband2_6,plot_upband2_7, color=color6, transp=gradtransp), fill(plot_loband2_6,plot_loband2_7, color=color6, transp=gradtransp) fill(plot_upband2_7,plot_upband2_8, color=color7, transp=gradtransp), fill(plot_loband2_7,plot_loband2_8, color=color7, transp=gradtransp) fill(plot_upband2_8,plot_upband2_9, color=color8, transp=gradtransp), fill(plot_loband2_8,plot_loband2_9, color=color8, transp=gradtransp) //----------------------- // Plot Extension //----------------------- if(displayline and enable_mtf and mtf_disp_typ == "Always Display") displayline := false var line mean = na, line.delete(mean), mean := displayline ? f_PriceLine(meanline, #FFCD00) : na var line res1 = na, line.delete(res1), res1 := displayline ? f_PriceLine(upband1, color.green) : na var line sup1 = na, line.delete(sup1), sup1 := displayline ? f_PriceLine(loband1, color.green) : na var line res2 = na, line.delete(res2), res2 := displayline ? f_PriceLine(upband2, color.red) : na var line sup2 = na, line.delete(sup2), sup2 := displayline ? f_PriceLine(loband2, color.red) : na //-------------- // Prep MTF Label //-------------- var brl = "\n--------------------------------------" dist_0 = "Distance from Mean: "+ tostring(((close-meanline)/close)*100, "#.##") + " %" dist_1 = "Distance from Mean: "+ tostring(((close-mtf1_meanline)/close)*100, "#.##") + " %" dist_2 = "Distance from Mean: "+ tostring(((close-mtf2_meanline)/close)*100, "#.##") + " %" var title = "Mean Reversion Channel\nMultiple TimeFrame Analysis" + brl tf0 = "\n\nTimeframe: "+ tf_0 + " (Current)\n\nStatus: " + get_stat(condition) + dist_0 + brl tf1 = not timeframe.ismonthly ? "\n\nTimeframe: "+ tf_1 + "\n\nStatus: " + get_stat(mtf1_condition) + dist_1 + brl : "" tf2 = not timeframe.isweekly and not timeframe.ismonthly ? "\n\nTimeframe: "+ tf_2 + "\n\nStatus: " + get_stat(mtf2_condition) + dist_2 + brl : "" mtf_lbl = title+tf0+tf1+tf2 var label label_mtf = na, label.delete(label_mtf), label_mtf := enable_mtf ? f_MTFLabel(mtf_lbl, meanline) : na //} Drawing End