Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
- // © quantifytools
- //@version=5
- indicator("Fair value bands", overlay=true, max_labels_count=500, max_lines_count=500)
- // Inputs
- //Settings
- groupFair1 = "Fair value basis & bands"
- i_btCheck = input.bool(false, "Highlight metric calculations", group=groupFair1, tooltip="Enables lot based coloring and time/volume distribution counters, found in Data Window tab.")
- i_boost = input.float(1, "Threshold band width", group=groupFair1, step=0.10, minval = 0)
- i_bandBoost = input.float(1, "Deviation band width", group=groupFair1, step=0.10, minval = 0)
- i_source = input.source(ohlc4, "", inline="source", group=groupFair1)
- i_smoothingType = input.string("SMA", "", options=["SMA", "EMA", "HMA", "RMA", "WMA", "VWMA", "Median"], inline="source", group=groupFair1)
- i_length = input.int(20, "", inline="source", group=groupFair1, minval=1, tooltip="Determine fair value basis using preferred price source, smoothing method and length.")
- i_trendMode = input.string("Cross", "", options=["Cross", "Direction"], inline="sourcecross", group=groupFair1)
- i_thresholdUpSrc = input.source(low, "▲", inline="sourcecross", group=groupFair1)
- i_thresholdDownSrc = input.source(high, "▼", inline="sourcecross", group=groupFair1, tooltip="Trend modes affect fair value basis color that indicates trend direction. Direction based trends consider direction of the fair value basis, e.g. fair value basis pointing up = uptrend. Cross based trends activate when selected price source has crossed fair value basis (and threshold band, if width set above 0).")
- //Visual elements
- groupVisual = "Visuals"
- i_upColorLine = input.color(color.new(color.white, 50), "Fair value basis ▲ / ▼", group=groupVisual, inline="line")
- i_downColorLine = input.color(color.new(color.red, 50), "", group=groupVisual, inline="line")
- i_upColorFill = input.color(color.new(color.white, 97), "Threshold band ▲ / ▼", group=groupVisual, inline="fill")
- i_downColorFill = input.color(color.new(color.red, 97), "", group=groupVisual, inline="fill")
- extremeDeviationUp = input.color(color.new(color.red, 10), "Deviation hue ▲ / ▼", group=groupVisual, inline="deviation")
- extremeDeviationDown = input.color(color.new(#3c699e, 10), "", group=groupVisual, inline="deviation")
- extremeUpColor = input.color(color.new(color.red, 90), "Deviation bands extreme ▲ / ▼", group=groupVisual, inline="band")
- extremeDownColor = input.color(color.new(#205497, 90), "", group=groupVisual, inline="band")
- deviationBandColor = input.color(color.new(color.white, 95), "Deviation bands", group=groupVisual, inline="dev")
- // Fair value basis
- //User defined source and smoothing
- smoothedValue(source, length) =>
- i_smoothingType == "SMA" ? ta.sma(source, length) : i_smoothingType == "EMA" ? ta.ema(source, length) :
- i_smoothingType == "HMA" ? ta.hma(source, length) : i_smoothingType == "RMA" ? ta.rma(source, length) :
- i_smoothingType == "VWMA" ? ta.vwma(source, length) : i_smoothingType == "Median" ? ta.median(source, length) : ta.wma(source, length)
- //Fair value basis
- fairPriceSmooth = smoothedValue(i_source, i_length)
- // Threshold band
- //Percentage distance between high/low and fair value basis
- lowSpread = low / fairPriceSmooth
- highSpread = high / fairPriceSmooth
- //Calculate low/high deviations from fair value basis
- deviationDown = low < fairPriceSmooth and high > fairPriceSmooth ? lowSpread : na
- deviationUp = low < fairPriceSmooth and high > fairPriceSmooth ? highSpread : na
- //Arrays for deviation values
- var deviationUpList = array.new_float(0, 0)
- var deviationDownList = array.new_float(0, 0)
- //Array size
- deviationUpSize = array.size(deviationUpList)
- deviationDownSize = array.size(deviationDownList)
- //Keeping array sizes fixed
- sizeLimitDev = 1000
- if deviationUpSize > sizeLimitDev
- array.remove(deviationUpList, 0)
- if deviationDownSize > sizeLimitDev
- array.remove(deviationDownList, 0)
- //Populating arrays
- array.push(deviationUpList, deviationUp)
- array.push(deviationDownList, deviationDown)
- //Median deviations
- medianUpDeviation = array.median(deviationUpList)
- medianDownDeviation = array.median(deviationDownList)
- //Defining threshold band based on median deviations
- upperBand = fairPriceSmooth * medianUpDeviation
- lowerBand = fairPriceSmooth * medianDownDeviation
- //Calculating band spread from fair value basis
- bandUpSpread = upperBand - fairPriceSmooth
- bandDownSpread = fairPriceSmooth - lowerBand
- //Threshold band
- upperBandBoosted = fairPriceSmooth + (bandUpSpread * i_boost)
- lowerBandBoosted = fairPriceSmooth - (bandDownSpread * i_boost)
- // Fair value deviation bands
- //Switch for trend logic
- var int dirSwitch = 0
- //Trend rules up/down
- trendRuleUp = i_trendMode == "Cross" ? i_thresholdUpSrc > upperBandBoosted : fairPriceSmooth > fairPriceSmooth[1]
- trendRuleDown = i_trendMode == "Cross" ? i_thresholdDownSrc < lowerBandBoosted : fairPriceSmooth < fairPriceSmooth[1]
- //Trend up/down switch as per user defined rules
- if trendRuleDown
- dirSwitch := -1
- else
- if trendRuleUp
- dirSwitch := 1
- //Percentage spread between fair value basis and OHLC4
- ohlcSpread = ohlc4 / fairPriceSmooth
- //Arrays for spread pivots
- var pivotUps = array.new_float(0, 0)
- var pivotDowns = array.new_float(0, 0)
- //Array size
- pivotUpsSize = array.size(pivotUps)
- pivotDownsSize = array.size(pivotDowns)
- //Keeping array sizes fixed
- sizeLimitBand = 2000
- if array.size(pivotUps) > sizeLimitBand
- array.remove(pivotUps, 0)
- if array.size(pivotDowns) > sizeLimitBand
- array.remove(pivotDowns, 0)
- //Spread pivots
- pivotUp = ta.pivothigh(ohlcSpread, 5, 5)
- pivotDown = ta.pivotlow(ohlcSpread, 5, 5)
- //Populating arrays. To exclude insignficant pivots, arrays are populated only when price is sufficently far away from the mean.
- if low > upperBand
- array.push(pivotUps, pivotUp)
- else
- if high < lowerBand
- array.push(pivotDowns, pivotDown)
- //Calculating median pivots from arrays
- medianPivotUp = array.median(pivotUps)
- medianPivotDown = array.median(pivotDowns)
- //Pivot bands 1x median
- pivotBandUpBase = fairPriceSmooth * (medianPivotUp)
- pivotBandDownBase = fairPriceSmooth * (medianPivotDown)
- //Spread between pivot band and fair value basis
- pBandUpSpread = (pivotBandUpBase - fairPriceSmooth) * i_bandBoost
- pBandDownSpread = (fairPriceSmooth - pivotBandDownBase) * i_bandBoost
- //Pivot bands 1x median
- pivotBandUp = fairPriceSmooth + pBandUpSpread
- pivotBandDown = fairPriceSmooth - pBandDownSpread
- //Pivot bands 2x median
- pivotBandUp2 = pivotBandUp + pBandUpSpread
- pivotBandDown2 = pivotBandDown - pBandDownSpread
- //Pivot bands 3x median
- pivotBandUp3 = pivotBandUp2 + pBandUpSpread
- pivotBandDown3 = pivotBandDown2 - pBandDownSpread
- // Metrics
- //Volume relative to volume moving average (SMA 20)
- volSma = ta.sma(volume, 20)
- volMultiplier = na(volSma) ? 0 : volume / volSma
- //Defining fair value deviation lots
- abovePos2 = (i_source > pivotBandUp2)
- betweenPos1AndPos2 = (i_source > pivotBandUp) and (i_source <= pivotBandUp2)
- betweenMidAndPos1 = (i_source > fairPriceSmooth) and (i_source <= pivotBandUp)
- betweenMidAndNeg1 = (i_source < fairPriceSmooth) and (i_source >= pivotBandDown)
- betweenNeg1AndNeg2 = (i_source < pivotBandDown) and (i_source >= pivotBandDown2)
- belowNeg2 = (i_source < pivotBandDown2)
- //Counters for fair value deviation lots
- var int abovePos2Count = 0
- var float abovePos2Vol = 0
- var int betweenPos1AndPos2Count = 0
- var float betweenPos1AndPos2Vol = 0
- var int betweenMidAndPos1Count = 0
- var float betweenMidAndPos1Vol = 0
- var int betweenMidAndNeg1Count = 0
- var float betweenMidAndNeg1Vol = 0
- var int betweenNeg1AndNeg2Count = 0
- var float betweenNeg1AndNeg2Vol = 0
- var int belowNeg2Count = 0
- var float belowNeg2Vol = 0
- //Populating counters
- if abovePos2
- abovePos2Count += 1
- abovePos2Vol += volMultiplier
- if betweenPos1AndPos2
- betweenPos1AndPos2Count += 1
- betweenPos1AndPos2Vol += volMultiplier
- if betweenMidAndPos1
- betweenMidAndPos1Count += 1
- betweenMidAndPos1Vol += volMultiplier
- if betweenMidAndNeg1
- betweenMidAndNeg1Count += 1
- betweenMidAndNeg1Vol += volMultiplier
- if betweenNeg1AndNeg2
- betweenNeg1AndNeg2Count += 1
- betweenNeg1AndNeg2Vol += volMultiplier
- if belowNeg2
- belowNeg2Count += 1
- belowNeg2Vol += volMultiplier
- //All counts combined
- allCounts = belowNeg2Count + betweenPos1AndPos2Count + betweenMidAndPos1Count + betweenMidAndNeg1Count + betweenNeg1AndNeg2Count + abovePos2Count
- //Time and volume distribution percentages
- above2PosPerc = math.round(abovePos2Count / allCounts, 3) * 100
- above2PosPercVol = math.round(abovePos2Vol / abovePos2Count, 2)
- above1PosPerc = math.round(betweenPos1AndPos2Count / allCounts, 2) * 100
- above1PosPercVol = math.round(betweenPos1AndPos2Vol / betweenPos1AndPos2Count, 2)
- aboveMidPosPerc = math.round(betweenMidAndPos1Count / allCounts, 2) * 100
- aboveMidPosPercVol = math.round(betweenMidAndPos1Vol / betweenMidAndPos1Count, 2)
- belowMidNegPerc = math.round(betweenMidAndNeg1Count / allCounts, 2) * 100
- belowMidNegPercVol = math.round(betweenMidAndNeg1Vol / betweenMidAndNeg1Count, 2)
- below1NegPerc = math.round(betweenNeg1AndNeg2Count / allCounts, 2) * 100
- below1NegPercVol = math.round(betweenNeg1AndNeg2Vol / betweenNeg1AndNeg2Count, 2)
- below2NegPerc = math.round(belowNeg2Count / allCounts, 3) * 100
- below2NegPercVol = math.round(belowNeg2Vol / belowNeg2Count, 2)
- // Table
- //Dividers
- n = "\n"
- l = "⎯⎯"
- vl = "|"
- //Source to string function
- srcString(source) =>
- sourceString = source == open ? "Open" : source == low ? "Low" : source == high ? "High" : source == close ? "Close" :
- source == hl2 ? "HL2" : source == hlc3 ? "HLC3" : source == ohlc4 ? "OHLC4" : "HLCC4"
- //Settings texts
- settingsText = "Settings" + n + "⎯⎯⎯⎯⎯⎯⎯⎯⎯" + n + srcString(i_source) + vl + i_smoothingType + vl + str.tostring(i_length) + n +
- (i_trendMode == "Cross" ? "Cross" + vl + "▲ " + srcString(i_thresholdUpSrc) + vl + "▼ " + srcString(i_thresholdDownSrc) : "Direction") + n +
- "Mid: " + str.tostring(i_boost) + vl + "Out: " + str.tostring(i_bandBoost)
- //Initializing table
- var metricTable = table.new(position = position.top_right, columns = 50, rows = 50, bgcolor = color.new(color.black, 80), border_width = 3, border_color=color.rgb(23, 23, 23))
- //Populating table
- table.cell(table_id = metricTable, column = 1, row = 0, text = "Position" + n + "⎯⎯⎯⎯" + n + "Time" + n + "⎯⎯⎯⎯" + n + "Volume", text_color=color.white, text_halign = text.align_left)
- table.cell(table_id = metricTable, column = 2, row = 0, text = "+3" + n + l + n + str.tostring(above2PosPerc) + "%" + n + l + n + str.tostring(above2PosPercVol) + "x" , text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.red, 70))
- table.cell(table_id = metricTable, column = 3, row = 0, text = "+2" + n + l + n + str.tostring(above1PosPerc) + "%" + n + l + n + str.tostring(above1PosPercVol) + "x", text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.red, 90))
- table.cell(table_id = metricTable, column = 4, row = 0, text = "+1" + n + l + n + str.tostring(aboveMidPosPerc) + "%" + n + l + n + str.tostring(aboveMidPosPercVol) + "x", text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.gray, 80))
- table.cell(table_id = metricTable, column = 6, row = 0, text = "-1 " + n + l + n + str.tostring(belowMidNegPerc) + "%" + n + l + n + str.tostring(belowMidNegPercVol) + "x", text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.gray, 80))
- table.cell(table_id = metricTable, column = 7, row = 0, text = "-2" + n + l + n + str.tostring(below1NegPerc) + "%" + n + l + n + str.tostring(below1NegPercVol) + "x", text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.blue, 90))
- table.cell(table_id = metricTable, column = 8, row = 0, text = "-3" + n + l + n + str.tostring(below2NegPerc) + "%" + n + l + n + str.tostring(below2NegPercVol) + "x", text_color=color.white, text_halign = text.align_center, bgcolor=color.new(color.blue, 70))
- table.cell(table_id = metricTable, column = 10, row = 0, text = settingsText, text_color=color.white, text_halign = text.align_left, text_valign = text.align_top, bgcolor=color.new(#100e48, 80))
- // Plots
- //Colors
- midColorLine = dirSwitch == 1 ? i_upColorLine : i_downColorLine
- midColorBand = dirSwitch == 1 ? i_upColorFill : i_downColorFill
- deviationColor = dirSwitch == 1 ? extremeDeviationUp : extremeDeviationDown
- extremeUpFill = extremeUpColor
- extremeDownFill = extremeDownColor
- //Plots
- pOhlc = plot(ohlc4, "OHLC4", color=color.new(color.white, 100), display=display.all - display.status_line)
- pUpper = plot(upperBandBoosted, "Threshold band up", color=color.new(color.white, 100), style=plot.style_linebr, display=display.all - display.status_line)
- pLower = plot(lowerBandBoosted, "Threshold band down", color=color.new(color.white, 100), style=plot.style_linebr, display=display.all - display.status_line)
- pMid = plot(fairPriceSmooth, "Fair value basis", color=midColorLine, linewidth=1, display=display.all - display.status_line)
- p1U = plot(pivotBandUp, "Fair value deviation band up 1x", color=deviationBandColor, display=display.all - display.status_line)
- p1D = plot(pivotBandDown, "Fair value deviation band down 1x", color=deviationBandColor, display=display.all - display.status_line)
- p2U = plot(pivotBandUp2, "Fair value deviation band up 2x", color=deviationBandColor, display=display.all - display.status_line)
- p2D = plot(pivotBandDown2, "Fair value deviation band down 2x", color=deviationBandColor, display=display.all - display.status_line)
- p3U = plot(pivotBandUp3, "Fair value deviation band up 3x", color=deviationBandColor, display=display.all - display.status_line)
- p3D = plot(pivotBandDown3, "Fair value deviation band down 3x", color=deviationBandColor, display=display.all - display.status_line)
- //Fills
- fill(p3U, p2U, color= extremeUpFill, title="Extreme fair value deviation band up fill")
- fill(p3D, p2D, color= extremeDownFill, title="Extreme fair value deviation band down fill")
- fill(pOhlc, pLower, pivotBandDown3, lowerBandBoosted, extremeDeviationDown, color.new(color.gray, 100), title="Extreme fair value deviation up hue fill")
- fill(pOhlc, pUpper, pivotBandUp3, upperBandBoosted, extremeDeviationUp, color.new(color.gray, 100), title="Extreme fair value deviation down hue fill")
- fill(pUpper, pLower, color= midColorBand, title="Threshold band fill")
- // Metric plots
- barcolor(i_btCheck and belowNeg2 ? color.blue : i_btCheck and betweenNeg1AndNeg2 ? color.rgb(124, 147, 239) :
- i_btCheck and betweenMidAndNeg1 ? color.aqua : i_btCheck and betweenMidAndPos1 ? color.yellow :
- i_btCheck and betweenPos1AndPos2 ? color.orange : i_btCheck and abovePos2 ? color.red : na)
- plot(i_btCheck ? volMultiplier : na, title="Relative volume", color=color.new(color.fuchsia, 100), display=display.all - display.status_line)
- plot(i_btCheck ? allCounts : na, title="All time counts", color=color.new(color.fuchsia, 100), display=display.all - display.status_line)
- plot(i_btCheck ? abovePos2Count : na, title="+3 time count", color=color.new(color.red, 100), display=display.all - display.status_line)
- plot(i_btCheck ? abovePos2Vol : na, title="+3 volume count cumulative", color=color.new(color.red, 100), display=display.all - display.status_line)
- plot(i_btCheck ? abovePos2Vol / abovePos2Count : na, title="+3 volume count average", color=color.new(color.red, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenPos1AndPos2Count : na, title="+2 time count", color=color.new(color.orange, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenPos1AndPos2Vol : na, title="+2 volume count cumulative", color=color.new(color.orange, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenPos1AndPos2Vol / betweenPos1AndPos2Count : na, title="+2 volume count average", color=color.new(color.orange, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndPos1Count : na, title="+1 time count", color=color.new(color.yellow, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndPos1Vol : na, title="+1 volume count cumulative", color=color.new(color.yellow, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndPos1Vol / betweenMidAndPos1Count : na, title="+1 volume count average", color=color.new(color.yellow, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndNeg1Count : na, title="-1 time count", color=color.new(color.aqua, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndNeg1Vol : na, title="-1 volume count cumulative", color=color.new(color.aqua, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenMidAndNeg1Vol / betweenMidAndNeg1Count : na, title="-1 volume count average", color=color.new(color.aqua, 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenNeg1AndNeg2Count : na, title="-2 time count", color=color.new(color.rgb(124, 147, 239) , 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenNeg1AndNeg2Vol : na, title="-2 volume count cumulative", color=color.new(color.rgb(124, 147, 239) , 100), display=display.all - display.status_line)
- plot(i_btCheck ? betweenNeg1AndNeg2Vol / betweenNeg1AndNeg2Count : na, title="-2 volume count average", color=color.new(color.rgb(124, 147, 239) , 100), display=display.all - display.status_line)
- plot(i_btCheck ? belowNeg2Count : na, title="-3 time count", color=color.new(color.blue, 100), display=display.all - display.status_line)
- plot(i_btCheck ? belowNeg2Vol : na, title="-3 volume count cumulative", color=color.new(color.blue, 100), display=display.all - display.status_line)
- plot(i_btCheck ? belowNeg2Vol / belowNeg2Count : na, title="-3 volume count average", color=color.new(color.blue, 100), display=display.all - display.status_line)
- // Alerts
- //Band cross scenarios
- deviation1CrossUp = ta.crossover(high, pivotBandUp)
- deviation2CrossUp = ta.crossover(high, pivotBandUp2)
- deviation3CrossUp = ta.crossover(high, pivotBandUp3)
- deviation1CrossDown = ta.crossunder(low, pivotBandDown)
- deviation2CrossDown = ta.crossunder(low, pivotBandDown2)
- deviation3CrossDown = ta.crossunder(low, pivotBandDown3)
- //Price at fair value basis during trend scenarios
- fairPriceInUp = dirSwitch == 1 and low <= upperBandBoosted
- fairPriceInDown = dirSwitch == -1 and high >= lowerBandBoosted
- //New trend scenarios
- newTrendUp = dirSwitch[1] == -1 and dirSwitch == 1 and barstate.isconfirmed
- newTrendDown = dirSwitch[1] == 1 and dirSwitch == -1 and barstate.isconfirmed
- //Individual
- alertcondition(deviation1CrossUp, "Deviation band +1 cross", "High cross above +1 band detected.")
- alertcondition(deviation2CrossUp, "Deviation band +2 cross", "High cross above +2 band detected.")
- alertcondition(deviation3CrossUp, "Deviation band +3 cross", "High cross above +3 band detected.")
- alertcondition(deviation1CrossDown, "Deviation band -1 cross", "Low cross below -1 band detected.")
- alertcondition(deviation2CrossDown, "Deviation band -2 cross", "Low cross below -2 band detected.")
- alertcondition(deviation3CrossDown, "Deviation band -3 cross", "Low cross below -3 band detected.")
- alertcondition(fairPriceInUp, "Low at fair value basis in an uptrend", "Low at fair value basis in an uptrend detected.")
- alertcondition(fairPriceInDown, "High at fair value basis in a downtrend", "High at fair value basis in a downtrend detected.")
- alertcondition(newTrendUp, "New trend up", "New trend up detected")
- alertcondition(newTrendDown, "New trend down", "New trend down detected")
- //Grouped
- alertcondition(deviation1CrossDown or deviation1CrossUp, "Deviation band -1 cross or +1 cross", "Low cross below -1/high cross above +1 band detected.")
- alertcondition(deviation2CrossDown or deviation2CrossUp, "Deviation band -2 cross or +2 cross", "Low cross below -2/high cross above +2 band detected.")
- alertcondition(deviation3CrossDown or deviation3CrossUp, "Deviation band -3 cross or +3 cross", "Low cross below -3/high cross above +3 band detected.")
- alertcondition(newTrendUp or newTrendDown, "New trend up or new trend down", "New trend up/down detected.")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement