delta-volume-channels-lucf.pine
Copy
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © LucF
//@version=5
indicator("Delta Volume Channels [LucF]", "DV Channels", true, precision = 6, max_labels_count = 500)
// Delta Volume Channels [LucF]
// v5, 2023.04.16 17:55
// This code was written using the recommendations from the Pine Script™ User Manual's Style Guide:
// https://www.tradingview.com/pine-script-docs/en/v5/writing/Style_guide.html
import PineCoders/Time/2 as PCtime
import PineCoders/lower_tf/4 as PCltf
import LucF/ta/3 as LucfTa
//#region ———————————————————— Constants
// Colors
color LIME = #00FF00ff
color LIME_MD = #00FF0090
color LIME_LT = #00FF0040
color TEAL = #008080ff
color TEAL_MD = #00808090
color TEAL_LT = #00808040
color PINK = #FF0080ff
color PINK_MD = #FF008090
color PINK_LT = #FF008040
color MAROON = #800000ff
color MAROON_MD = #80000090
color MAROON_LT = #80000040
color ORANGE = #c56606ff
color ORANGE_BR = #FF8000ff
color GRAY = #808080ff
color GRAY_MD = #80808090
color GRAY_LT = #80808030
color WHITE = #FFFFFFff
color BLACK = #000000ff
// Reference MAs
string MA01 = "Simple MA"
string MA02 = "Exponential MA"
string MA03 = "Wilder MA"
string MA04 = "Weighted MA"
string MA05 = "Volume-weighted MA"
string MA06 = "Arnaud Legoux MA"
string MA07 = "Hull MA"
string MA08 = "Symmetrically-weighted MA"
// Intrabar precisions
string LTF1 = "Covering most chart bars (least precise)"
string LTF2 = "Covering some chart bars (less precise)"
string LTF3 = "Covering less chart bars (more precise)"
string LTF4 = "Covering few chart bars (very precise)"
string LTF5 = "Covering the least chart bars (most precise)"
string LTF6 = "~12 intrabars per chart bar"
string LTF7 = "~24 intrabars per chart bar"
string LTF8 = "~50 intrabars per chart bar"
string LTF9 = "~100 intrabars per chart bar"
string LTF10 = "~250 intrabars per chart bar"
// Line styles
string STL1 = "Line"
string STL2 = "Circles"
string STL3 = "Crosses"
// Marker DV channel transitions
string ST0 = "None"
string ST1 = "DV channel strong bull state"
string ST2 = "DV channel bull or strong bull state"
string ST3 = "DV channel strong bear state"
string ST4 = "DV channel bear or strong bear state"
// Marker Divergence channel transitions
string SV0 = "None"
string SV1 = "Divergence channel strong bull state"
string SV2 = "Divergence channel bull or strong bull state"
string SV3 = "Divergence channel strong bear state"
string SV4 = "Divergence channel bear or strong bear state"
// Bar color choices
string CB0 = "None"
string CB1 = "On divergences only"
string CB2 = "On divergences and on the state of the DV channel"
string CB3 = "On divergences and on the state of the divergence channel"
string CB4 = "On divergences and on the combined state of both channels"
// Channel level sources
string CH1 = "High and Low"
string CH2 = "Open and Close"
// Channel breach sources
string BR1 = "`low` must breach channel's top, `high` must breach channel's bottom"
string BR2 = "`high` must breach channel's top, `low` must breach channel's bottom"
string BR3 = "Close"
string BR4 = "Open"
string BR5 = "The average of high and low (hl2)"
string BR6 = "The average of high, low and close (hlc3)"
string BR7 = "The average of high, low and two times the close (hlcc4)"
string BR8 = "The average of high, low and close and open (ohlc4)"
// Tooltips
string TT_REF = "Your choices here determine the reference that will be used as the DV channel's baseline.
The MA type and length defined here are also used to calculate the MA of the DV% weights."
string TT_CAP = "This is the maximum number of standard deviations away from the reference line that the DV%-weighted line can extend to.
It limits swings of the DV%-weighted line, keeping the chart's vertical scale within acceptable boundaries."
string TT_RVOL = "In addition to the weight of DV, use the weight of the relative volume for the bar.
This weight is determined using the percentile rank of the bar's volume in the specified number of bars."
string TT_LTF = "Your selection here controls how many intrabars will be analyzed for each chart bar.
The more intrabars you analyze, the more precise the calculations will be,
but the less chart bars will be covered by the indicator's calculations because a maximum of 100K intrabars can be analyzed.\n\n
The first five choices determine the lower timeframe used for intrabars using how much chart coverage you want.
The last five choices allow you to select approximately how many intrabars you want analyzed per chart bar."
string TT_LTF_BOX = "Displays the LTF used and intrabar statistics in a configurable position and color."
string TT_BIAS = "This option enables a guess on the bull/bear bias of the channel before it is breached.
It uses the number of changes of the top/bottom channel levels to determine a bias.
When more changes of the top level occur, the bias is bullish. When more changes of the bottom level occur, the bias is bearish.
\n\n Note that enabling this setting will make the channel's states less reliable."
string TT_COLORS = "'🡑🡑' and '🡓🡓' indicate the colors used for strong bull/bear conditions.
\n'🡑' and '🡓' indicate bull/bear conditions."
string TT_MARKERS = "The conditions you use to determine when markers appear will also be used to trigger alerts created from this script.
\n\nMarkers are non-repainting; they appear on the close of bars."
string TT_BARS = "If the coloring of bars on divergences is active, their body will always be colored in the divergence color, regardless of this checkbox's state."
string TT_DIV = "A divergence occurs when the slope of the reference line does not match that of the DV%-weighted line."
string TT_FILTERS = "The filters are additional conditions that must be true for a marker to appear.
\n\n'Bar polarity' means that the bar's up/dn polarity must match that of the marker.
\n\n'Close-to-close polarity' means that the `close` must be higher than the previous one for an up marker, and vice versa.
\n\n'Bull/bear CCI' means that CCI (using the same source and length as the reference line) must be above/below 0.
\n\n'Rising volume' means the volume of the bar must be higher than that of the previous bar. This condition is the same for up/dn markers.
\n\nThe filter on divergences requires a divergence to have occurred in the last number of bars you specify.
\n\nThe filter on 'Efficient Work' requires its bull/bear state to match the direction of the marker. ('Efficient Work' is one of my indicators).
\n\nAs markers are non-repainting, keep in mind that marker conditions must be true on the bar's close, which is when the marker will appear."
//#endregion
//#region ———————————————————— Inputs
string ltfModeInput = input.string(LTF8, "Intrabar precision", inline = "ltf", options = [LTF1, LTF2, LTF3, LTF4, LTF5, LTF6, LTF7, LTF8, LTF9, LTF10], tooltip = TT_LTF)
string GRP0 = "DV channel"
bool reflLineShowInput = input.bool(false, "Reference line ", group = GRP0, inline = "refLine")
int refLineWidthInput = input.int(1, " Width", group = GRP0, inline = "refLine", minval = 1)
string refLineStyleInput = input.string(STL1, "", group = GRP0, inline = "refLine", options = [STL1, STL2, STL3])
color refLineUpUpColorInput = input.color(LIME, " 🡑🡑", group = GRP0, inline = "refLineColors")
color refLineDnDnColorInput = input.color(PINK, "🡓🡓", group = GRP0, inline = "refLineColors")
color refLineUpColorInput = input.color(TEAL, " 🡑", group = GRP0, inline = "refLineColors")
color refLineDnColorInput = input.color(MAROON, "🡓", group = GRP0, inline = "refLineColors", tooltip = TT_COLORS)
string refTypeInput = input.string(MA06, " ", group = GRP0, inline = "ref", options = [MA01, MA02, MA03, MA04, MA05, MA06, MA07, MA08], tooltip = TT_REF)
float refSourceInput = input.source(close, "", group = GRP0, inline = "ref")
int refLengthInput = input.int(20, " Length", group = GRP0, inline = "ref", minval = 2)
bool dvlLineShowInput = input.bool(false, "DV%-weighted line", group = GRP0, inline = "dvLine")
int dvLineWidthInput = input.int(2, " Width", group = GRP0, inline = "dvLine", minval = 1)
string dvLineStyleInput = input.string(STL1, "", group = GRP0, inline = "dvLine", options = [STL1, STL2, STL3])
color dvLineUpUpColorInput = input.color(LIME, " 🡑🡑", group = GRP0, inline = "dvLineColors")
color dvLineDnDnColorInput = input.color(PINK, "🡓🡓", group = GRP0, inline = "dvLineColors")
color dvLineUpColorInput = input.color(TEAL, " 🡑", group = GRP0, inline = "dvLineColors")
color dvLineDnColorInput = input.color(MAROON, "🡓", group = GRP0, inline = "dvLineColors")
int sigmaCapInput = input.int(5, " Cap (in standard deviations)", group = GRP0, inline = "cap", minval = 1, tooltip = TT_CAP)
bool useRelVolWeightInput = input.string("Use", " ", group = GRP0, inline = "RelVolW", options = ["Use", "Don't use"], tooltip = TT_RVOL) == "Use"
int relVolLookbackInput = input.int(100, "relative volume over n bars", group = GRP0, inline = "RelVolW", minval = 2)
bool dvFillShowInput = input.bool(true, "DV channel", group = GRP0, inline = "dvFill")
color dvFillUpUpColorInput = input.color(LIME_MD, " 🡑🡑", group = GRP0, inline = "dvFill")
color dvFillDnDnColorInput = input.color(PINK_MD, "🡓🡓", group = GRP0, inline = "dvFill")
color dvFillUpColorInput = input.color(TEAL_MD, " 🡑", group = GRP0, inline = "dvFill")
color dvFillDnColorInput = input.color(MAROON_MD, "🡓", group = GRP0, inline = "dvFill")
string GRP1 = "Divergence channel"
bool divLinesShowInput = input.bool(false, "Divergence levels", group = GRP1, inline = "divLines")
int divLinesWidthInput = input.int(1, " Width", group = GRP1, inline = "divLines", minval = 1)
string divLinesStyleInput = input.string(STL1, "", group = GRP1, inline = "divLines", options = [STL1, STL2, STL3])
color divLinesUpUpColorInput = input.color(LIME, " 🡑🡑", group = GRP1, inline = "divLinesColors")
color divLinesDnDnColorInput = input.color(PINK, "🡓🡓", group = GRP1, inline = "divLinesColors")
color divLinesUpColorInput = input.color(TEAL, " 🡑", group = GRP1, inline = "divLinesColors")
color divLinesDnColorInput = input.color(MAROON, "🡓", group = GRP1, inline = "divLinesColors")
color divLinesNtColorInput = input.color(GRAY, "N", group = GRP1, inline = "divLinesColors")
bool divFillShowInput = input.bool(true, "Divergence channel", group = GRP1, inline = "divFill")
color divFillUpUpColorInput = input.color(LIME_MD, " 🡑🡑", group = GRP1, inline = "divFill")
color divFillDnDnColorInput = input.color(PINK_MD, "🡓🡓", group = GRP1, inline = "divFill")
color divFillUpColorInput = input.color(TEAL_MD, " 🡑", group = GRP1, inline = "divFill")
color divFillDnColorInput = input.color(MAROON_MD, "🡓", group = GRP1, inline = "divFill")
color divFillNtColorInput = input.color(GRAY_MD, "N", group = GRP1, inline = "divFill")
string divChannelLevelsInput = input.string(CH1, " Levels are defined using", group = GRP1, options = [CH1, CH2])
string divChannelBreachesInput = input.string(BR1, " Breaches are determined using", group = GRP1, options = [BR1, BR2, BR3, BR4, BR5, BR6, BR7, BR8])
bool divChannelBiasInput = input.string("Off", " Estimate unbreached channel bias", group = GRP1, options = ["On", "Off"], tooltip = TT_BIAS) == "On"
string GRP2 = "Other visuals"
string colorBarModeInput = input.string(CB2, "Bar colors", group = GRP2, inline = "barMode", options = [CB0, CB1, CB2, CB3, CB4])
bool barsEmptyOnDecVolInput = input.bool(false, "Don't color falling volume bars", group = GRP2, inline = "barMode", tooltip = TT_BARS)
color barsUpUpColorInput = input.color(LIME, " 🡑🡑", group = GRP2, inline = "barColors")
color barsDnDnColorInput = input.color(PINK, "🡓🡓", group = GRP2, inline = "barColors")
color barsUpColorInput = input.color(TEAL, "🡑", group = GRP2, inline = "barColors")
color barsDnColorInput = input.color(MAROON, "🡓", group = GRP2, inline = "barColors")
color barsNtColorInput = input.color(GRAY, "N", group = GRP2, inline = "barColors")
color barsDivColorInput = input.color(ORANGE, "D", group = GRP2, inline = "barColors")
bool showCharDivInput = input.bool(false, "Divergence mark", group = GRP2, inline = "divChar")
string charDivInput = input.string("•", "", group = GRP2, inline = "divChar")
color charDivColorInput = input.color(ORANGE, "", group = GRP2, inline = "divChar")
bool charDivAboveInput = input.bool(true, "Above bar", group = GRP2, inline = "divChar", tooltip = TT_DIV)
bool showTooltipsInput = input.bool(false, "Tooltips of raw values", group = GRP2)
bool showInfoBoxInput = input.bool(true, "Information box", group = GRP2, tooltip = TT_LTF_BOX)
string infoBoxSizeInput = input.string("small", " ", group = GRP2, inline = "infoBox", options = ["tiny", "small", "normal", "large", "huge", "auto"])
string infoBoxYPosInput = input.string("bottom", "↕", group = GRP2, inline = "infoBox", options = ["top", "middle", "bottom"])
string infoBoxXPosInput = input.string("left", "↔", group = GRP2, inline = "infoBox", options = ["left", "center", "right"])
color infoBoxColorInput = input.color(GRAY_MD, "", group = GRP2, inline = "infoBox")
color infoBoxTxtColorInput = input.color(BLACK, "T", group = GRP2, inline = "infoBox")
string GRP3 = "Marker/Alert conditions"
string markerUpDvModeInput = input.string(ST0, "Up markers on transitions to ", group = GRP3, inline = "upMarker", options = [ST0, ST1, ST2])
string markerUpDivModeInput = input.string(SV0, "", group = GRP3, inline = "upMarker", options = [SV0, SV1, SV2])
color markerUpColorInput = input.color(ORANGE_BR, "🡑", group = GRP3, inline = "upMarker", tooltip = TT_MARKERS)
string markerDnDvModeInput = input.string(ST0, "Down markers on transitions to", group = GRP3, inline = "dnMarker", options = [ST0, ST3, ST4])
string markerDnDivModeInput = input.string(SV0, "", group = GRP3, inline = "dnMarker", options = [SV0, SV3, SV4])
color markerDnColorInput = input.color(ORANGE_BR, "🡓", group = GRP3, inline = "dnMarker")
bool markerBarPolarityInput = input.bool(false, "Filter on bar polarity ", group = GRP3, inline = "Filters1")
bool markerClosePolarityInput= input.bool(false, "Filter on close-to-close polarity", group = GRP3, inline = "Filters1", tooltip = TT_FILTERS)
bool markerCciStateInput = input.bool(false, "Filter on bull/bear CCI ", group = GRP3, inline = "Filters2")
bool markerRisingVolInput = input.bool(false, "Filter on rising volume", group = GRP3, inline = "Filters2")
bool markerDivInput = input.bool(false, "Filter on divergence in last n bars", group = GRP3, inline = "Filters3")
int markerDivBarsInput = input.int(5, "", group = GRP3, inline = "Filters3", minval = 1)
bool markerEwInput = input.bool(false, "Filter on bull/bear Efficient Work", group = GRP3, inline = "Filters4")
string alertUpMsgInput = input.text_area("▲", "Up alert text", group = GRP3)
string alertDnMsgInput = input.text_area("▼", "Down alert text", group = GRP3)
//#endregion
//#region ———————————————————— Functions
//@function Determines when a state is entered on a bar where the previous state was different.
//@param state (series bool) The state whose transition into must be identified.
//@returns (series bool) `true` on the bar where we transition into the state, `false` otherwise.
transitionTo(series bool state) =>
bool result = (not state[1] and state)
//@function Determines a "plot_style" to be used from a user's input.
//@param state (input string) The user selection string of his line style choice (depends on the `STL1`, `STL2` and `STL3` string constants).
//@returns (plot_style) The `style` named argument required in `plot()`.
lineStyleFromUserInput(userSelection) =>
result = switch userSelection
STL1 => plot.style_line
STL2 => plot.style_circles
STL3 => plot.style_cross
=> plot.style_line
//#endregion
//#region ———————————————————— Calculations
// Lower timeframe (LTF) used to mine intrabars.
var string intrabarTf = PCltf.ltf(ltfModeInput, LTF1, LTF2, LTF3, LTF4, LTF5, LTF6, LTF7, LTF8, LTF9, LTF10)
// Get two arrays, one each for up and dn volumes of intrabars. `dnVolumes` values are negative.
[upVolumes, dnVolumes] = request.security_lower_tf(syminfo.tickerid, intrabarTf, LucfTa.upDnIntrabarVolumesByPolarity())
// Total up/dn volumes for intrabars.
float totalUpVolume = array.sum(upVolumes)
float totalDnVolume = array.sum(dnVolumes)
// Total volume for intrabars.
float intrabarVolume = totalUpVolume - totalDnVolume
// Delta volume
float dv = totalUpVolume + totalDnVolume
// Delta volume percent
float dvPct = (dv / intrabarVolume) * 100
float dvWeight = math.abs(dvPct / 100)
// Relative volume weight
float relVolPctRank = ta.percentrank(intrabarVolume, relVolLookbackInput) / 100.
float relVolumeWeight = na(dv) ? na : useRelVolWeightInput ? relVolPctRank : 1
// Combined weight
float combinedWeight = dvWeight * relVolumeWeight
// MAs of reference source and capped dv%-weighted source.
float weightedSource = refSourceInput + (math.sign(dvPct) * math.min(refSourceInput * combinedWeight, sigmaCapInput * ta.stdev(refSourceInput, refLengthInput)))
float reference = LucfTa.ma(refTypeInput, refSourceInput, refLengthInput)
float dvWeightedRef = LucfTa.ma(refTypeInput, weightedSource, refLengthInput)
// Determine bull/bear and strong bull/bear states of the DV channel.
bool dvChannelBull = dvWeightedRef > reference
bool dvChannelBear = not dvChannelBull
bool dvChannelBullStrong = dvChannelBull and close > reference and ta.rising(reference, 1) and ta.rising(dvWeightedRef, 1)
bool dvChannelBearStrong = dvChannelBear and close < reference and ta.falling(reference, 1) and ta.falling(dvWeightedRef, 1)
// Intrabar stats
[intrabars, chartBarsCovered, avgIntrabars] = PCltf.ltfStats(upVolumes)
float volumeOnAllIntrabars = ta.cum(intrabarVolume)
float allIntrabars = ta.cum(intrabars)
// Error detection
if volumeOnAllIntrabars == 0 and barstate.islast
runtime.error("No volume is provided by the data vendor.")
else if allIntrabars == 0 and barstate.islast
runtime.error("No intrabar information exists at the '" + intrabarTf + "' timeframe.")
// ————— Divergence channel
// Detect divergences between the slope of the reference line and that of the DV-weighted line.
bool divergence = dv != 0 and math.sign(ta.change(reference)) != math.sign(ta.change(dvWeightedRef))
// Level sources
float divChannelHiSrc = divChannelLevelsInput == CH1 ? high : math.max(open, close)
float divChannelLoSrc = divChannelLevelsInput == CH1 ? low : math.min(open, close)
// Breach sources
[divBreachHiSrc, divBreachLoSrc] =
switch divChannelBreachesInput
BR1 => [low, high]
BR2 => [high, low]
BR3 => [close, close]
BR4 => [open, open]
BR5 => [hl2, hl2]
BR6 => [hlc3, hlc3]
BR7 => [hlcc4, hlcc4]
BR8 => [ohlc4, ohlc4]
=> [float(na), float(na)]
// Update the divergence channel.
[divChannelHi, divChannelLo, divChannelBull, divChannelBear, divChannelBreached, newDivChannel, preBreachUpChanges, preBreachDnChanges] =
LucfTa.divergenceChannel(divergence, divChannelHiSrc, divChannelLoSrc, divBreachHiSrc, divBreachLoSrc)
// If needed, take a guess on the state of the channel when it has not yet been breached.
bool preBreachBiasBull = not divChannelBreached and divChannelBiasInput and preBreachUpChanges > preBreachDnChanges
bool preBreachBiasBear = not divChannelBreached and divChannelBiasInput and preBreachUpChanges < preBreachDnChanges
// Strong bull/bear states occur when the divergence channel's bull/bear state matches that of the DV channel.
bool divChannelBullStrong = divChannelBull and dvChannelBullStrong
bool divChannelBearStrong = divChannelBear and dvChannelBearStrong
// ————— Marker filters and triggers
// Bar polarity
bool barUp = close > open
bool barDn = close < open
// Close-to-close polarity
bool closeToCloseUp = ta.change(close) > 0
bool closeToCloseDn = ta.change(close) < 0
// CCI bull/bear
float cciSignal = ta.cci(close, refLengthInput)
bool cciBull = cciSignal > 0
bool cciBear = cciSignal < 0
// RIsing volume
bool risingVolume = ta.change(volume) > 0
// Divergence in last n bars
bool divPresent = ta.barssince(divergence) <= markerDivBarsInput
// Efficient work
float ew = LucfTa.efficientWork(refLengthInput)
bool ewBull = ew > 0
bool ewBear = ew < 0
// Base conditions for markers to appear.
bool upMarkerDvCondition =
switch markerUpDvModeInput
ST1 => transitionTo(dvChannelBullStrong)
ST2 => transitionTo(dvChannelBull) or transitionTo(dvChannelBullStrong)
=> false
bool upMarkerDivCondition =
switch markerUpDivModeInput
SV1 => transitionTo(divChannelBullStrong)
SV2 => transitionTo(divChannelBull) or transitionTo(divChannelBullStrong)
=> false
bool dnMarkerDvCondition =
switch markerDnDvModeInput
ST3 => transitionTo(dvChannelBearStrong)
ST4 => transitionTo(dvChannelBear) or transitionTo(dvChannelBearStrong)
=> false
bool dnMarkerDivCondition =
switch markerDnDivModeInput
SV3 => transitionTo(divChannelBearStrong)
SV4 => transitionTo(divChannelBear) or transitionTo(divChannelBearStrong)
=> false
// Apply filters to base conditions.
bool upMarker = upMarkerDvCondition or upMarkerDivCondition
bool dnMarker = dnMarkerDvCondition or dnMarkerDivCondition
upMarker :=
(markerUpDvModeInput != ST0 or markerUpDivModeInput != SV0) and upMarker and barstate.isconfirmed and
(not markerBarPolarityInput or barUp) and
(not markerClosePolarityInput or closeToCloseUp) and
(not markerCciStateInput or cciBull) and
(not markerRisingVolInput or risingVolume) and
(not markerDivInput or divPresent) and
(not markerEwInput or ewBull)
dnMarker :=
(markerDnDvModeInput != ST0 or markerDnDivModeInput != SV0) and dnMarker and barstate.isconfirmed and
(not markerBarPolarityInput or barDn) and
(not markerClosePolarityInput or closeToCloseDn) and
(not markerCciStateInput or cciBear) and
(not markerRisingVolInput or risingVolume) and
(not markerDivInput or divPresent) and
(not markerEwInput or ewBear)
//#endregion
//#region ———————————————————— Visuals
// ————— DV Channel lines and fill.
// Determine colors.
[refLineColor, dvLineColor, dvFillColor] =
switch
dvChannelBullStrong => [refLineUpUpColorInput, dvLineUpUpColorInput, dvFillUpUpColorInput]
dvChannelBearStrong => [refLineDnDnColorInput, dvLineDnDnColorInput, dvFillDnDnColorInput]
dvChannelBull => [refLineUpColorInput, dvLineUpColorInput, dvFillUpColorInput]
dvChannelBear => [refLineDnColorInput, dvLineDnColorInput, dvFillDnColorInput]
=> [color(na), color(na), color(na)]
color dvColor = dv > 0 ? dvLineUpUpColorInput : dv < 0 ? dvLineDnDnColorInput : color.silver
// Styles for lines.
var refLineStyle = lineStyleFromUserInput(refLineStyleInput)
var dvLineStyle = lineStyleFromUserInput(dvLineStyleInput)
// Plot lines and fill them.
var bool plotDvLineValues = reflLineShowInput or dvlLineShowInput or dvFillShowInput
dvRefPlot = plot(plotDvLineValues ? dvWeightedRef : na, "DV-weighted Reference", dvlLineShowInput ? dvLineColor : na, dvLineWidthInput, dvLineStyle)
refPlot = plot(plotDvLineValues and not na(dv) ? reference : na, "Reference", reflLineShowInput ? refLineColor : na, refLineWidthInput, refLineStyle)
fill(dvRefPlot, refPlot, reference, dvWeightedRef, dvFillShowInput ? dvFillColor : na, dvFillShowInput ? color.new(dvFillColor, 90) : na, "Fill")
// ————— Divergence channel lines and fill.
// Determine colors.
[divLinesColor, divFillColor] =
switch
divChannelBreached =>
switch
divChannelBullStrong => [divLinesUpUpColorInput, divFillUpUpColorInput]
divChannelBearStrong => [divLinesDnDnColorInput, divFillDnDnColorInput]
divChannelBull => [divLinesUpColorInput, divFillUpColorInput]
divChannelBear => [divLinesDnColorInput, divFillDnColorInput]
=> [divLinesNtColorInput, divFillNtColorInput]
=>
switch
divChannelBiasInput and preBreachBiasBull => [divLinesUpColorInput, divFillUpColorInput]
divChannelBiasInput and preBreachBiasBear => [divLinesDnColorInput, divFillDnColorInput]
=> [divLinesNtColorInput, divFillNtColorInput]
// Plot the channel levels and fill.
var bool plotDivLineValues = divLinesShowInput or divFillShowInput
var divLineStyle = lineStyleFromUserInput(divLinesStyleInput)
float divChannelMid = math.avg(divChannelHi, divChannelLo)
divChannelHiPlot = plot(plotDivLineValues ? divChannelHi : na, "Divergence Channel Hi", not newDivChannel and divLinesShowInput ? divLinesColor : na, divLinesWidthInput, divLineStyle)
divChannelLoPlot = plot(plotDivLineValues ? divChannelLo : na, "Divergence Channel Lo", not newDivChannel and divLinesShowInput ? divLinesColor : na, divLinesWidthInput, divLineStyle)
// This midline is used to start/end the two different gradient fills used to fill the divergence channel.
divChannelMidPlot = plot(plotDivLineValues ? divChannelMid : na, "Divergence Channel Mid", na, display = display.none)
// Fill from the middle going up and down.
fill(divChannelHiPlot, divChannelMidPlot, divChannelHi, divChannelMid, not newDivChannel and divFillShowInput ? divFillColor : na, not newDivChannel and divFillShowInput ? color.new(divFillColor, 99) : na)
fill(divChannelMidPlot, divChannelLoPlot, divChannelMid, divChannelLo, not newDivChannel and divFillShowInput ? color.new(divFillColor, 99) : na, not newDivChannel and divFillShowInput ? divFillColor : na)
// ————— Display key values in indicator values and Data Window.
float signedDvWeight = dvPct / 100
float signedCombinedWeight = math.sign(signedDvWeight) * combinedWeight
displayLocations = display.status_line + display.data_window
plot(na, "═════════════════", display = displayLocations)
plot(signedDvWeight, "DV% weight (1=100%)", display = displayLocations, color = dvColor)
plot(relVolumeWeight, "Relative Volume weight", display = displayLocations)
plot(signedCombinedWeight, "Combined weight", display = displayLocations, color = dvColor)
plot(na, "═════════════════", display = displayLocations)
plot(dv, "Volume delta", display = displayLocations, color = dvColor)
plot(totalUpVolume, "Up volume for the bar", display = displayLocations, color = dvLineUpUpColorInput)
plot(totalDnVolume, "Dn volume for the bar", display = displayLocations, color = dvLineDnDnColorInput)
plot(intrabarVolume, "Total intrabar volume", display = displayLocations)
plot(na, "═════════════════", display = displayLocations)
plot(intrabars, "Intrabars in this bar", display = displayLocations)
plot(avgIntrabars, "Average intrabars", display = displayLocations)
plot(chartBarsCovered, "Chart bars covered", display = displayLocations)
plot(bar_index + 1, "Chart bars", display = displayLocations)
// ————— Markers
plotchar(upMarker, "Up Marker", "▲", location.belowbar, markerUpColorInput, size = size.tiny)
plotchar(dnMarker, "Down Marker", "▼", location.abovebar, markerDnColorInput, size = size.tiny)
// ————— Alerts
switch
upMarker => alert(alertUpMsgInput)
dnMarker => alert(alertDnMsgInput)
// ————— Chart bars.
// Color
color barColor =
switch colorBarModeInput
CB0 =>
na
CB1 =>
switch
divergence => barsDivColorInput
CB2 =>
switch
divergence => barsDivColorInput
dvChannelBullStrong => barsUpUpColorInput
dvChannelBearStrong => barsDnDnColorInput
dvChannelBull => barsUpColorInput
dvChannelBear => barsDnColorInput
=> barsNtColorInput
CB3 =>
switch
divergence => barsDivColorInput
divChannelBullStrong => barsUpUpColorInput
divChannelBearStrong => barsDnDnColorInput
divChannelBull => barsUpColorInput
divChannelBear => barsDnColorInput
=> barsNtColorInput
CB4 =>
switch
divergence => barsDivColorInput
dvChannelBullStrong and divChannelBullStrong => barsUpUpColorInput
dvChannelBearStrong and divChannelBearStrong => barsDnDnColorInput
(dvChannelBull or dvChannelBullStrong) and (divChannelBull or divChannelBullStrong) => barsUpColorInput
(dvChannelBear or dvChannelBearStrong) and (divChannelBear or divChannelBearStrong) => barsDnColorInput
=> barsNtColorInput
=> na
// Empty bodies on decreasing chart volume.
if barsEmptyOnDecVolInput and ta.falling(volume, 1) and not divergence
barColor := na
barcolor(barColor)
// ————— Plot character showing divergences.
plotchar(showCharDivInput ? divergence : na, "Divergence character", charDivInput, charDivAboveInput ? location.abovebar : location.belowbar, charDivColorInput, size = size.tiny)
// ————— Tooltips containing bar stats.
if showTooltipsInput
string tooltipText =
"DV = " + str.tostring(totalUpVolume, format.volume) +
" − " + str.tostring(math.abs(totalDnVolume), format.volume) +
" = " + str.tostring(dv, format.volume) +
"\nDV% = " + str.tostring(dv, format.volume) +
" / " + str.tostring(intrabarVolume, format.volume) +
" = " + str.tostring(dvPct, format.percent) +
str.format("\n\nDV weight = {0,number,0.000}\nRelVol weight = {1,number,0.000}\nCombined weight = {2,number,0.000}", signedDvWeight, relVolumeWeight, signedCombinedWeight)
label.new(bar_index, high, " \n \n \n \n \n \n ", style = label.style_none, color = color(na), tooltip = tooltipText)
// ————— Display information box only once on the last historical bar because it doesn't need to update in real time.
// Display information box only once on the last historical bar, instead of on all realtime updates, as when `barstate.islast` is used.
if showInfoBoxInput and barstate.islastconfirmedhistory
var table infoBox = table.new(infoBoxYPosInput + "_" + infoBoxXPosInput, 1, 1)
color infoBoxBgColor = infoBoxColorInput
string txt = str.format(
"Uses intrabars at {0}\nAvg intrabars per chart bar: {1,number,#.##}\nChart bars covered: {2} / {3} ({4,number,percent})",
PCtime.formattedNoOfPeriods(timeframe.in_seconds(intrabarTf) * 1000),
avgIntrabars, chartBarsCovered, bar_index + 1, chartBarsCovered / (bar_index + 1))
if avgIntrabars < 5
txt += "\nThis quantity of intrabars is dangerously small.\nResults will not be as reliable with so few."
infoBoxBgColor := color.red
table.cell(infoBox, 0, 0, txt, text_color = infoBoxTxtColorInput, text_size = infoBoxSizeInput, bgcolor = infoBoxBgColor)
//#endregion
Show all 588 lines