Volume Price Balance by serkany88
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © serkany88

//@version=5
indicator("Volume Price Balance", overlay=false, precision=2)
import Bjorgum/BjCandlePatterns/4 as cp

//Define function for checking condition in last candles
f_ideal_TimesInLast(_cond, _len) =>  math.sum(_cond ? 1 : 0, _len)
rejectionCandles = input.bool(defval=true, title='Rejection Candle Confluence', group="Comparison", tooltip="Calculates rejection candles based on body size and wicks. This is required to be enabled to show rejection points.")
showRejectionPoints = input.bool(defval=true, title='Show Rejection Points', group="Comparison")
bBLength = input.int(defval=50, title='Length of Last Volume Price Comparison', group="Comparison", tooltip="Overall result lookback length")
smoothing = input.int(defval=20, title='Smoothing', group="Comparison")
bUltraMultiplier = input.float(defval=2.2, title='Ultra High Comparison Multiplier', group="Comparison")
bVeryHighMultiplier = input.float(defval=1.8, title='Very High Comparison Multiplier', group="Comparison")
bHighMultiplier = input.float(defval=1.2, title='High Comparison Multiplier', group="Comparison")

//////////////// Volume Spread Analysis Area ////////////////

lengthVolumeMA = input(defval=20, title='Length of MA applied on Volume', group="VSA Volume")
ratioUltraVolume = input(defval=2.2, title='Ultra High Volume Ratio', group="VSA Volume")
ratioVeryHighVolume = input(defval=1.8, title='Very High Volume Ratio', group="VSA Volume")
ratioHighVolume = input(defval=1.2, title='High Volume Ratio', group="VSA Volume")
ratioNormalVolume = input(defval=0.8, title='Normal Volume Ratio', group="VSA Volume")
ratioLowVolume = input(defval=0.4, title='Low Volume Ratio', group="VSA Volume")
ratioVeryLowVolume = input(defval=0.4, title='Very Low Volume Ratio', group="VSA Volume")

float volumeMA = 0
volumeMA := nz(volumeMA[1]) + (volume - nz(volumeMA[1])) / lengthVolumeMA

ultraHighVolumeMin = volumeMA * ratioUltraVolume
veryHighVolumeMin = volumeMA * ratioVeryHighVolume
highVolumeMin = volumeMA * ratioHighVolume
normalVolumeMin = volumeMA * ratioNormalVolume
lowVolumeMin = volumeMA * ratioLowVolume
veryLowVolumeMin = volumeMA * ratioVeryLowVolume

volUltraHigh = volume >= ultraHighVolumeMin ? true : false
volVeryHigh = volume >= veryHighVolumeMin and volume < ultraHighVolumeMin ? true : false
volHigh = volume >= highVolumeMin and volume < veryHighVolumeMin ? true : false
volNormal = volume >= normalVolumeMin and volume < highVolumeMin ? true : false
volLow = volume >= lowVolumeMin and volume < normalVolumeMin ? true : false
volVeryLow = volume < lowVolumeMin ? true : false

//Vol Last Calculations
lastVolUltraHigh = f_ideal_TimesInLast(volUltraHigh, bBLength)
lastVolVeryHigh = f_ideal_TimesInLast(volVeryHigh, bBLength)
lastVolHigh = f_ideal_TimesInLast(volHigh, bBLength)
lastVolNormal = f_ideal_TimesInLast(volNormal, bBLength)

//////////////// Price Spread Analysis Area ////////////////

lengthPriceMA = input(defval=20, title='Length of MA applied on Price', group="PSA Price")
ratioUltraPrice = input(defval=2.2, title='Ultra High Price Ratio', group="PSA Price")
ratioVeryHighPrice = input(defval=1.8, title='Very High Price Ratio', group="PSA Price")
ratioHighPrice = input(defval=1.2, title='High Price Ratio', group="PSA Price")
ratioNormalPrice = input(defval=0.8, title='Normal Price Ratio', group="PSA Price")
ratioLowPrice = input(defval=0.4, title='Low Price Ratio', group="PSA Price")
ratioVeryLowPrice = input(defval=0.4, title='Very Low Price Ratio', group="PSA Price")

currentP = math.abs(close - open)

float PriceMA = 0
PriceMA := nz(PriceMA[1]) + (currentP - nz(PriceMA[1])) / lengthPriceMA

ultraHighPriceMin = PriceMA * ratioUltraPrice
veryHighPriceMin = PriceMA * ratioVeryHighPrice
highPriceMin = PriceMA * ratioHighPrice
normalPriceMin = PriceMA * ratioNormalPrice
lowPriceMin = PriceMA * ratioLowPrice
veryLowPriceMin = PriceMA * ratioVeryLowPrice

PUltraHigh = currentP >= ultraHighPriceMin ? true : false
PVeryHigh = currentP >= veryHighPriceMin and currentP < ultraHighPriceMin ? true : false
PHigh = currentP >= highPriceMin and currentP < veryHighPriceMin ? true : false
PNormal = currentP >= normalPriceMin and currentP < highPriceMin ? true : false
PLow = currentP >= lowPriceMin and currentP < normalPriceMin ? true : false
PVeryLow = currentP < lowPriceMin ? true : false

