Historical Federal Fund Futures Curve
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Based upon the code of of @ BarefootJoey, @ longfiat, @ OpptionsOnly
// Editor: @BadDerivativesTrader
// @version=5
indicator('Historical Federal Fund Futures Curve', overlay=false)

primarycol = input.color(color.rgb(255, 255, 255), "Primary color")
secondcol = input.color(color.gray, "Secondary color")
width = input(8, 'Spacing between labels', tooltip="Measured in bars")
size = input.string(size.small, "Label Size", options=[size.tiny, size.small, size.normal, size.large, size.huge])
lb = input.int(7, "Lookback for Historical Curve 1", minval=1)
lb2 = input.int(30, "Lookback for Historical Curve 2", minval=1)
percentdecimals = input.int(defval=3, title='% Decimals', minval=0, maxval=10)

symbols = 12
ff_jan = 100 - request.security('ZQF2024', "D", close) // instead of "D" the original script used timeframe.period
ff_dec = 100 - request.security('ZQZ2023', "D", close)
ff_nov = 100 - request.security('ZQX2023', "D", close)
ff_oct = 100 - request.security('ZQV2023', "D", close)
ff_sep = 100 - request.security('ZQU2023', "D", close)
ff_aug = 100 - request.security('ZQQ2023', "D", close)
ff_jul = 100 - request.security('ZQN2023', "D", close)
ff_jun = 100 - request.security('ZQM2023', "D", close)
ff_may = 100 - request.security('ZQK2023', "D", close)
ff_apr = 100 - request.security('ZQJ2023', "D", close)
ff_mar = 100 - request.security('ZQH2023', "D", close)
ff_feb = 100 - request.security('ZQG2023', "D", close)

pos(idx) =>
    bar_index - (symbols - idx) * width

truncatepercent(number, percentdecimals) =>
    factor = math.pow(10, percentdecimals)
    int(number * factor) / factor

draw_label(idx, src, txt, tip, col) =>
    var label la = na
    label.delete(la)
    la := label.new(pos(idx), src, text=txt, color=color.new(color.black,100), style=label.style_label_up, textcolor=col, size=size, tooltip=tip)
    la

draw_line(idx1, src1, idx2, src2) =>
    var line li = na
    line.delete(li)
    li := line.new(pos(idx1), src1, pos(idx2), src2, color=primarycol, width=2)
    li
    
draw_line2(idx1, src1, idx2, src2) =>
    var line li = na
    line.delete(li)
    li := line.new(pos(idx1), src1, pos(idx2), src2, style=line.style_dotted, color=color.new(primarycol,33), width=2)
    li
    
draw_line3(idx1, src1, idx2, src2) =>
    var line li = na
    line.delete(li)
    li := line.new(pos(idx1), src1, pos(idx2), src2, style=line.style_dotted, color=color.new(primarycol,66), width=2)
    li

draw_label(0, ff_feb, 'Feb-23' ,"Current: " + str.tostring(truncatepercent(ff_feb,percentdecimals)) + (ff_feb>ff_feb[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_feb[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_feb[lb2],percentdecimals)) + " %",secondcol)
draw_label(1, ff_mar, 'Mar-23' ,"Current: " + str.tostring(truncatepercent(ff_mar,percentdecimals)) + (ff_mar>ff_mar[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_mar[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_mar[lb2],percentdecimals)) + " %",secondcol)
draw_label(2, ff_apr, 'Apr-23' ,"Current: " + str.tostring(truncatepercent(ff_apr,percentdecimals)) + (ff_apr>ff_apr[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_apr[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_apr[lb2],percentdecimals)) + " %",primarycol)
draw_label(3, ff_may, 'May-23' ,"Current: " + str.tostring(truncatepercent(ff_may,percentdecimals)) + (ff_may>ff_may[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_may[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_may[lb2],percentdecimals)) + " %",secondcol)
draw_label(4, ff_jun, 'Jun-23' ,"Current: " + str.tostring(truncatepercent(ff_jun,percentdecimals)) + (ff_jun>ff_jun[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_jun[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_jun[lb2],percentdecimals)) + " %",secondcol)
draw_label(5, ff_jul, 'Jul-23' ,"Current: " + str.tostring(truncatepercent(ff_jul,percentdecimals)) + (ff_jul>ff_jul[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_jul[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_jul[lb2],percentdecimals)) + " %",primarycol)
draw_label(6, ff_aug, 'Aug-23' ,"Current: " + str.tostring(truncatepercent(ff_aug,percentdecimals)) + (ff_aug>ff_aug[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_aug[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_aug[lb2],percentdecimals)) + " %",secondcol)
draw_label(7, ff_sep, 'Sep-23' ,"Current: " + str.tostring(truncatepercent(ff_sep,percentdecimals)) + (ff_sep>ff_sep[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_sep[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_sep[lb2],percentdecimals)) + " %",secondcol)
draw_label(8, ff_oct, 'Oct-23' ,"Current: " + str.tostring(truncatepercent(ff_oct,percentdecimals)) + (ff_oct>ff_oct[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_oct[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_oct[lb2],percentdecimals)) + " %",secondcol)
draw_label(9, ff_nov, 'Nov-23' ,"Current: " + str.tostring(truncatepercent(ff_nov,percentdecimals)) + (ff_nov>ff_nov[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_nov[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_nov[lb2],percentdecimals)) + " %",primarycol)
draw_label(10, ff_dec, 'Dec-23' ,"Current: " + str.tostring(truncatepercent(ff_dec,percentdecimals)) + (ff_dec>ff_dec[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_dec[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_dec[lb2],percentdecimals)) + " %",secondcol)
draw_label(11, ff_jan, 'Jan-24' ,"Current: " + str.tostring(truncatepercent(ff_jan,percentdecimals)) + (ff_jan>ff_jan[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(ff_jan[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(ff_jan[lb2],percentdecimals)) + " %",primarycol)

a = draw_line(0, ff_feb, 1, ff_mar)
b = draw_line(1, ff_mar, 2, ff_apr)
c = draw_line(2, ff_apr, 3, ff_may)
d = draw_line(3, ff_may, 4, ff_jun)
e = draw_line(4, ff_jun, 5, ff_jul)
f = draw_line(5, ff_jul, 6, ff_aug)
g = draw_line(6, ff_aug, 7, ff_sep)
h = draw_line(7, ff_sep, 8, ff_oct)
i = draw_line(8, ff_oct, 9, ff_nov)
j = draw_line(9, ff_nov, 10, ff_dec)
k = draw_line(10, ff_dec, 11, ff_jan)

a2 = draw_line2(0, ff_feb[lb], 1, ff_mar[lb])
b2 = draw_line2(1, ff_mar[lb], 2, ff_apr[lb])
c2 = draw_line2(2, ff_apr[lb], 3, ff_may[lb])
d2 = draw_line2(3, ff_may[lb], 4, ff_jun[lb])
e2 = draw_line2(4, ff_jun[lb], 5, ff_jul[lb])
f2 = draw_line2(5, ff_jul[lb], 6, ff_aug[lb])
g2 = draw_line2(6, ff_aug[lb], 7, ff_sep[lb])
h2 = draw_line2(7, ff_sep[lb], 8, ff_oct[lb])
i2 = draw_line2(8, ff_oct[lb], 9, ff_nov[lb])
j2 = draw_line2(9, ff_nov[lb], 10, ff_dec[lb])
k2 = draw_line2(10, ff_dec[lb], 11, ff_jan[lb])

a3 = draw_line3(0, ff_feb[lb2], 1, ff_mar[lb2])
b3 = draw_line3(1, ff_mar[lb2], 2, ff_apr[lb2])
c3 = draw_line3(2, ff_apr[lb2], 3, ff_may[lb2])
d3 = draw_line3(3, ff_may[lb2], 4, ff_jun[lb2])
e3 = draw_line3(4, ff_jun[lb2], 5, ff_jul[lb2])
f3 = draw_line3(5, ff_jul[lb2], 6, ff_aug[lb2])
g3 = draw_line3(6, ff_aug[lb2], 7, ff_sep[lb2])
h3 = draw_line3(7, ff_sep[lb2], 8, ff_oct[lb2])
i3 = draw_line3(8, ff_oct[lb2], 9, ff_nov[lb2])
j3 = draw_line3(9, ff_nov[lb2], 10, ff_dec[lb2])
k3 = draw_line3(10, ff_dec[lb2], 11, ff_jan[lb2])

// Labels
var label langle = na
langle := label.new(pos(11),  y=ff_jan[lb],  size=size.small, text=str.tostring(lb,"#") + " day", color=color.new(color.white,100), style=label.style_label_left, textcolor=color.new(primarycol,33)) 
label.delete(langle[1])

var label langle2 = na
langle2 := label.new(pos(11),  y=ff_jan[lb2],  size=size.small, text=str.tostring(lb2,"#") + " day", color=color.new(color.white,100), style=label.style_label_left, textcolor=color.new(primarycol,66)) 
label.delete(langle2[1])

var label langle3 = na
langle3 := label.new(pos(11),  y=ff_jan,  size=size.small, text="Current", color=color.new(color.white,100), style=label.style_label_left, textcolor=color.new(primarycol,0)) 
label.delete(langle3[1])

cctxtbb = (ff_nov > ff_nov[lb]) and (ff_apr > ff_apr[lb]) ? "Bear" : (ff_nov < ff_nov[lb]) and (ff_apr < ff_apr[lb]) ? "Bull" : "Undetermined"
cctxtst = (ff_nov - ff_apr) > (ff_nov[lb] - ff_apr[lb]) ? "Steepening" : (ff_nov - ff_apr) < (ff_nov[lb] - ff_apr[lb]) ? "Flattening" : na
cctxttt = ((ff_nov > ff_nov[lb]) and (ff_apr > ff_apr[lb])) and ((ff_nov - ff_apr) < (ff_nov[lb] - ff_apr[lb])) ? "Fed tightening. Market expects slower growth. Overheating: Seek commodities, high-yield bonds, & cyclical value. Expect: Rate hikes." : // Bear Flattening = Overheat
  ((ff_nov > ff_nov[lb]) and (ff_apr > ff_apr[lb])) and ((ff_nov - ff_apr) > (ff_nov[lb] - ff_apr[lb])) ? "Fed tightening. Market expects faster growth. Recovery: Seek stocks, corporate bonds, & cyclical growth." : //Bear Steepening = Recovery
  ((ff_nov < ff_nov[lb]) and (ff_apr < ff_apr[lb])) and ((ff_nov - ff_apr) > (ff_nov[lb] - ff_apr[lb])) ? "Fed loosening. Market expects faster growth. Reflation: Seek bonds, government bonds, & defensive growth. Expect: Rate cuts. " :  // Bull Steepening = Reflation
  ((ff_nov < ff_nov[lb]) and (ff_apr < ff_apr[lb])) and ((ff_nov - ff_apr) < (ff_nov[lb] - ff_apr[lb])) ? "Fed loosening. Market expects slower growth. Stagflation: Seek cash (money market), inflation-linked bonds, & defensive value." : // Bull Flattening = Stagflation
  na

position2 = input.string(position.top_center, "Curve Analysis Info Panel Position", [position.top_right, position.middle_right, position.bottom_right, position.bottom_left, position.middle_left, position.top_left, position.bottom_center, position.top_center]) //, group=grtk)
var table Ticker3 = na
Ticker3 := table.new(position2, 1, 3)
if barstate.islast
    table.cell(Ticker3, 0, 0, 
      text = cctxtbb + " " + cctxtst,
      text_size = size, 
      text_color = cctxtbb == "Bear" ? color.new(color.red,20) :  cctxtbb == "Bull" ? color.new(color.green,20) : color.new(color.gray,20),
      tooltip = cctxttt)