BTC Futures Basis
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © daylad

//@version=5
indicator("BTC Futures Basis", precision=2)

cnA = input.symbol("BITMEX:XBTUSDTH22", "A")
cnB = input.symbol("BITMEX:XBTM22", "B")
cnC = input.symbol("BINANCE:BTCH2022", "C")
cnD = input.symbol("BINANCE:BTCM2022", "D")
cnE = input.symbol("DERIBIT:BTC25H22", "E")
cnF = input.symbol("DERIBIT:BTC24M22", "F")
cnG = input.symbol("OKEX:BTCUSDT25H2022", "G")
cnH = input.symbol("OKEX:BTCUSDT24M2022", "H")
cnI = input.symbol("FTX:BTC0325", "I")
cnJ = input.symbol("FTX:BTC0624", "J")
cnK = input.symbol("CME:BTC2!", "K")

spot = request.security("COINBASE:BTCUSD", "1", ohlc4)
ftA = request.security(cnA, "1", ohlc4)
ftB = request.security(cnB, "1", ohlc4)
ftC = request.security(cnC, "1", ohlc4)
ftD = request.security(cnD, "1", ohlc4)
ftE = request.security(cnE, "1", ohlc4)
ftF = request.security(cnF, "1", ohlc4)
ftG = request.security(cnG, "1", ohlc4)
ftH = request.security(cnH, "1", ohlc4)
ftI = request.security(cnI, "1", ohlc4)
ftJ = request.security(cnJ, "1", ohlc4)
ftK = request.security(cnK, "1", ohlc4, gaps = barmerge.gaps_on)

prA = (ftA-spot)/spot
prB = (ftB-spot)/spot
prC = (ftC-spot)/spot
prD = (ftD-spot)/spot
prE = (ftE-spot)/spot
prF = (ftF-spot)/spot
prG = (ftG-spot)/spot
prH = (ftH-spot)/spot
prI = (ftI-spot)/spot
prJ = (ftJ-spot)/spot
prK = (ftK-spot)/spot

var string GP2 = "Display"
string  tableYposInput = input.string("top", "Panel position", inline = "11", options = ["top", "middle", "bottom"], group = GP2)
string  tableXposInput = input.string("right", "", inline = "11", options = ["left", "center", "right"], group = GP2)
var table panel = table.new(tableYposInput + "_" + tableXposInput, 2, 12, frame_color=#000000, frame_width=1)
if barstate.islast
    table.cell(panel, 0, 0, "Contract", bgcolor = #C2C2C2)
    table.cell(panel, 1, 0, "Basis", bgcolor = #9F9F9F)
    table.cell(panel, 0, 1, cnA, bgcolor = #FF5AAF)
    table.cell(panel, 1, 1, str.tostring(prA, "#.##" + "%"), bgcolor = #FF5AAF)
    table.cell(panel, 0, 2, cnB, bgcolor = #9F0162)
    table.cell(panel, 1, 2, str.tostring(prB, "#.##" + "%"), bgcolor = #9F0162)
    table.cell(panel, 0, 3, cnC, bgcolor = #FFC33B)
    table.cell(panel, 1, 3, str.tostring(prC, "#.##" + "%"), bgcolor = #FFC33B)
    table.cell(panel, 0, 4, cnD, bgcolor = #FF6E3A)
    table.cell(panel, 1, 4, str.tostring(prD, "#.##" + "%"), bgcolor = #FF6E3A)
    table.cell(panel, 0, 5, cnE, bgcolor = #00C2F9)
    table.cell(panel, 1, 5, str.tostring(prE, "#.##" + "%"), bgcolor = #00C2F9)
    table.cell(panel, 0, 6, cnF, bgcolor = #008DF9)
    table.cell(panel, 1, 6, str.tostring(prF, "#.##" + "%"), bgcolor = #008DF9)
    table.cell(panel, 0, 7, cnG, bgcolor = #FFC4D4)
    table.cell(panel, 1, 7, str.tostring(prG, "#.##" + "%"), bgcolor = #FFC4D4)
    table.cell(panel, 0, 8, cnH, bgcolor = #A40122)
    table.cell(panel, 1, 8, str.tostring(prH, "#.##" + "%"), bgcolor = #A40122)
    table.cell(panel, 0, 9, cnI, bgcolor = #00FCCF)
    table.cell(panel, 1, 9, str.tostring(prI, "#.##" + "%"), bgcolor = #00FCCF)
    table.cell(panel, 0, 10, cnJ, bgcolor = #00FB1D)
    table.cell(panel, 1, 10, str.tostring(prJ, "#.##" + "%"), bgcolor = #00FB1D)
    table.cell(panel, 0, 11, cnK, bgcolor = #9F24E2)
    table.cell(panel, 1, 11, str.tostring(prK, "#.##" + "%"), bgcolor = #9F24E2)

hline(0, "Backwardation Level", color=color.new(#BCBCBC, 0))
plot(prA*100, "A", color=color.new(#FF5AAF, 0))
plot(prB*100, "B", color=color.new(#9F0162, 0))
plot(prC*100, "C", color=color.new(#FFC33B, 0))
plot(prD*100, "D", color=color.new(#FF6E3A, 0))
plot(prE*100, "E", color=color.new(#00C2F9, 0))
plot(prF*100, "F", color=color.new(#008DF9, 0))
plot(prG*100, "G", color=color.new(#FFC4D4, 0))
plot(prH*100, "H", color=color.new(#A40122, 0))
plot(prI*100, "I", color=color.new(#00FCCF, 0))
plot(prJ*100, "J", color=color.new(#00FB1D, 0))
plot(prK*100, "K", color=color.new(#9F24E2, 0))

//Alert function
lvl = input.float(0.01, "Get an alert when basis crosses below this %:", step=0.01)
bwA = ta.crossover(lvl, prE*100)
bwB = ta.crossover(lvl, prG*100)
bwC = ta.crossover(lvl, prI*100)
if bwA or bwB or bwC
    alert("BACKWARDATION", alert.freq_once_per_bar)
plotchar(bwA, "BWD", "▼", location.top, color=#FF0000, size = size.tiny)