/////////////////////////// Other Calculations ////////////////////////
// Rejection Candle Functions
bullishhRejection() => rejectionCandles ? close > open and cp.topWick() > cp.body() * 1.15 : false
bearishRejection() => rejectionCandles ? close < open and cp.bottomWick() > cp.body() * 1.15 : false

//Bullish Definitions
bullishUltraP = close > open and PUltraHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection() == false
bullishVeryHighP = close > open and PVeryHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection() == false
bullishHighP = close > open and PHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection() == false
//bullishNormalP = close > open and PNormal and (volUltraHigh or volVeryHigh)

// Rejection Points Variables
bullishUltraRejection = close > open and PUltraHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection()
bullishVeryHighRejection = close > open and PVeryHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection()
bullishHighRejection = close > open and PHigh and (volUltraHigh or volVeryHigh or volHigh) and bullishhRejection()

// Calculate how many times we've faced certain condition
lastBUltra = f_ideal_TimesInLast(bullishUltraP, bBLength)
lastBVeryHigh = f_ideal_TimesInLast(bullishVeryHighP, bBLength) 
lastBHigh = f_ideal_TimesInLast(bullishHighP, bBLength)

//Bearish Definitions
bearishUltraP = close < open and PUltraHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection() == false
bearishVeryHighP = close < open and PVeryHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection() == false
bearishHighP = close < open and PHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection() == false

// Rejection Points Variables
bearishUltraRejection = close < open and PUltraHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection()
bearishVeryHighRejection = close < open and PVeryHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection()
bearishHighRejection = close < open and PHigh and (volUltraHigh or volVeryHigh or volHigh) and bearishRejection()

lastBearUltra = f_ideal_TimesInLast(bearishUltraP, bBLength)
lastBearVeryHigh = f_ideal_TimesInLast(bearishVeryHighP, bBLength)
lastBearHigh = f_ideal_TimesInLast(bearishHighP, bBLength)

// Define Calculations With Strengths
TotalBullStrength = lastBUltra * bUltraMultiplier + lastBVeryHigh * bVeryHighMultiplier + lastBHigh * bHighMultiplier
TotalBearStrength = lastBearUltra * bUltraMultiplier + lastBearVeryHigh * bVeryHighMultiplier + lastBearHigh * bHighMultiplier

// Draw the Plots and Fills
bullPlot = plot(ta.sma(TotalBullStrength, smoothing), title="Bull Strength", color=color.green, linewidth = 1, style= plot.style_line)
bearPlot = plot(ta.sma(TotalBearStrength, smoothing), title="Bear Strength", color=color.red, linewidth = 1, style= plot.style_line)
fill(plot1 = bullPlot, plot2 = bearPlot, color=ta.sma(TotalBullStrength, smoothing) > ta.sma(TotalBearStrength, smoothing) ? color.new(color.green, 70) : color.new(color.red, 70))

plotshape(showRejectionPoints ? bullishUltraRejection : na, title= "Bullish Ultra Rejection", style= shape.circle, location = location.top, color=color.red, size = size.tiny)
plotshape(showRejectionPoints ? bullishVeryHighRejection : na, title= "Bullish Very High Rejection", style= shape.circle, location = location.top, color=color.white, size = size.tiny)
plotshape(showRejectionPoints ? bullishHighRejection : na, title= "Bullish High Rejection", style= shape.circle, location = location.top, color=color.gray, size = size.tiny)

plotshape(showRejectionPoints ? bearishUltraRejection : na, title= "Bearish Ultra Rejection", style= shape.circle, location = location.bottom, color=color.green, size = size.tiny)
plotshape(showRejectionPoints ? bearishVeryHighRejection : na, title= "Bearish Very High Rejection", style= shape.circle, location = location.bottom, color=color.white, size = size.tiny)
plotshape(showRejectionPoints ? bearishHighRejection : na, title= "Bearish High Rejection", style= shape.circle, location = location.bottom, color=color.gray, size = size.tiny)

//================== Table Status ==================
showStrategyStatus =input.bool(true, "Show Current Status Table", group='Current Status')
statusTableSize =input.string(size.small, "Table Size", options=[size.tiny, size.small, size.normal, size.large, size.huge], group='Current Status')

if showStrategyStatus and barstate.islast
    var toptablesize = statusTableSize
    ////////////// Current Status Table /////////////
    var table strategyStatus = table.new(position.top_right, 5, 3, frame_color = #d1d1d1, bgcolor = #d1d1d1, frame_width = 1,border_width=2)
    table.cell(strategyStatus, 1, 0, text='Bull Strength', bgcolor=#292b2b, text_color=color.white, text_halign=text.align_center, text_valign=text.align_center, text_size=toptablesize)
    table.cell(strategyStatus, 2, 0, text='Bear Strength', bgcolor=#292b2b, text_color=color.white, text_halign=text.align_center, text_valign=text.align_center, text_size=toptablesize)
    table.cell(strategyStatus, 1, 1, text=str.tostring(TotalBullStrength), bgcolor=#292b2b, text_color=color.white, text_halign=text.align_center, text_valign=text.align_center, text_size=toptablesize)
    table.cell(strategyStatus, 2, 1, text=str.tostring(TotalBearStrength), bgcolor=#292b2b, text_color=color.white, text_halign=text.align_center, text_valign=text.align_center, text_size=toptablesize)