Volume CVD and Open Interest
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Texmoonbeam

//@version=5
indicator("Volume CVD and Open Interest", overlay = false, max_labels_count=500, max_lines_count=500, max_boxes_count=500, max_bars_back = 5000)


ltf = input.timeframe(defval="1", title='Timeframe used to get Volume data', tooltip="This is the minute timeframe data volume is taken from. There is a maximum of 5000 bars of data, so on higher chart timeframes, you will need to use a higher volume source timeframe like m5, to be able to look back further, e.g there are 1440 minute candles in a daily candle")
lookback = input.int(defval=500, title='Bars to look back', tooltip = "Only used to get the min/max for colour gradient")
source = input.string(defval = "Up/Down Volume (Net)", title = "Source Data", options=["Volume", "Up/Down Volume (Net)", "Up Volume", "Down Volume", "Cumulative Volume", "Cumulative Volume EMA", "Open Interest"])
hot = input.color(defval=color.new(color.yellow,50), title='High')
cold = input.color(defval=color.new(color.red,50), title='Low')
oi = input.symbol("BTCUSDTPERP_OI", title = "Open Interest Ticker")
oicalc = input.string(defval = "Close - Prev Close", title = "Open Interest Calculation", options=["Close - Prev Close", "Close - Open", "Close"], tooltip="As only a single value can be used, you can choose where this value comes from, either the Close value of OI, the Close - Open for that candle, or the Close - previous candle Close")

ResolutionToSec(res)=>
    mins = res == "1" ? 1 :
           res == "3" ? 3 :
           res == "5" ? 5 :
           res == "10" ? 10 :
           res == "15" ? 15 :
           res == "30" ? 30 :
           res == "45" ? 45 :
           res == "60" ? 60 :
           res == "120" ? 120 :
           res == "180" ? 180 :
           res == "240" ? 240 :
           res == "D" or res == "1D" ? 1440 :
           res == "W" or res == "1W" ? 10080 :
           res == "M" or res == "1M" ? 43200 :
           res == "" ? int(str.tonumber(timeframe.period)) : 
           str.substring(timeframe.period, 2, 3) == "S" ? int(str.tonumber(str.substring(timeframe.period, 0, 2)))/60 : int(str.tonumber(res))
    ms = mins * 60 * 1000

bar = ResolutionToSec(timeframe.period) 

upAndDownVolume() =>
    posVol = 0.0
    negVol = 0.0  
    switch
        close >  open     => posVol += volume
        close <  open     => negVol -= volume
        close >= close[1] => posVol += volume
        close <  close[1] => negVol -= volume

    [posVol, negVol]

[upVol, downVol] = request.security_lower_tf(syminfo.tickerid, ltf, upAndDownVolume())
[oiOpen, oiHigh, oiLow, oiClose] = request.security(oi, timeframe.period, [open, high, low, close], ignore_invalid_symbol = true)

var deltac = 0.0
var deltacema = 0.0
var color colD = na

upVolume = array.sum(upVol)
downVolume = array.sum(downVol)
delta = upVolume + downVolume
deltac:= 0 
for v = 0 to 13
    deltac := deltac + delta[v]
deltacema := ta.ema(deltac, 14)
deltahigh = ta.highest(delta, lookback)
deltalow = ta.lowest(delta, lookback)
upvolhigh = ta.highest(upVolume, lookback)
upvollow = ta.lowest(upVolume, lookback)
vollow = ta.lowest(volume, lookback)
volhigh = ta.highest(volume, lookback)
downvolhigh = ta.highest(downVolume, lookback)
downvollow = ta.lowest(downVolume, lookback)
deltachigh = ta.highest(deltac, lookback)
deltaclow = ta.lowest(deltac, lookback)
deltachighema = ta.highest(deltacema, lookback)
deltaclowema = ta.lowest(deltacema, lookback)
oival = oicalc == "Close - Prev Close" ? oiClose - oiClose[1] : oicalc == "Close - Open" ? oiClose - oiOpen : oiClose
oihigh = ta.highest(oival, lookback)
oilow = ta.lowest(oival, lookback)
s = 0.0
sl = 0.0
sh = 0.0
if (source == "Volume")
    s := volume
    sl := vollow
    sh := volhigh
if (source == "Up/Down Volume (Net)")
    s := delta
    sl := deltalow
    sh := deltahigh
if (source == "Up Volume")
    s := upVolume
    sl := upvollow
    sh := upvolhigh
if (source == "Down Volume")
    s := downVolume
    sl := downvollow
    sh := downvolhigh
if (source == "Cumulative Volume")
    s := deltac
    sl := deltaclow
    sh := deltachigh
if (source == "Cumulative Volume EMA")
    s := deltacema
    sl := deltaclowema
    sh := deltachighema
if (source == "Open Interest")
    s := oival
    sl := oilow
    sh := oihigh

col = color.from_gradient(s, sl, sh, cold, hot)
plot(s, title="Source", style = plot.style_columns, color=col)

  
// if (bar_index == last_bar_index)

//     label.new(x=bar_index, y=(oival)*1.01, text = str.tostring(oiClose)+ ' ' +str.tostring(oiClose[1])+' '+str.tostring(oiOpen))

//plot(delta, "V", style = plot.style_columns, color=col)

//plotchar(delta, "delta", "—", location.absolute, color = col, size = size.small)

//label.new(x=timenow, y=high, xloc =  xloc.bar_time, text = str.tostring(deltahigh))

//for x = 0 to n
    //col = color.from_gradient(deltanorm, deltalow, detlahigh, color.yellow, color.red)
    //box.new(left=time, bottom=minp+(step*x), top=minp+(step)+(step*x), right=time+(bar), border_color = color.new(color.white, 100), bgcolor = col, xloc = xloc.bar_time)

// if candle not pivot, store up / down price, add to box array, color as delta, when next candle is higher or lower, delete, otherwise extend
// m = 76
// if (bar_index == last_bar_index)
//     nb = box.new(left=bar_index[m], top = open[m], right=last_bar_index, bottom=close[m], bgcolor = color.new(col[m],20), border_color = color.new(col[m],20))