RF+ Replay for Heikin Ashi
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tvenn

//@version=5
indicator(title="RF+ Replay for Heikin Ashi", shorttitle="RF+ Replay", overlay = true, max_bars_back = 1000, max_lines_count = 400, max_labels_count = 400)

//=Constants
green   = #95BD5F
red     = #EA1889
mocha   = #C0A392
transp  = color.new(#000000, 100)

haTicker = ticker.heikinashi(syminfo.tickerid)

initO = request.security(haTicker, timeframe.period, open)
initC = request.security(haTicker, timeframe.period, close)
initH = request.security(haTicker, timeframe.period, high)
initL = request.security(haTicker, timeframe.period, low)

haC          = (open + high + low + close) / 4
haO          = float(na)
haO          := na(haO[1]) ? (initO + initC) / 2 : (nz(initO[1]) + nz(initC[1])) / 2
haH          = high
haL          = low

useHAMode           = input(true, title="Enable Heikin Ashi Mode", tooltip="Range filters and moving averages will be calculated using Heikin Ashi OHLC values when this is enabled.")
showHACandles       = input(true, title="Show Heikin Ashi Candles", tooltip="This will print HA candles onto your standard chart, to help backtest using the Tradingview replay mode using Heikin Ashi type candles while using realprice data.\n\nYou will need to hide your existing chart candles, so you don't see two sets of candles.\n\nThese settings can be found by clicking the settings Cog icon, or right-clicking on your chart and selecting 'Chart Settings' from the menu, then selecting the tab named 'Symbol' and unchecking 'Body', 'Borders' and 'Wick'. This can also then be saved as a template and applied to a layout.")
showRangeFilters    = input(true, title="Show Range Filters")
enableMA            = input(true, title="Show Moving Averages")
enableMTFMA         = input(false, title="Show MTF Moving Average levels")
showRealPriceLine   = input(false, title="Show Real Price Line", tooltip="This will show a real price line for use with Heikin Ashi candles.\n\nNOTE: this feature may lag slightly due to the large number of features. Consider using a standalone indicator such as 'Real Price Line + Dots (for Heikin Ashi), linked in my Tradingview profile.")
plotrealclosedots   = input(false, title="Show Real Price Close Dots")
enableSessions      = input(true, title="Show Sessions", tooltip="This simply toggles your sessions so you don't have to reselect all the options to turn your prefered sessions settings on and off.")
showWatermark       = input(true, title="Show Watermark")

grp_HA              = "Heikin Ashi candles"

candleUpColor       = input.color(#ffffff, title="", group=grp_HA, inline="2")
candleDownColor     = input.color(color.rgb(131, 131, 131), title="", group=grp_HA, inline="2")
candleBodyEnabled   = input(true,  title="Body", group=grp_HA, inline="2")

candleUpBorderColor = input.color(color.black, title="", group=grp_HA, inline="3")
candleDownBorderColor = input.color(color.black, title="", group=grp_HA, inline="3")
candleBorderEnabled = input(true,  title="Border", group=grp_HA, inline="3")

wickUpColor         = input.color(color.rgb(110, 110, 110), title="", group=grp_HA, inline="4")
wickDownColor       = input.color(color.rgb(110, 110, 110), title="", group=grp_HA, inline="4")
candleWickEnabled   = input(true,  title="Wick", group=grp_HA, inline="4")

candleColor         = haC >= haO and showHACandles and candleBodyEnabled ? candleUpColor : showHACandles and candleBodyEnabled ? candleDownColor : na
candleBorderColor   = haC >= haO and showHACandles and candleBorderEnabled ? candleUpBorderColor : showHACandles and candleBorderEnabled ? candleDownBorderColor : na
candleWickColor     = haC >= haO and showHACandles and candleWickEnabled ? wickUpColor : showHACandles and candleWickEnabled ? wickDownColor : na


//=Moving averages
//==================================================================================================================================

ma(length, type) =>
    if(useHAMode)
        switch type
            "SMA" => ta.sma(haC, length)
            "EMA" => ta.ema(haC, length)
            "SMMA (RMA)" => ta.rma(haC, length)
            "WMA" => ta.wma(haC, length)
            "VWMA" => ta.vwma(haC, length)
    else
        switch type
            "SMA" => ta.sma(close, length)
            "EMA" => ta.ema(close, length)
            "SMMA (RMA)" => ta.rma(close, length)
            "WMA" => ta.wma(close, length)
            "VWMA" => ta.vwma(close, length)


grp_MA      = "Moving averages"

ma1color    = input(color.new(color.orange, 40), title="", group=grp_MA, inline="1")
lenMA1      = input(144, title="", group=grp_MA, inline="1")
typeMA1     = input.string(title = "", defval = "EMA", options=["EMA", "SMA", "SMMA (RMA)", "WMA", "VWMA"], group=grp_MA, inline="1")
showma1     = input(true, title="Show", group=grp_MA, inline="1")

ma2color    = input(color.new(green, 40), title="", group=grp_MA, inline="2")
lenMA2      = input(50, title="", group=grp_MA, inline="2")
typeMA2     = input.string(title = "", defval = "EMA", options=["EMA", "SMA", "SMMA (RMA)", "WMA", "VWMA"], group=grp_MA, inline="2")
showma2     = input(false, title="Show", group=grp_MA, inline="2")

ma3color    = input(color.new(red, 40), title="", group=grp_MA, inline="3")
lenMA3      = input(21, title="", group=grp_MA, inline="3")
typeMA3     = input.string(title = "", defval = "EMA", options=["EMA", "SMA", "SMMA (RMA)", "WMA", "VWMA"], group=grp_MA, inline="3")
showma3     = input(false, title="Show", group=grp_MA, inline="3")

plot(enableMA and showma1 ? ma(lenMA1, typeMA1) : na, title="MA 1", color = ma1color, linewidth=2)
plot(enableMA and showma2 ? ma(lenMA2, typeMA2) : na, title="MA 2", color = ma2color, linewidth=2)
plot(enableMA and showma3 ? ma(lenMA3, typeMA3) : na, title="MA 3", color = ma3color, linewidth=2)

priceCrossingMA1 = ta.crossover(ma(lenMA1, typeMA1), close)


//=MTF MA settings
//==================================================================================================================================
grp_MTFMA   = "MTF MA Settings"
MTFMALen    = input.int(144, title="", minval=2, maxval=200, step=1, group=grp_MTFMA, inline="0")
MTFMAType   = input.string("EMA", title="", options=["SMA", "EMA", "SMMA (RMA)", "WMA", "VWMA"], group=grp_MTFMA, inline="0")

realPriceTicker = ticker.new(prefix=syminfo.prefix, ticker=syminfo.ticker)

getTimeframe(timeframe) =>
    switch timeframe
        "1 min" => "1"
        "2 min" => "2"
        "3 min" => "3"
        "5 min" => "5"
        "10 min" => "10"
        "15 min" => "15"

// MTF MA #1
tf1LineColor 	= input.color(color.new(color.orange, 40), title="", group=grp_MTFMA, inline='1')
tf1 			= input.string("3 min", title="", options=["1 min", "2 min", "3 min", "5 min", "10 min", "15 min"], group=grp_MTFMA, inline='1')
enableTF1 		= input.bool(true, title='Show Level', group=grp_MTFMA, inline='1', tooltip="")
// MTF MA #2 
tf2LineColor 	= input.color(color.new(color.orange, 40), title="", group=grp_MTFMA, inline='2')
tf2 			= input.string("5 min", title="", options=["1 min", "2 min", "3 min", "5 min", "10 min", "15 min"], group=grp_MTFMA, inline='2')
enableTF2 		= input.bool(true, title='Show Level', group=grp_MTFMA, inline='2')
// MTF MA #3 
tf3LineColor 	= input.color(color.new(color.orange, 40), title='', group=grp_MTFMA, inline='3')
tf3 		 	= input.string("15 min", title='', options=["1 min", "2 min", "3 min", "5 min", "10 min", "15 min"], group=grp_MTFMA, inline='3')
enableTF3		= input.bool(true, title='Show Level', group=grp_MTFMA, inline='3')

mtf1MA          = request.security(symbol=realPriceTicker, timeframe=getTimeframe(tf1), expression=ma(MTFMALen, MTFMAType), lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
mtf2MA          = request.security(symbol=realPriceTicker, timeframe=getTimeframe(tf2), expression=ma(MTFMALen, MTFMAType), lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)
mtf3MA          = request.security(symbol=realPriceTicker, timeframe=getTimeframe(tf3), expression=ma(MTFMALen, MTFMAType), lookahead=barmerge.lookahead_off, gaps=barmerge.gaps_off)

mtfMATextColor 	= input.color(color.new(color.orange, 70), title="", group=grp_MTFMA, inline="4")
showTFLabel     = input.bool(true, title='Show Timeframe Labels', group=grp_MTFMA, inline="4")

if (enableMTFMA and enableTF1)
	mtf1MALine = line.new(x1=bar_index, x2=bar_index + 10, y1=mtf1MA, y2=mtf1MA, extend=extend.right, style=line.style_dotted, color=tf1LineColor)
	line.delete(mtf1MALine[1])
	if(showTFLabel)
		mtf1MALabel = label.new(x=(bar_index + 20), y=mtf1MA, style=label.style_label_down, text=tf1, color=na, textcolor=mtfMATextColor)
		label.delete(mtf1MALabel[1])

if(enableMTFMA and enableTF2)
	mtf2MALine = line.new(x1=bar_index, x2=bar_index + 10, y1=mtf2MA, y2=mtf2MA, extend=extend.right, style=line.style_dotted, color=tf2LineColor)
	line.delete(mtf2MALine[1])
	if(showTFLabel)
		mtf2MALabel = label.new(x=(bar_index + 20), y=mtf2MA, style=label.style_label_down, text=tf2, color=na, textcolor=mtfMATextColor)
		label.delete(mtf2MALabel[1])

if(enableMTFMA and enableTF3)
	mtf3MALine = line.new(x1=bar_index, x2=bar_index + 10, y1=mtf3MA, y2=mtf3MA, extend=extend.right, style=line.style_dotted, color=tf3LineColor)
	line.delete(mtf3MALine[1])
	if(showTFLabel)
		mtf3MALabel = label.new(x=(bar_index + 20), y=mtf3MA, style=label.style_label_down, text=tf3, color=na, textcolor=mtfMATextColor)
		label.delete(mtf3MALabel[1])


//=Real price line & real price close dots
//==================================================================================================================================

//Real price line and dot settings
grp_RPHA                    = "Real price for Heikin Ashi candles"
showRealPriceLinelinecolor  = input(color.new(#41ffb0, 0), title="Real Price Line Color", group=grp_RPHA)
realpricelinewidth          = input.string("Thin", options=["Thin", "Thicc"], title="Real Price Line Width", group=grp_RPHA)
realclosedotscolor          = input(color.new(#41ffb0, 40), title="Real Price Close Dot Color", group=grp_RPHA)
size                        = input.string("Auto", options=["Auto", "Tiny", "Small"], title="Real Price Close Dot Size", group=grp_RPHA)

_realPriceLineWidth = switch realpricelinewidth
    "Thin" => 1
    "Thicc" => 2

real_price = ticker.new(prefix=syminfo.prefix, ticker=syminfo.ticker)
real_close = request.security(symbol=real_price, timeframe='', expression=close, gaps=barmerge.gaps_off, lookahead=barmerge.lookahead_off)

if(showRealPriceLine)
    real_price_line = line.new(bar_index[1], real_close, bar_index, real_close, xloc.bar_index, extend.both, showRealPriceLinelinecolor, line.style_dotted, _realPriceLineWidth)
    line.delete(real_price_line[1])

//=Plot candles above MA and Below Range Filters
//==================================================================================================================================
plotcandle(haO, haH, haL, haC, color=candleColor, bordercolor=candleBorderColor, wickcolor=candleWickColor)

//Show real price close dots above candles
showrealpricemicrodotstiny = (plotrealclosedots and size=="Tiny" ? display.all : display.none)
showrealpricemicrodotsauto = (plotrealclosedots and size=="Auto" ? display.all : display.none)
showrealpricedotslarge = (plotrealclosedots and size=="Small" ? display.all : display.none)

plotchar(series=real_close, title="Real close dots", location=location.absolute, color=realclosedotscolor, editable=false, char="•", size=size.tiny, display=showrealpricemicrodotstiny)
plotchar(series=real_close, title="Real close dots", location=location.absolute, color=realclosedotscolor, editable=false, char="•", size=size.auto, display=showrealpricemicrodotsauto)
plotshape(series=real_close, title="Real close dots", location=location.absolute, color=realclosedotscolor, editable=false, style=shape.circle, size=size.auto, display=showrealpricedotslarge)


//=Range Filter settings
//==================================================================================================================================

//Range Filter #1 Settings
grp_RF1 = "Range Filter #1 settings"
rf1UpColor   = input.color(color.new(#ffffff, 40), title="Range Filter 1 bullish color", group=grp_RF1)
rf1DownColor = input.color(color.new(color.blue, 40), title="Range Filter 1 bearish color", group=grp_RF1)
rf1LineWidth = input.int(1, minval=1, maxval=3, title="Range Filter 1 line width", group=grp_RF1)
src1    = input(defval=hl2, title="Source", group=grp_RF1)
per1    = input(defval=30, title="Sampling Period", group=grp_RF1)
mult1   = input(defval=2.6, title="Range Multiplier", group=grp_RF1)

//Range Filter #2 Settings
grp_RF2 = "Range Filter #2 settings"
rf2UpColor   = input.color(color.new(#ffffff, 10), title="Range Filter 2 bullish color", group=grp_RF2)
rf2DownColor = input.color(color.new(color.blue, 10), title="Range Filter 2 bearish color", group=grp_RF2)
rf2LineWidth = input.int(2, minval=1, maxval=3, title="Range Filter 2 line width", group=grp_RF2)
src2    = input(defval=ohlc4, title="Source", group=grp_RF2)
per2    = input(defval=48, title="Sampling Period", group=grp_RF2)
mult2   = input(defval=3.4, title="Range Multiplier", group=grp_RF2)


sourceType(type, showHA) =>
    if(useHAMode)
        switch type
            open => initO
            high => initH
            low => initL
            close => initC
            hl2 => ((initH + initL) / 2)
            hlc3 => (initH + initL + initC / 3)
            ohlc4 => ((initO + initH + initL + initC) / 4)
            hlcc4 => ((initH + initL + initC + initC) / 4)
    else if(useHAMode==false)
        switch type
            open => open
            high => high
            low => low
            close => close
            hl2 => hl2
            hlc3 => hlc3
            ohlc4 => ohlc4
            hlcc4 => hlcc4

filterSrc1 = sourceType(src1, useHAMode)
filterSrc2 = sourceType(src2, useHAMode)

// Smooth average
smoothrng(x, t, m) =>
    wper = t * 2 - 1
    avrng = ta.ema(math.abs(x - x[1]), t)
    smoothrng = ta.ema(avrng, wper) * m
    smoothrng
smrng1 = smoothrng(filterSrc1, per1, mult1)
smrng2 = smoothrng(filterSrc2, per2, mult2)

// Range Filter calculation
rngfilt(x, r) =>
    rngfilt = x
    rngfilt := x > nz(rngfilt[1]) ? x - r < nz(rngfilt[1]) ? nz(rngfilt[1]) : x - r : 
       x + r > nz(rngfilt[1]) ? nz(rngfilt[1]) : x + r
    rngfilt
filt1 = rngfilt(filterSrc1, smrng1)
filt2 = rngfilt(filterSrc2, smrng2)

//RF Filter direction
upward1     = 0.0
downward1   = 0.0
upward2     = 0.0
downward2   = 0.0
upward1     := filt1 > filt1[1] ? nz(upward1[1]) + 1 : filt1 < filt1[1] ? 0 : nz(upward1[1])
downward1   := filt1 < filt1[1] ? nz(downward1[1]) + 1 : filt1 > filt1[1] ? 0 : nz(downward1[1])
upward2     := filt2 > filt2[1] ? nz(upward2[1]) + 1 : filt2 < filt2[1] ? 0 : nz(upward2[1])
downward2   := filt2 < filt2[1] ? nz(downward2[1]) + 1 : filt2 > filt2[1] ? 0 : nz(downward2[1])

//RF Target bands
hband1 = filt1 + smrng1
lband1 = filt1 - smrng1
hband2 = filt2 + smrng2
lband2 = filt2 - smrng2

//RF Colors
filtcolor1  = upward1 > 0 ? rf1UpColor : rf1DownColor
filtcolor2  = upward2 > 0 ? rf2UpColor : rf2DownColor
filtplot1   = plot((showRangeFilters ? filt1 : na), color=filtcolor1, linewidth=rf1LineWidth, title="Range Filter #1")
filtplot2   = plot((showRangeFilters ? filt2 : na), color=filtcolor2, linewidth=rf2LineWidth, title="Range Filter #2")

//RF range bands upper / lower
hbandplot1 = plot(hband1, color=transp, title="RF #1 high", linewidth=1, display=display.none)
lbandplot1 = plot(lband1, color=transp, title="RF #1 low", linewidth=1, display=display.none)
hbandplot2 = plot(hband2, color=color.new(#CCCCCC, 70), title="RF #2 high", linewidth=1, display=display.none)
lbandplot2 = plot(lband2, color=color.new(#CCCCCC, 70), title="RF #2 low", linewidth=1, display=display.none)

//RF Fills
fill(hbandplot1, filtplot1, color=color.new(#5B9CF6, 100), title="RF #1 area high")
fill(lbandplot1, filtplot1, color=color.new(#5B9CF6, 100), title="RF #1 area low")
fill(hbandplot2, filtplot2, color=color.new(#cccccc, 100), title="RF #2 area high")
fill(lbandplot2, filtplot2, color=color.new(#cccccc, 100), title="RF #2 area low")


//=Sessions
//==================================================================================================================================
london_color    = color.new(color.green, 90)
newyork_color   = color.new(color.blue, 90)
tokyo_color     = color.new(color.red, 90)
sydney_color    = color.new(color.orange, 90)

grp_SESS        = "Sessions"
utcsetting      = input.string(title="Select UTC timezone", defval="UTC+1", options=["UTC-11","UTC-10","UTC-9","UTC-8","UTC-7","UTC-6","UTC-5","UTC-4","UTC-3","UTC-2:30","UTC-2","UTC-1","UTC","UTC+1","UTC+2","UTC+3","UTC+4","UTC+4:30","UTC+5","UTC+6","UTC+7","UTC+8","UTC+9","UTC+10","UTC+11","UTC+12","UTC+13"], group=grp_SESS)
daylightsavings = input(false, title="Add 1hr for daylight savings time (DST)", group=grp_SESS)

// Show sessions
timelondon  = time(timeframe.period, daylightsavings ? "0800-1700:23456" : "0700-1600:23456", utcsetting)
timenewyork = time(timeframe.period, daylightsavings ? "1300-2200:23456" : "1200-2100:23456", utcsetting)
timetokyo   = time(timeframe.period, daylightsavings ? "0000-0900:23456" : "2300-0800:23456", utcsetting)
timesydney  = time(timeframe.period, daylightsavings ? "2200-0700:23456" : "2100-0600:23456", utcsetting)

// Show Trading Sessions
// Inverted Sessions Logic
invertedsessioncolor= input.color(color.new(color.blue, 90), title="", group=grp_SESS, inline="0")
invertsessioncolour = input(false, title="Enable inverted session(s) color", tooltip="This will color the background of the entire chart - except the trading session(s) you select, to give you a clear chart, free of a distracting background colors. 👌", group=grp_SESS, inline="0")

// London Session
londonsessioncolor  = input.color(london_color, title="", group=grp_SESS, inline="1")
showlondonsession   = input(false, title="Show London session", group=grp_SESS, inline="1")
bgcolor((enableSessions and timelondon and showlondonsession and not invertsessioncolour) ? londonsessioncolor : na)

// New York Session
newyorksessioncolor = input.color(newyork_color, title="", group=grp_SESS, inline="2")
shownewyorksession  = input(false, title="Show New York session", group=grp_SESS, inline="2")
bgcolor((enableSessions and timenewyork and shownewyorksession and not invertsessioncolour) ? newyorksessioncolor : na)

// Tokyo Session
tokyosessioncolor   = input.color(tokyo_color, title="", group=grp_SESS, inline="3")
showtokyosession    = input(false, title="Show Tokyo session", group=grp_SESS, inline="3")
bgcolor((enableSessions and timetokyo and showtokyosession and not invertsessioncolour) ? tokyosessioncolor : na)

// Sydney Session
sydneysessioncolor  = input.color(sydney_color, title="", group=grp_SESS, inline="4")
showsydneysession   = input(false, title="Show Sydney session", group=grp_SESS, inline="4")
bgcolor((enableSessions and timesydney and showsydneysession and not invertsessioncolour) ? sydneysessioncolor : na)

bgcolor((enableSessions and invertsessioncolour and not (showlondonsession ? timelondon : na) and not (shownewyorksession ? timenewyork : na) and not (showtokyosession ? timetokyo : na) and not (showsydneysession ? timesydney : na)) ? invertedsessioncolor : na)


//=Watermark
//==================================================================================================================================
textVPosition = input.string("top", "", options = ["top", "middle", "bottom"], group = "Watermark text", inline="w0")
showText      = input(true, title="Show text", group = "Watermark text", inline="w0")
s_title       = input.string("normal", "", options = ["small", "normal", "large", "huge", "auto"], group = "Watermark text", inline="w1")
c_title       = input.color(color.new(color.gray, 0), "", group = "Watermark text", inline="w1")
title         = input("RF+", "", group = "Watermark text", inline="w1")
s_subtitle    = input.string("normal", "", options = ["small", "normal", "large", "huge", "auto"], group = "Watermark text", inline="w2")
c_subtitle    = input(color.new(color.gray, 0), "", group = "Watermark text", inline="w2")
subtitle      = input("Replay for Heikin Ashi", "", group = "Watermark text", inline="w2")

symVPosition  = input.string("bottom", "", options = ["top", "middle", "bottom"], group = "Watermark symbol", inline="w3")
showSymText   = input(true, title="Show symbol", group = "Watermark symbol", inline="w3")
symInfo       = syminfo.ticker  + " | " + timeframe.period + (timeframe.isminutes ? "M" : na)
date          = str.tostring(dayofmonth(time_close)) + "/" + str.tostring(month(time_close)) + "/" + str.tostring(year(time_close))
s_symInfo     = input.string("normal", "", options = ["small", "normal", "large", "huge", "auto"], group = "Watermark symbol", inline="w4")
c_symInfo     = input(color.new(color.gray, 30), "", group = "Watermark symbol", inline="w4")
c_bg          = color.new(color.blue, 100)


//text watermark creation
textWatermark = table.new(textVPosition + "_" + "center", 1, 3)
if showText == true and showWatermark
    table.cell(textWatermark, 0, 0, title, 0, 0, c_title, "center", text_size = s_title, bgcolor = c_bg)
    table.cell(textWatermark, 0, 1, subtitle, 0, 0, c_subtitle, "center", text_size = s_subtitle, bgcolor = c_bg)
//symbol info watermark creation
symWatermark = table.new(symVPosition + "_" + "center", 5, 5)
if showSymText == true and showWatermark
    table.cell(symWatermark, 0, 1, symInfo, 0, 0, c_symInfo, "center", text_size = s_symInfo, bgcolor = c_bg)



