// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//@version=5
//@author=LucF
// Delta Volume Columns Pro [LucF]
// v8 2023.03.27 10:49 — LucF
// This indicator plots either delta volume columns or a volume balance line.
// This code was written using the following:
// • The recommendations from the Pine Script™ User Manual's Style Guide: https://www.tradingview.com/pine-script-docs/en/v5/writing/Style_guide.html
// • The `gradientAdvDecPro()` function from the PineCoders Color Gradient Framework: https://www.tradingview.com/script/hqH4YIFa-Color-Gradient-Framework-PineCoders/
// • The "Time" library from PineCoders to convert a TF string into long form for display: https://www.tradingview.com/script/tyeeNU9I-Time/
// • The "lower_tf" library from PineCoders to manage the LTF user selection: https://www.tradingview.com/script/UxiDkNg0-lower-tf/
// My indicator displaying "Delta Volume Candles" using the same method of calculation is here: https://www.tradingview.com/script/h0yZPTiS-Delta-Volume-Candles-LucF/
// A simpler version of this indicator is my "Delta Volume Columns": https://www.tradingview.com/script/YFBNr8I6-Delta-Volume-Columns-LucF/
// My "Realtime Delta Volume Action" provides realtime volume delta only, calculated from chart updates instead of from a LTF: https://www.tradingview.com/script/Xh8tLDTe-Delta-Volume-Realtime-Action-LucF/
// My "Realtime 5D Profile" also provides realtime volume delta, but presented as a profile: https://www.tradingview.com/script/tNG177SV-Realtime-5D-Profile-LucF/
// For "CVD - Cumulative Volume Delta Candles", see this TradingView script: https://www.tradingview.com/script/NlM312nK-CVD-Cumulative-Volume-Delta-Candles/
// This indicator's page on TV: https://www.tradingview.com/script/F2ylEYOO-Delta-Volume-Columns-Pro-LucF/
indicator("Delta Volume Columns Pro [LucF]", "Delta Volume Columns Pro", format = format.volume, max_labels_count = 500)
import PineCoders/Time/2 as PCtime
import PineCoders/lower_tf/4 as PCltf
import LucF/ta/2 as LucfTa
//#region ———————————————————— Constants
// Colors used as defaults in inputs.
color AQUA = #0080FFff
color BLACK = #000000ff
color BLUE = #013BCAff
color CORAL = #FF8080ff
color GOLD = #CCCC00ff
color GRAY = #808080ff
color GREEN = #008000ff
color LIME = #00FF00ff
color MAROON = #800000ff
color NONE = #FFFFFF00
color ORANGE = #FF8000ff
color PINK = #FF0080ff
color RED = #FF0000ff
color REDLITE = #EF535030
color VIOLET = #AA00FFff
color YELLOW = #FFFF00ff
color WHITE = #FFFFFFff
// Colors used for Data Window and markers.
color GENERAL_UP_HI = LIME
color GENERAL_DN_HI = RED
color GENERAL_UP_LO = GREEN
color GENERAL_DN_LO = MAROON
color GENERAL_NT = GRAY
// Input options.
string ON = "On"
string OFF = "Off"
string ZR0 = "Hide"
string ZR1 = "Combined Balances — Dual Solid Colors (All Bull/All Bear Only)"
string CB0 = "None"
string CB1 = "On Bar Balance"
string CB2 = "Average Balance"
string CB3 = "Momentum Balance"
string CB4 = "Markers Bias"
string CB5 = "CB5"
string CB6 = "Combined Balances"
string CB7 = "Relative Balance"
string CB8 = "Percent Balance"
string CC0 = "None"
string CC1 = "On Bar Balance — Single Color Gradient"
string CC2 = "On Bar Balance — Dual Color Gradient"
string CC3 = "Average Balance — Dual Color Gradient"
string CC4 = "Momentum Balance — Dual Color Gradient"
string CC5 = "Marker Bias — Dual Color Gradient"
string CC6 = "On Bar Balance — Single color, 2 tones"
string CC7 = "On Bar Balance — Dual Solid Colors"
string CC8 = "Combined Balances — Dual Color Gradient"
string CC9 = "Relative Balance — Dual Color Gradient"
string CC10 = "Line vs divergence levels — Dual Color Gradient"
string CC11 = "Percent Balance — Dual Color Gradient"
string TD1 = "Both"
string TD2 = "Longs Only"
string TD3 = "Shorts Only"
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"
string TT_BRITE = "0 (darkest) — 100 (brightest)."
string TT_COL_AVG = "Turning averages on here overrides the Line calculation selection below. The lines' color and fill are controlled in the 'Line' and Line fill' sections below."
string TT_COL_COLOR_CALC = "You can color the top and bottom columns using different calculations."
string TT_LINE_COLOR_CALC = "You can color the line using a different calculation than the one used to calulate the line. This allows you to show more information with the line."
string TT_CHART_BARS = "As calculated in the 'Divergences' section."
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_MARKER1 = "All five balances (On Bar, Average, Momentum, Relative and Percent) are bull/bear."
string TT_MARKER2 = "A double bump is two consecutive up/dn bars with +/‒ volume delta, and rising buy/sell volume above its average."
string TT_MARKER3 = "A divergence is confirmed up/dn when the chosen balance is up/dn on the previous bar when that bar was dn/up, and this bar is up/dn."
string TT_MARKER4 = "Balance shifts occur when the chosen balance crosses into bull/bear territory."
string TT_MARKER5 = "Marker bias shifts occur when it crosses into bull/bear territory."
string TT_ALERT_UP = "This text will replace the alert message for up alerts."
string TT_ALERT_DN = "This text will replace the alert message for down alerts."
string TT_VOL_DISC = "This displays a background to indicate that the volume from the chart's TF does not match the volume at the intrabar TF."
//#endregion
//#region ———————————————————— Inputs
string GP00 = "Columns"
bool columnsOnInput = input.string(OFF, "Columns", inline = "00", options = [OFF, ON], group = GP00) == ON
bool columnsAvgsOnInput = input.string(OFF, "Averages", inline = "00", options = [OFF, ON], group = GP00, tooltip = TT_COL_AVG) == ON
string columnsTopColorCalcInput = input.string(CC6, "Color Tops on", inline = "01", options = [CC6, CC7, CC1, CC3, CC4, CC9, CC11, CC5], group = GP00)
string columnsBotColorCalcInput = input.string(CC6, "Bottoms", inline = "01", options = [CC6, CC7, CC1, CC3, CC4, CC9, CC11, CC5], group = GP00, tooltip = TT_COL_COLOR_CALC)
color columnsUpZeroOneColorInput = input(GREEN, "🡑", inline = "02", group = GP00)
color columnsDnZeroOneColorInput = input(MAROON, "🡓", inline = "02", group = GP00)
float columnsBriteInput = 100 - input.int(100, "🔆", inline = "02", minval = 0, maxval = 100, step = 5, group = GP00, tooltip = TT_BRITE)
string GP01 = "Line"
string balanceLineCalcInput = input.string(CB2, "Calculation", inline = "11", options = [CB0, CB1, CB2, CB3, CB7, CB8, CB4, CB6], group = GP01)
int balanceLineThicknessInput = input.int(1, "Line Thickness", inline = "11", minval = 1, maxval = 16, group = GP01)
string balanceLineColorCalcInput = input.string(CB8, "Color on", inline = "12", options = [CB0, CB1, CB2, CB3, CB7, CB8, CB4, CB6], group = GP01, tooltip = TT_LINE_COLOR_CALC)
color balanceLineUpColorInput = input(WHITE, "🡑", inline = "12", group = GP01)
color balanceLineDnColorInput = input(AQUA, "🡓", inline = "12", group = GP01)
float balanceLineBriteInput = 100 - input.int(100, "🔆", inline = "12", minval = 0, maxval = 100, step = 5, group = GP01, tooltip = TT_BRITE)
string GP02 = "Line Fill"
string balanceFillColorCalcInput = input.string(CC4, "Color on", inline = "21", options = [CC0, CC2, CC3, CC4, CC9, CC11, CC5, CC10], group = GP02)
color balanceFillUpColorInput = input(GOLD, "🡑", inline = "21", group = GP02)
color balanceFillDnColorInput = input(VIOLET, "🡓", inline = "21", group = GP02)
float balanceFillBriteInput = 100 - input.int(100, "🔆", inline = "21", minval = 0, maxval = 100, step = 5, group = GP02, tooltip = TT_BRITE)
string GP03 = "Zero Line"
string zeroLineColorCalcInput = input.string(ZR1, "Display", inline = "31", options = [ZR0, ZR1], group = GP03)
int zeroLineThicknessInput = input.int(1, "Line Thickness", inline = "31", minval = 0, group = GP03)
color zeroLineUpColorInput = input(LIME, "🡑", inline = "32", group = GP03)
color zeroLineDnColorInput = input(RED, "🡓", inline = "32", group = GP03)
float zeroLineBriteInput = 100 - input.int(100, "🔆", inline = "32", minval = 0, maxval = 100, step = 5, group = GP03, tooltip = TT_BRITE)
string GP04 = "Divergences"
string divergenceCalcInput = input.string(CB1, "Detection", inline = "41", options = [CB0, CB1, CB2, CB3, CB7, CB8, CB6], group = GP04)
color divergenceDotColorInput = input(ORANGE, "Dot color", inline = "41", group = GP04)
color divLevelsUpColorInput = input(GREEN, "Levels: 🡑", inline = "42", group = GP04)
color divLevelsDnColorInput = input(MAROON, "🡓", inline = "42", group = GP04)
color divLevelsNtColorInput = input(GRAY, "•", inline = "42", group = GP04)
float divLevelsBriteInput = 100 - input.int(100, "🔆", inline = "42", minval = 0, maxval = 100, step = 5, group = GP04, tooltip = TT_BRITE)
bool filldivLevelsInput = input(false, "Fill expanded levels", inline = "43", group = GP04)
float filldivLevelsBriteInput = 100 - input.int(50, "🔆", inline = "43", minval = 0, maxval = 100, step = 5, group = GP04, tooltip = TT_BRITE)
string GP05 = "Background"
string bgFillColorCalcInput = input.string(CC5, "Color on", inline = "51", options = [CC0, CC2, CC3, CC4, CC9, CC11, CC5], group = GP05)
color bgUpColorInput = input(GRAY, "🡑", inline = "51", group = GP05)
color bgDnColorInput = input(BLUE, "🡓", inline = "51", group = GP05)
float bgFillBriteInput = 100 - input.int(100, "🔆", inline = "51", minval = 0, maxval = 100, step = 5, group = GP05, tooltip = TT_BRITE)
string GP06 = "Chart Bars"
string chartBarsColorCalcInput = input.string(CC9, "Color on", inline = "61", options = [CC0, CC7, CC2, CC3, CC4, CC9, CC11, CC5], group = GP06)
color chartBarsUpColorInput = input(WHITE, "🡑", inline = "61", group = GP06)
color chartBarsDnColorInput = input(AQUA, "🡓", inline = "61", group = GP06)
float chartBarsBriteInput = 100 - input.int(100, "🔆", inline = "61", minval = 0, maxval = 100, step = 5, group = GP06, tooltip = TT_BRITE)
bool hollowOutBodiesInput = input(false, "Empty bodies on decreasing volume", group = GP06)
bool chartBarsShowDivInput = input(true, "Show divergences", inline = "62", group = GP06)
color chartBarsDivColorInput = input(ORANGE, "", inline = "62", group = GP06, tooltip = TT_CHART_BARS)
string GP07 = "Intrabars (LTF)"
string ltfModeInput = input.string(LTF9, "Intrabar precision", options = [LTF1, LTF2, LTF3, LTF4, LTF5, LTF6, LTF7, LTF8, LTF9, LTF10], group = GP07, tooltip = TT_LTF)
bool showInfoBoxInput = input.bool(true, "Show information box", group = GP07, tooltip = TT_LTF_BOX)
string infoBoxSizeInput = input.string("small", "Size ", inline = "72", group = GP07, options = ["tiny", "small", "normal", "large", "huge", "auto"])
string infoBoxYPosInput = input.string("bottom", "↕", inline = "72", group = GP07, options = ["top", "middle", "bottom"])
string infoBoxXPosInput = input.string("right", "↔", inline = "72", group = GP07, options = ["left", "center", "right"])
color infoBoxColorInput = input.color(color.gray, "", inline = "72", group = GP07)
color infoBoxTxtColorInput = input.color(color.black, "T", inline = "72", group = GP07)
string GP08 = "Markers"
string markerDirInput = input.string(TD1, "Direction", inline = "81", options = [TD1, TD2, TD3], group = GP08)
bool showMarker1Input = input(false, "Marker 1: Balances Agreement", group = GP08, tooltip = TT_MARKER1)
bool showMarker2Input = input(false, "Marker 2: Double Bumps", group = GP08, tooltip = TT_MARKER2)
bool showMarker3Input = input(false, "Marker 3: Divergence Confirmations", inline = "82", group = GP08, tooltip = TT_MARKER3)
string marker3ModeInput = input.string(CB1, "On", inline = "82", options = [CB1, CB2, CB3, CB7, CB8, CB6], group = GP08)
bool showMarker4Input = input(false, "Marker 4: Balance Shifts", inline = "83", group = GP08, tooltip = TT_MARKER4)
string marker4ModeInput = input.string(CB2, "On", inline = "83", options = [CB2, CB3, CB7, CB8, CB6], group = GP08)
bool showMarker5Input = input(false, "Marker 5: Markers Bias Shifts", group = GP08, tooltip = TT_MARKER5)
string alertMsgUpInput = input.text_area("", "Up alert message", group = GP08, tooltip = TT_ALERT_UP)
string alertMsgDnInput = input.text_area("", "Down alert message", group = GP08, tooltip = TT_ALERT_DN)
string GP09 = "Periods"
int balAvgPeriodInput = input.int(50, "Average Balance", minval = 2, group = GP09)
int balMomPeriodInput = input.int(14, "Momentum Balance", minval = 2, group = GP09)
int balRelPeriodInput = input.int(14, "Relative Balance", minval = 1, group = GP09)
int balPctPeriodInput = input.int(14, "Percent Balance", minval = 1, group = GP09)
int biasPeriodInput = input.int(14, "Markers Bias", minval = 2, group = GP09)
string GP10 = "Volume Discrepancies"
color bgColorInput = input(REDLITE, "Background Color", inline = "100", group = GP10)
float failureToleranceInput = input.float(1., "Failure Tolerance (%)", inline = "100", minval = 0., maxval = 50., step = 0.25, group = GP10, tooltip = TT_VOL_DISC) / 100
//#endregion
//#region ———————————————————— Functions
// ————— Returns 1 when boolean `cond` is true, 0 if false.
zeroOne(cond) =>
int result = cond ? 1 : 0
// ————— Function appends `sep` and `txt` to `msg` when `cond` is true.
addTextIf(cond, msg, txt, sep) =>
string result = cond ? msg + (msg != "" ? sep : "") + txt : msg
// ————— Function returning a gradient between two bull or two bear colors, depending on whether the source signal is above/below the centerline.
// The gradient is proportional to the current qty of advances/declines of the `source`.
// The count of advances/declines resets to one when the `source` crosses the `center` and is limited by `steps`.
gradientAdvDecPro(source, center, steps, bearWeakColor, bearStrongColor, bullWeakColor, bullStrongColor) =>
// float source : input signal.
// float center : (- ∞ to ∞) centerline used to determine if signal is bullish/bearish.
// float steps : Maximum number of steps in the gradient from the weak color to the strong color.
// color bearWeakColor : bear color at adv/dec qty of 1.
// color bearStrongColor: bear color at adv/dec qty of `steps`.
// color bullWeakColor : bull color at adv/dec qty of 1.
// color bullStrongColor: bull color at adv/dec qty of `steps`.
var float qtyAdvDec = 0.
var float maxSteps = math.max(1, steps)
bool xUp = ta.crossover(source, center)
bool xDn = ta.crossunder(source, center)
float chg = ta.change(source)
bool up = chg > 0
bool dn = chg < 0
bool srcBull = source > center
bool srcBear = source < center
qtyAdvDec :=
srcBull ? xUp ? 1 : up ? math.min(maxSteps, qtyAdvDec + 1) : dn ? math.max(1, qtyAdvDec - 1) : qtyAdvDec :
srcBear ? xDn ? 1 : dn ? math.min(maxSteps, qtyAdvDec + 1) : up ? math.max(1, qtyAdvDec - 1) : qtyAdvDec : qtyAdvDec
var color result = na
result :=
srcBull ? color.from_gradient(qtyAdvDec, 1, maxSteps, bullWeakColor, bullStrongColor) :
srcBear ? color.from_gradient(qtyAdvDec, 1, maxSteps, bearWeakColor, bearStrongColor) : result
// @function Determines if the volume for an intrabar is up or down.
// @returns ([float, float]) A tuple of two values, one of which contains the bar's volume. `upVol` is the positive volume of up bars. `dnVol` is the negative volume of down bars.
// Note that when this function is called with `request.security_lower_tf()` a tuple of float[] arrays will be returned.
upDnIntrabarVolumesByPolarity() =>
float upVol = 0.0
float dnVol = 0.0
switch
// Bar polarity can be determined.
close > open => upVol += volume
close < open => dnVol -= volume
// If not, use price movement since last bar.
close > nz(close[1]) => upVol += volume
close < nz(close[1]) => dnVol -= volume
// If not, use previously known polarity.
nz(upVol[1]) > 0 => upVol += volume
nz(dnVol[1]) < 0 => dnVol -= volume
[upVol, dnVol]
//#endregion
//#region ———————————————————— Calculations
// ————— Calculate DV using LTF intrabars.
// Determine intrabar TF.
string intrabarTf = PCltf.ltf(ltfModeInput, LTF1, LTF2, LTF3, LTF4, LTF5, LTF6, LTF7, LTF8, LTF9, LTF10)
// Fetch two arrays containing one element per intrabar. One array contains up volume values (positive), the other down volume values (negative). Volume for each intrabar is either up or down.
[ltfVolumesUp, ltfVolumesDn] = request.security_lower_tf(syminfo.tickerid, intrabarTf, upDnIntrabarVolumesByPolarity())
// Total up/dn volumes for intrabars.
float ltfVolUp = nz(array.sum(ltfVolumesUp))
float ltfVolDn = nz(array.sum(ltfVolumesDn))
// ———— Intrabar stats
[intrabars, chartBarsCovered, avgIntrabars] = PCltf.ltfStats(ltfVolumesUp)
int chartBars = bar_index + 1
// —————————— ASSEMBLE VOLUME INFORMATION.
// ————— Bar polarity.
bool barUp = ta.rising(close, 1)
bool barDn = ta.falling(close, 1)
// ————— Select between historical and realtime delta calcs. In rt, use user selection.
float volB = ltfVolUp
float volS = - ltfVolDn
float volT = volB + volS
// ————— Column top/bottom.
float barBTop = volB
float barSTop = - volS
// ————— Buy/Sell Balance On Bar (CB1).
bool volUp = ta.rising(volume, 1)
float balBar = volB - volS
bool balBarBull = volB > volS
bool balBarBear = volB < volS
bool balBarDivUp = ta.rising( balBar, 1) and barDn
bool balBarDivDn = ta.falling(balBar, 1) and barUp
bool balBarDiv = balBarDivUp or balBarDivDn
// ————— Buy/Sell Balance Averages (CB2).
float balAvgBMa = ta.ema(volB, balAvgPeriodInput)
float balAvgSMa = ta.ema(volS, balAvgPeriodInput)
float balAvg = balAvgBMa - balAvgSMa
bool balAvgBull = balAvg > 0
bool balAvgBear = balAvg < 0
bool balAvgDivUp = ta.rising( balAvg, 1) and barDn
bool balAvgDivDn = ta.falling(balAvg, 1) and barUp
bool balAvgDiv = balAvgDivUp or balAvgDivDn
// ————— Buy/Sell Balance Momentum (CB3).
float balMomBMa = ta.sma(volB, balAvgPeriodInput * 2)
float balMomSMa = ta.sma(volS, balAvgPeriodInput * 2)
float balMomBMaDelta = balAvgBMa - balMomBMa
float balMomSMaDelta = balAvgSMa - balMomSMa
float balMomDeltaDelta= balMomBMaDelta - balMomSMaDelta
float balMom = ta.rsi(balMomDeltaDelta, balMomPeriodInput) - 50
bool balMomBull = balMom > 0
bool balMomBear = balMom < 0
bool balMomDivUp = ta.rising( balMom, 1) and barDn
bool balMomDivDn = ta.falling(balMom, 1) and barUp
bool balMomDiv = balMomDivUp or balMomDivDn
// ————— Buy/Sell Balance Relative (CB7).
float volUpDelta = math.max(0, volB - balAvgBMa)
float volDnDelta = math.max(0, volS - balAvgSMa)
float balRel = ta.alma(volUpDelta - volDnDelta, balRelPeriodInput, 0.85, 6)
bool balRelBull = balRel > 0
bool balRelBear = balRel < 0
bool balRelDivUp = ta.rising( balRel, 1) and barDn
bool balRelDivDn = ta.falling(balRel, 1) and barUp
bool balRelDiv = balRelDivUp or balRelDivDn
// ————— Buy/Sell Balance Percent (CB8).
float balPct = ta.alma(100 * balBar / volume, balPctPeriodInput, 0.85, 6)
bool balPctBull = balPct > 0
bool balPctBear = balPct < 0
bool balPctDivUp = ta.rising( balPct, 1) and barDn
bool balPctDivDn = ta.falling(balPct, 1) and barUp
bool balPctDiv = balPctDivUp or balPctDivDn
// ————— Combined Balances bull/bear values (CB6).
float balAll = zeroOne(balBarBull) + zeroOne(balAvgBull) + zeroOne(balMomBull) + zeroOne(balRelBull) + zeroOne(balPctBull) - zeroOne(balBarBear) - zeroOne(balAvgBear) - zeroOne(balMomBear) - zeroOne(balRelBear) - zeroOne(balPctBear)
bool balAllBull = balAll > 0
bool balAllBear = balAll < 0
bool balAllBullFull = balAll == 5
bool balAllBearFull = balAll == - 5
bool balAllDivUp = ta.rising( balAll, 1) and barDn
bool balAllDivDn = ta.falling(balAll, 1) and barUp
bool balAllDiv = balAllDivUp or balAllDivDn
// ————— Return divergence as per user-selected detection mode.
f_divUp(divCalc) =>
bool result =
switch divCalc
CB1 => balBarDivUp
CB2 => balAvgDivUp
CB3 => balMomDivUp
CB6 => balAllDivUp
CB7 => balRelDivUp
CB8 => balPctDivUp
=> false
f_divDn(divCalc) =>
bool result =
switch divCalc
CB1 => balBarDivDn
CB2 => balAvgDivDn
CB3 => balMomDivDn
CB6 => balAllDivDn
CB7 => balRelDivDn
CB8 => balPctDivDn
=> false
f_markerSignal(signalCalc) =>
float result =
switch signalCalc
CB2 => balAvg
CB3 => balMom
CB6 => balAll
CB7 => balRel
CB8 => balPct
=> na
// —————————— Marker Calcs
// User-selected marker directions.
bool doLongs = markerDirInput == TD1 or markerDirInput == TD2
bool doShorts = markerDirInput == TD1 or markerDirInput == TD3
// ————— Marker Conditions.
bool bumpUp = barUp and balBarBull and volB > balAvgBMa and ta.rising(volB, 1)
bool bumpDn = barDn and balBarBear and volS > balAvgSMa and ta.rising(volS, 1)
bool divUp = f_divUp(marker3ModeInput)
bool divDn = f_divDn(marker3ModeInput)
bool c1U = balAllBullFull
bool c1D = balAllBearFull
bool c2U = bumpUp[1] and bumpUp[2] and ta.rising(close, 1)[1]
bool c2D = bumpDn[1] and bumpDn[2] and ta.falling(close, 1)[1]
bool c3U = divUp[2] and barUp[1] and not (divUp[1] or divDn[1])
bool c3D = divDn[2] and barDn[1] and not (divUp[1] or divDn[1])
bool c4U = ta.crossover( f_markerSignal(marker4ModeInput), 0)[1]
bool c4D = ta.crossunder(f_markerSignal(marker4ModeInput), 0)[1]
// ————— Marker bias.
float cUps = zeroOne(c1U) + zeroOne(c2U) + zeroOne(c3U) + zeroOne(c4U)
float cDns = zeroOne(c1D) + zeroOne(c2D) + zeroOne(c3D) + zeroOne(c4D)
float balMrk = math.sum(cUps - cDns, biasPeriodInput)
bool balMrkBull = balMrk > 0
bool balMrkBear = balMrk < 0
// Crosses above/below a middle buffer zone.
int buffer = 0
bool c5U = ta.crossover( balMrk, buffer)[1]
bool c5D = ta.crossunder(balMrk, - buffer)[1]
// ————— Assembly.
bool a1U = showMarker1Input and doLongs and c1U
bool a1D = showMarker1Input and doShorts and c1D
bool a2U = showMarker2Input and doLongs and c2U
bool a2D = showMarker2Input and doShorts and c2D
bool a3U = showMarker3Input and doLongs and c3U
bool a3D = showMarker3Input and doShorts and c3D
bool a4U = showMarker4Input and doLongs and c4U
bool a4D = showMarker4Input and doShorts and c4D
bool a5U = showMarker5Input and doLongs and c5U
bool a5D = showMarker5Input and doShorts and c5D
// —————————— User-selected line calculation
if columnsAvgsOnInput
// Dual Buy/Sell Averages are displayed with columns; override user selection of line calc.
balanceLineCalcInput := CB5
float balance =
switch balanceLineCalcInput
CB1 => balBar
CB2 => balAvg
CB3 => balMom
CB4 => balMrk
CB5 => balAvgBMa
CB6 => balAll
CB7 => balRel
CB8 => balPct
=> na
// =>————————— Divergence levels.
bool divLevelsDiv = f_divUp(divergenceCalcInput) or f_divDn(divergenceCalcInput)
// Update the divergence channel.
[divLevelsHi, divLevelsLo, divLevelsHState, divLevelsLState, divLevelsBreached, divLevelsChanged, preBreachUpChanges, preBreachDnChanges] =
LucfTa.divergenceChannel(divLevelsDiv, balance, balance, balance, balance)
bool divLevelsNState = not (divLevelsHState or divLevelsLState)
//#endregion
//#region ———————————————————— Visuals
// ————— Returns signal from coloring mode.
colorModeToSignal(colorMode) =>
float result =
switch colorMode
CC1 => balBar
CC2 => balBar
CC3 => balAvg
CC4 => balMom
CC5 => balMrk
CC8 => balAll
CC9 => balRel
CC10 => math.avg(divLevelsHi, divLevelsLo)
CC11 => balPct
=> na
// —————————— Data Window
plotLocations = display.data_window + display.status_line
plot(volB, "Buy Volume", balBarBull ? GENERAL_UP_HI : GENERAL_UP_LO, display = plotLocations)
plot(volS, "Sell Volume", balBarBear ? GENERAL_DN_HI : GENERAL_DN_LO, display = plotLocations)
plot(volT, "Buy + Sell Volume", balBarBull ? GENERAL_UP_HI : GENERAL_DN_HI, display = plotLocations)
plot(volume, "Total Volume", balBarBull ? GENERAL_UP_HI : GENERAL_DN_HI, display = plotLocations)
plot(balBar, "Balance", balBarBull ? GENERAL_UP_HI : GENERAL_DN_HI, display = plotLocations)
plot(balMrk, "Marker Bias", GENERAL_NT, display = plotLocations)
plot(avgIntrabars, "Average intrabars", GENERAL_NT, display = plotLocations)
plot(intrabars, "Intrabars", GENERAL_NT, display = plotLocations)
plot(chartBarsCovered, "Chart bars covered", GENERAL_NT, display = plotLocations)
plot(chartBars, "Chart bars total", GENERAL_NT, display = plotLocations)
// —————————— Columns
// ————— #1: Build colors.
// Used when a bull/bear gradient coloring mode is selected, so bars can be of bull or bear color.
color barsGradientUpLoColor = color.new(columnsUpZeroOneColorInput, math.max(90, columnsBriteInput))
color barsGradientUpHiColor = color.new(columnsUpZeroOneColorInput, columnsBriteInput)
color barsGradientDnLoColor = color.new(columnsDnZeroOneColorInput, math.max(90, columnsBriteInput))
color barsGradientDnHiColor = color.new(columnsDnZeroOneColorInput, columnsBriteInput)
color barsGradientBColor = columnsTopColorCalcInput == CC7 ? balBarBull ? columnsUpZeroOneColorInput : columnsDnZeroOneColorInput : gradientAdvDecPro(colorModeToSignal(columnsTopColorCalcInput), 0, 8, barsGradientDnLoColor, barsGradientDnHiColor, barsGradientUpLoColor, barsGradientUpHiColor)
color barsGradientSColor = columnsBotColorCalcInput == CC7 ? balBarBull ? columnsUpZeroOneColorInput : columnsDnZeroOneColorInput : gradientAdvDecPro(colorModeToSignal(columnsBotColorCalcInput), 0, 8, barsGradientDnLoColor, barsGradientDnHiColor, barsGradientUpLoColor, barsGradientUpHiColor)
// Selection between hard colors or single color gradient on adv/dec of buy/sell ratio.
color barsSolidUpLoColor = color.new(columnsUpZeroOneColorInput, 50)
color barsSolidUpHiColor = color.new(columnsUpZeroOneColorInput, columnsBriteInput)
color barsSolidDnLoColor = color.new(columnsDnZeroOneColorInput, 50)
color barsSolidDnHiColor = color.new(columnsDnZeroOneColorInput, columnsBriteInput)
color barsBBalBarColor = columnsTopColorCalcInput == CC1 ? gradientAdvDecPro(barBTop, 0, 6, barsGradientDnLoColor, barsGradientDnHiColor, barsGradientUpLoColor, barsGradientUpHiColor) : columnsTopColorCalcInput == CC6 ? balBarBull ? barsSolidUpHiColor : barsSolidUpLoColor : columnsTopColorCalcInput == CC7 ? balBarBull ? barsSolidUpHiColor : barsSolidDnHiColor : na
color barsSBalBarColor = columnsBotColorCalcInput == CC1 ? gradientAdvDecPro(barSTop, 0, 6, barsGradientDnLoColor, barsGradientDnHiColor, barsGradientUpLoColor, barsGradientUpHiColor) : columnsBotColorCalcInput == CC6 ? balBarBull ? barsSolidDnLoColor : barsSolidDnHiColor : columnsBotColorCalcInput == CC7 ? balBarBull ? barsSolidUpHiColor : barsSolidDnHiColor : na
// Final assembly of top/bot/middle column colors.
bool solidColTop = columnsTopColorCalcInput == CC1 or columnsTopColorCalcInput == CC6 or columnsTopColorCalcInput == CC7
bool solidColBot = columnsBotColorCalcInput == CC1 or columnsBotColorCalcInput == CC6 or columnsBotColorCalcInput == CC7
color barsBColor = solidColTop ? barsBBalBarColor : barsGradientBColor
color barsSColor = solidColBot ? barsSBalBarColor : barsGradientSColor
// ————— #2: Plot columns.
// Plot Buy part above.
plotcandle(columnsOnInput ? barBTop : na, columnsOnInput ? 0 : na, columnsOnInput ? barBTop : na, columnsOnInput ? 0 : na, "Column Buy", barsBColor, wickcolor = na, bordercolor = na)
// Plot Sell part below.
plotcandle(columnsOnInput ? barSTop : na, columnsOnInput ? 0 : na, columnsOnInput ? barSTop : na, columnsOnInput ? 0 : na, "Column Sell", barsSColor, wickcolor = na, bordercolor = na)
// —————————— Line
bool signalIsBull =
switch balanceLineColorCalcInput
CB0 => false
CB1 => balBarBull
CB2 => balAvgBull
CB3 => balMomBull
CB4 => balMrkBull
CB6 => balAllBull
CB7 => balRelBull
CB8 => balPctBull
=> na
color balanceLineColor = signalIsBull ? color.new(balanceLineUpColorInput, balanceLineBriteInput) : color.new(balanceLineDnColorInput, balanceLineBriteInput)
balancePlot = plot(balance, "Volume Balance", balanceLineColor, balanceLineThicknessInput)
balanceLoPlot = plot(balanceLineCalcInput == CB5 ? - balAvgSMa : na, "Volume Balance Lo Line for Sell Ma", balanceLineColor, balanceLineThicknessInput)
color zeroLineColor = zeroLineColorCalcInput == ZR1 ? balAllBullFull ? color.new(zeroLineUpColorInput, zeroLineBriteInput) : balAllBearFull ? color.new(zeroLineDnColorInput, zeroLineBriteInput) : na : na
zeroPlot = plot(0, "Zero Line", zeroLineColor, zeroLineThicknessInput, plot.style_circles)
// Normal fill between balance and zero line.
color lineFillUpLoColor = color.new(balanceFillUpColorInput, math.max(90, balanceFillBriteInput))
color lineFillUpHiColor = color.new(balanceFillUpColorInput, balanceFillBriteInput)
color lineFillDnLoColor = color.new(balanceFillDnColorInput, math.max(90, balanceFillBriteInput))
color lineFillDnHiColor = color.new(balanceFillDnColorInput, balanceFillBriteInput)
int lineFillGradientSteps = balanceFillColorCalcInput == CC10 ? 8 : balanceFillColorCalcInput == CC2 ? 4 : 16
float lineFillGradientCenter = balanceFillColorCalcInput != CC10 ? 0 : math.avg(divLevelsHi, divLevelsLo)
float lineFillGradientSignal = balanceFillColorCalcInput != CC10 ? colorModeToSignal(balanceFillColorCalcInput) : balance
color balanceFillColor = gradientAdvDecPro(lineFillGradientSignal, lineFillGradientCenter, lineFillGradientSteps, lineFillDnLoColor, lineFillDnHiColor, lineFillUpLoColor, lineFillUpHiColor)
fill(balancePlot, zeroPlot, balanceLineCalcInput != CB5 ? balanceFillColor : na, title = "Volume Balance Fill")
// When Dual Buy/Sell Averages are plotted, fill between those.
fill(balancePlot, balanceLoPlot, balanceLineCalcInput == CB5 ? balanceFillColor : na, title = "Buy/Sell Averages Fill")
// —————————— Divergences and divergence levels
color divLevelsColor = divLevelsHState ? color.new(divLevelsUpColorInput, divLevelsBriteInput) : divLevelsLState ? color.new(divLevelsDnColorInput, divLevelsBriteInput) : color.new(divLevelsNtColorInput, divLevelsBriteInput)
color divLevelsFillColor = divLevelsHState ? color.new(divLevelsUpColorInput, filldivLevelsBriteInput) : divLevelsLState ? color.new(divLevelsDnColorInput, filldivLevelsBriteInput) : color.new(divLevelsNtColorInput, filldivLevelsBriteInput)
color divColor = divergenceDotColorInput
// Divergence levels.
divLevelsHiPlot = plot(not columnsOnInput and balanceLineCalcInput != CB5 and divergenceCalcInput != CB0 ? divLevelsHi : na, "Divergence Hi Level", divLevelsChanged ? na : divLevelsColor)
divLevelsLoPlot = plot(not columnsOnInput and balanceLineCalcInput != CB5 and divergenceCalcInput != CB0 ? divLevelsLo : na, "Divergence Lo Level", divLevelsChanged ? na : divLevelsColor)
fill(divLevelsHiPlot, divLevelsLoPlot, filldivLevelsInput and not divLevelsChanged ? divLevelsFillColor : na)
// Divergence dots.
plotchar(divergenceCalcInput != CB0 and divLevelsDiv, "Divergence dot", "•", location.top, divColor)
// —————————— Chart bars
color chartBarsUpLoColor = color.new(chartBarsUpColorInput, math.max(90, chartBarsBriteInput))
color chartBarsUpHiColor = color.new(chartBarsUpColorInput, chartBarsBriteInput)
color chartBarsDnLoColor = color.new(chartBarsDnColorInput, math.max(90, chartBarsBriteInput))
color chartBarsDnHiColor = color.new(chartBarsDnColorInput, chartBarsBriteInput)
int chartBarsFillGradientSteps = chartBarsColorCalcInput == CC2 or chartBarsColorCalcInput == CC9 ? 4 : 8
color chartBarsFillColor = chartBarsColorCalcInput == CC7 ? balBarBull ? chartBarsUpHiColor : chartBarsDnHiColor : gradientAdvDecPro(colorModeToSignal(chartBarsColorCalcInput), 0, chartBarsFillGradientSteps, chartBarsDnLoColor, chartBarsDnHiColor, chartBarsUpLoColor, chartBarsUpHiColor)
barcolor(chartBarsShowDivInput and divLevelsDiv ? chartBarsDivColorInput : hollowOutBodiesInput and not volUp ? na : chartBarsFillColor)
// —————————— Background
// Color background on selected signal.
color bgUpLoColor = color.new(bgUpColorInput, math.max(90, bgFillBriteInput))
color bgUpHiColor = color.new(bgUpColorInput, bgFillBriteInput)
color bgDnLoColor = color.new(bgDnColorInput, math.max(90, bgFillBriteInput))
color bgDnHiColor = color.new(bgDnColorInput, bgFillBriteInput)
color bgFillColor = gradientAdvDecPro(colorModeToSignal(bgFillColorCalcInput), 0, 32, bgDnLoColor, bgDnHiColor, bgUpLoColor, bgUpHiColor)
// Color background on total volume discrepancy.
bool totalVolumeDisc = math.abs(volT - volume) / volume > failureToleranceInput
bgcolor(totalVolumeDisc and color.t(bgColorInput) != 100 ? bgColorInput : bgFillColor)
// —————————— Markers
plotshape(a1U, "Marker 1 Up", shape.triangleup, location.bottom, NONE, size = size.tiny, text = "▲\n1", textcolor = GENERAL_UP_HI)
plotshape(a1D, "Marker 1 Dn", shape.triangledown, location.top, NONE, size = size.tiny, text = "1\n▼", textcolor = GENERAL_DN_HI)
plotshape(a2U, "Marker 2 Up", shape.triangleup, location.bottom, NONE, size = size.tiny, text = "▲\n2", textcolor = GENERAL_UP_HI)
plotshape(a2D, "Marker 2 Dn", shape.triangledown, location.top, NONE, size = size.tiny, text = "2\n▼", textcolor = GENERAL_DN_HI)
plotshape(a3U, "Marker 3 Up", shape.triangleup, location.bottom, NONE, size = size.tiny, text = "▲\n3", textcolor = GENERAL_UP_HI)
plotshape(a3D, "Marker 3 Dn", shape.triangledown, location.top, NONE, size = size.tiny, text = "3\n▼", textcolor = GENERAL_DN_HI)
plotshape(a4U, "Marker 4 Up", shape.triangleup, location.bottom, NONE, size = size.tiny, text = "▲\n4", textcolor = GENERAL_UP_HI)
plotshape(a4D, "Marker 4 Dn", shape.triangledown, location.top, NONE, size = size.tiny, text = "4\n▼", textcolor = GENERAL_DN_HI)
plotshape(a5U, "Marker 5 Up", shape.triangleup, location.bottom, NONE, size = size.tiny, text = "▲\n5", textcolor = GENERAL_UP_HI)
plotshape(a5D, "Marker 5 Dn", shape.triangledown, location.top, NONE, size = size.tiny, text = "5\n▼", textcolor = GENERAL_DN_HI)
// —————————— Information box
// 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
//#region ———————————————————— Alerts
// —————Build alert messages.
string alertMsgUp = ""
string alertMsgDn = ""
alertMsgUp := addTextIf(a1U, alertMsgUp, "M1▲", ", ")
alertMsgUp := addTextIf(a2U, alertMsgUp, "M2▲", ", ")
alertMsgUp := addTextIf(a3U, alertMsgUp, "M3▲", ", ")
alertMsgUp := addTextIf(a4U, alertMsgUp, "M4▲", ", ")
alertMsgUp := addTextIf(a5U, alertMsgUp, "M5▲", ", ")
alertMsgDn := addTextIf(a1D, alertMsgDn, "M1▼", ", ")
alertMsgDn := addTextIf(a2D, alertMsgDn, "M2▼", ", ")
alertMsgDn := addTextIf(a3D, alertMsgDn, "M3▼", ", ")
alertMsgDn := addTextIf(a4D, alertMsgDn, "M4▼", ", ")
alertMsgDn := addTextIf(a5D, alertMsgDn, "M5▼", ", ")
// ————— Triger alert if needed.
bool alertUp = alertMsgUp != ""
bool alertDn = alertMsgDn != ""
if alertUp
alert(alertMsgUp == "" ? alertMsgUp : alertMsgUp, alert.freq_once_per_bar)
if alertDn
alert(alertMsgDn == "" ? alertMsgDn : alertMsgDn, alert.freq_once_per_bar)
//#endregion
Overview
Delta Volume Columns Pro calculates volume delta from buy and sell volumes and renders it as colored columns or a balance line. Users select calculation methods for balance such as on bar, average, or momentum balance and configure colors for tops bottoms zero line and fills. Divergence detection marks levels with optional filling of expanded zones. The tool visualizes volume dynamics and momentum shifts with multiple display options for thickness and colors.
The inputs you can change from the gear icon on the chart. Defaults are the author's.
Setting
Type
Default
Variable
Columns
string
OFF
columnsOnInput
Averages
string
OFF
columnsAvgsOnInput
Color Tops on
string
CC6
columnsTopColorCalcInput
Bottoms
string
CC6
columnsBotColorCalcInput
🡑
input
GREEN
columnsUpZeroOneColorInput
🡓
input
MAROON
columnsDnZeroOneColorInput
🔆
int
100
🔆
Calculation
string
CB2
balanceLineCalcInput
Line Thickness
int
1
balanceLineThicknessInput
Color on
string
CB8
balanceLineColorCalcInput
🡑
input
WHITE
balanceLineUpColorInput
🡓
input
AQUA
balanceLineDnColorInput
Show all 40 settings
Setting
Type
Default
Variable
🔆
int
100
🔆
Color on
string
CC4
balanceFillColorCalcInput
🡑
input
GOLD
balanceFillUpColorInput
🡓
input
VIOLET
balanceFillDnColorInput
🔆
int
100
🔆
Display
string
ZR1
zeroLineColorCalcInput
Line Thickness
int
1
zeroLineThicknessInput
🡑
input
LIME
zeroLineUpColorInput
🡓
input
RED
zeroLineDnColorInput
🔆
int
100
🔆
Detection
string
CB1
divergenceCalcInput
Dot color
input
ORANGE
divergenceDotColorInput
Levels: 🡑
input
GREEN
divLevelsUpColorInput
🡓
input
MAROON
divLevelsDnColorInput
•
input
GRAY
divLevelsNtColorInput
🔆
int
100
🔆
Fill expanded levels
bool
false
filldivLevelsInput
🔆
int
50
🔆
Color on
string
CC5
bgFillColorCalcInput
🡑
input
GRAY
bgUpColorInput
🡓
input
BLUE
bgDnColorInput
🔆
int
100
🔆
Color on
string
CC9
chartBarsColorCalcInput
🡑
input
WHITE
chartBarsUpColorInput
🡓
input
AQUA
chartBarsDnColorInput
🔆
int
100
🔆
Empty bodies on decreasing volume
bool
false
hollowOutBodiesInput
Show divergences
bool
true
chartBarsShowDivInput
Third-party code. ForexR did not write, test or endorse this script, and the summary above was generated automatically from its code and README. Indicators describe the past, and a strategy's backtest can look better than live trading, especially when a script repaints. Check it in the Strategy Tester and on a demo account before you rely on it.
Oscillator Suite plots a normalised price oscillator in a separate pane and overlays a signal line that can use SMA, EMA, WMA, HMA, RMA, VWMA, T3, ZEMA, KAMA, or GKEMA. It marks bullish and bearish divergences, a smoothed money-flow series, confluence areas an
This indicator tracks and visualises cryptocurrency derivatives positioning data across major perpetual exchanges, including Binance, BitMEX, and Kraken. It calculates net longs, net shorts, delta, or position ratios and plots them in a separate pane as candle
This indicator plots the Money Flow Index in a separate pane and marks regular and hidden bullish and bearish divergences between price pivots and the oscillator. It builds the index from positive and negative money flow, then finds pivot highs and lows using
This indicator plots a smoothed Z-score of cumulative volume delta in a separate pane, showing whether buying or selling pressure is dominant and when the reading enters extreme zones. It marks spot accumulation, climax or exhaustion, and pivot-based price-vol
This indicator plots a heuristic volume-balance oscillator in a separate pane. It estimates signed volume from the close location inside each bar, multiplies that by volume, and sums the proxy over a rolling window. The sum is scaled between minus one and plus
This indicator is an oscillator suite inspired by Market Cipher B and VuManChu Cipher B that plots in a separate pane. It combines a WaveTrend oscillator, a Money Flow Index (MFI) area, and an RSI indicator with automatic divergence detection. Visual signals h