Volume Buoyancy [LucF]
// 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

int MAX_BARS_BACK = 1000
indicator("Volume Buoyancy [LucF]", "Volume Buoyancy", max_bars_back = MAX_BARS_BACK, timeframe = "", timeframe_gaps = false, precision = 2)

// Buoyancy [LucF]
// v3, 2022.11.06 13:05 — LucF

// 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 LucF/ta/2 as LucfTa



//#region ———————————————————— Constants and inputs


// Key levels
float LEVEL_MID = 0.
float LEVEL_HI  = 0.3
float LEVEL_LO  = -0.3

// Colors
color BLUE    = #3179f5
color GRAY    = #434651
color GRAY_LT = #9598a1
color GREEN   = #006200
color LIME    = #00FF00
color MAROON  = #800000
color ORANGE  = #e65100
color PINK    = #FF0080
color YELLOW  = #fbc02d

// 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"

// Bar coloring modes
string CB1 = "Buoyancy line"
string CB2 = "MA line"
string CB3 = "Channel fill"
string CB4 = "MA fill"

// Tooltips
string TT_TARGET    = "This value specifies the number of bars for which volume is added to calculate the target."
string TT_LINE      = "'🡑' and '🡓' indicate bull/bear conditions, which occur when the line is above/below the centerline."
string TT_CHANNEL   = "'🡑' and '🡓' indicate bull/bear conditions, which occur when buoyancy is above/below its MA while not also being above/below the centerline.
  \n\n'🡑🡑' and '🡓🡓' indicate strong bull/bear conditions, which require buoyancy to be above/below its MA, and above/below the centerline."

// Inputs
int     periodInput             = input.int(20,         "Target bars",      inline = "target", minval = 1, maxval = int(MAX_BARS_BACK / 4), tooltip = TT_TARGET)
bool    buoyancyShowInput       = input.bool(true,      "Buoyancy",         inline = "buoyancy")
color   buoyancyUpColorInput    = input.color(GRAY_LT,  "🡑",                inline = "buoyancy")
color   buoyancyDnColorInput    = input.color(GRAY_LT,  "🡓",                inline = "buoyancy", tooltip = TT_LINE)
bool    maShowInput             = input.bool(false,     "MA line",          inline = "ma")
color   maUpColorInput          = input.color(YELLOW,   " 🡑",               inline = "ma")
color   maDnColorInput          = input.color(ORANGE,   "🡓",                inline = "ma")
string  maTypeInput             = input.string(MA06,    "",                 inline = "ma", options = [MA01, MA02, MA03, MA04, MA05, MA06, MA07, MA08])
int     maLengthInput           = input.int(20,         "Length",           inline = "ma", minval = 2, tooltip = TT_LINE)
bool    channelShowInput        = input.bool(true,      "Channel",          inline = "channel")
color   channelUpColorInput     = input.color(GREEN,    "  🡑",              inline = "channel")
color   channelDnColorInput     = input.color(MAROON,   "🡓",                inline = "channel")
color   channelUpUpColorInput   = input.color(LIME,     "🡑🡑",               inline = "channel")
color   channelDnDnColorInput   = input.color(PINK,     "🡓🡓",               inline = "channel", tooltip = TT_CHANNEL)
bool    maFillShowInput         = input.bool(true,      "MA fill",          inline = "maFill")
color   maFillUpColorInput      = input.color(GRAY,     "   🡑",             inline = "maFill")
color   maFillDnColorInput      = input.color(BLUE,     "🡓",                inline = "maFill")
bool    colorBarsInput          = input.bool(false,     "Color chart bars using the color of", inline = "bars")
string  colorBarsModeInput      = input.string(CB3,     "",                 inline = "bars", options = [CB1, CB2, CB3, CB4])
//#endregion



//#region ———————————————————— Calculations


// Stop the indicator on charts with no volume.
if barstate.islast and ta.cum(nz(volume)) == 0
    runtime.error("No volume is provided by the data vendor.")

// Calculate buoyancy and its MA.
float buoyancy = LucfTa.buoyancy(volume, periodInput, MAX_BARS_BACK)
float ma       = LucfTa.ma(maTypeInput, buoyancy, maLengthInput)

// States
bool  maIsBull       = ma       > LEVEL_MID
bool  buoyancyIsBull = buoyancy > LEVEL_MID
bool  channelIsBull  = buoyancy > ma
//#endregion



//#region ———————————————————— Plots


// Plotting colors
color channelColor      = channelIsBull ? buoyancyIsBull ? channelUpUpColorInput : channelUpColorInput : buoyancyIsBull ? channelDnColorInput : channelDnDnColorInput
color buoyancyColor     = buoyancyIsBull ? buoyancyUpColorInput : buoyancyDnColorInput
color maColor           = maIsBull ? maUpColorInput : maDnColorInput
color maChannelTopColor = maIsBull ? maFillUpColorInput : color.new(maFillDnColorInput, 95)
color maChannelBotColor = maIsBull ? color.new(maFillUpColorInput, 95) : maFillDnColorInput

// Plots
buoyancyPlot = plot(buoyancy, "Buoyancy", buoyancyShowInput ? buoyancyColor : na)
maPlot       = plot(ma, "MA", maShowInput ? maColor : na)
zeroPlot     = plot(LEVEL_MID, "Phantom Mid Level", display = display.none)

// Fill the MA channel (the space between the middle level and the MA).
fill(maPlot, zeroPlot, not maFillShowInput ? na : maIsBull ? LEVEL_HI : LEVEL_MID, maIsBull ? LEVEL_MID : LEVEL_LO, maChannelTopColor, maChannelBotColor)

// Fill the buoyancy channel (between the buoyancy line and its MA).
fill(maPlot, buoyancyPlot, ma, not channelShowInput ? na : buoyancy, color.new(channelColor, 70), channelColor)

// Levels
hline(LEVEL_HI,  "High level", buoyancyUpColorInput, hline.style_dotted)
hline(LEVEL_MID, "Mid level",  color.gray, hline.style_dotted)
hline(LEVEL_LO,  "Low level",  buoyancyDnColorInput, hline.style_dotted)

// Color bars
color barColor = 
  switch colorBarsModeInput
    CB1 => buoyancyColor
    CB2 => maColor
    CB3 => channelColor
    CB4 => maIsBull ? maFillUpColorInput : maFillDnColorInput
    => na
barcolor(colorBarsInput ? barColor : na)
//#endregion
