Historical US Bond Yield Curve
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// Editor: © BarefootJoey
// Authors: © longfiat, OpptionsOnly
//
//@version=5
indicator('Historical US Bond Yield Curve', overlay=false)

primarycol = input.color(color.orange, "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(180, "Lookback for Historical Curve 1", minval=1)
lb2 = input.int(360, "Lookback for Historical Curve 2", minval=1)
percentdecimals = input.int(defval=3, title='% Decimals', minval=0, maxval=10)

hline(0, color=color.new(secondcol,50))

symbols = 12
us30y = request.security('US30Y', "D", close) // instead of "D" the original script used timeframe.period
us20y = request.security('US20Y', "D", close)
us10y = request.security('US10Y', "D", close)
us7y = request.security('US07Y', "D", close)
us5y = request.security('US05Y', "D", close)
us3y = request.security('US03Y', "D", close)
us2y = request.security('US02Y', "D", close)
us1y = request.security('US01Y', "D", close)
us6m = request.security('US06MY', "D", close)
us3m = request.security('US03MY', "D", close)
us2m = request.security('US02MY', "D", close)
us1m = request.security('US01MY', "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, us1m, '1M' ,"Current: " + str.tostring(truncatepercent(us1m,percentdecimals)) + (us1m>us1m[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us1m[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us1m[lb2],percentdecimals)) + " %",secondcol)
draw_label(1, us2m, '2M' ,"Current: " + str.tostring(truncatepercent(us2m,percentdecimals)) + (us2m>us2m[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us2m[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us2m[lb2],percentdecimals)) + " %",secondcol)
draw_label(2, us3m, '3M' ,"Current: " + str.tostring(truncatepercent(us3m,percentdecimals)) + (us3m>us3m[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us3m[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us3m[lb2],percentdecimals)) + " %",primarycol)
draw_label(3, us6m, '6M' ,"Current: " + str.tostring(truncatepercent(us6m,percentdecimals)) + (us6m>us6m[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us6m[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us6m[lb2],percentdecimals)) + " %",secondcol)
draw_label(4, us1y, '1Y' ,"Current: " + str.tostring(truncatepercent(us1y,percentdecimals)) + (us1y>us1y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us1y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us1y[lb2],percentdecimals)) + " %",secondcol)
draw_label(5, us2y, '2Y' ,"Current: " + str.tostring(truncatepercent(us2y,percentdecimals)) + (us2y>us2y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us2y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us2y[lb2],percentdecimals)) + " %",primarycol)
draw_label(6, us3y, '3Y' ,"Current: " + str.tostring(truncatepercent(us3y,percentdecimals)) + (us3y>us3y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us3y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us3y[lb2],percentdecimals)) + " %",secondcol)
draw_label(7, us5y, '5Y' ,"Current: " + str.tostring(truncatepercent(us5y,percentdecimals)) + (us5y>us5y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us5y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us5y[lb2],percentdecimals)) + " %",secondcol)
draw_label(8, us7y, '7Y' ,"Current: " + str.tostring(truncatepercent(us7y,percentdecimals)) + (us7y>us7y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us7y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us7y[lb2],percentdecimals)) + " %",secondcol)
draw_label(9, us10y, '10Y' ,"Current: " + str.tostring(truncatepercent(us10y,percentdecimals)) + (us10y>us10y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us10y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us10y[lb2],percentdecimals)) + " %",primarycol)
draw_label(10, us20y, '20Y' ,"Current: " + str.tostring(truncatepercent(us20y,percentdecimals)) + (us20y>us20y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us20y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us20y[lb2],percentdecimals)) + " %",secondcol)
draw_label(11, us30y, '30Y' ,"Current: " + str.tostring(truncatepercent(us30y,percentdecimals)) + (us30y>us30y[1]?" % & rising":" % & falling") + "\n" + str.tostring(lb) + " days ago: " + str.tostring(truncatepercent(us30y[lb],percentdecimals)) + " %\n" + str.tostring(lb2) + " days ago: " + str.tostring(truncatepercent(us30y[lb2],percentdecimals)) + " %",primarycol)

a = draw_line(0, us1m, 1, us2m)
b = draw_line(1, us2m, 2, us3m)
c = draw_line(2, us3m, 3, us6m)
d = draw_line(3, us6m, 4, us1y)
e = draw_line(4, us1y, 5, us2y)
f = draw_line(5, us2y, 6, us3y)
g = draw_line(6, us3y, 7, us5y)
h = draw_line(7, us5y, 8, us7y)
i = draw_line(8, us7y, 9, us10y)
j = draw_line(9, us10y, 10, us20y)
k = draw_line(10, us20y, 11, us30y)

a2 = draw_line2(0, us1m[lb], 1, us2m[lb])
b2 = draw_line2(1, us2m[lb], 2, us3m[lb])
c2 = draw_line2(2, us3m[lb], 3, us6m[lb])
d2 = draw_line2(3, us6m[lb], 4, us1y[lb])
e2 = draw_line2(4, us1y[lb], 5, us2y[lb])
f2 = draw_line2(5, us2y[lb], 6, us3y[lb])
g2 = draw_line2(6, us3y[lb], 7, us5y[lb])
h2 = draw_line2(7, us5y[lb], 8, us7y[lb])
i2 = draw_line2(8, us7y[lb], 9, us10y[lb])
j2 = draw_line2(9, us10y[lb], 10, us20y[lb])
k2 = draw_line2(10, us20y[lb], 11, us30y[lb])

a3 = draw_line3(0, us1m[lb2], 1, us2m[lb2])
b3 = draw_line3(1, us2m[lb2], 2, us3m[lb2])
c3 = draw_line3(2, us3m[lb2], 3, us6m[lb2])
d3 = draw_line3(3, us6m[lb2], 4, us1y[lb2])
e3 = draw_line3(4, us1y[lb2], 5, us2y[lb2])
f3 = draw_line3(5, us2y[lb2], 6, us3y[lb2])
g3 = draw_line3(6, us3y[lb2], 7, us5y[lb2])
h3 = draw_line3(7, us5y[lb2], 8, us7y[lb2])
i3 = draw_line3(8, us7y[lb2], 9, us10y[lb2])
j3 = draw_line3(9, us10y[lb2], 10, us20y[lb2])
k3 = draw_line3(10, us20y[lb2], 11, us30y[lb2])

// Labels
var label langle = na
langle := label.new(pos(11),  y=us30y[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=us30y[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=us30y,  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])

// Important Spreads
us10y3m = (us10y - us3m) * 100
us10y2y = (us10y - us2y) * 100
us10y30y = (us30y - us10y) * 100
last103inv = ta.barssince(us10y3m<0)
last103inv2 = ta.barssince(ta.crossunder(us10y3m,0))
last102inv = ta.barssince(us10y2y<0)
last102inv2 = ta.barssince(ta.crossunder(us10y2y,0))
last1030inv = ta.barssince(us10y30y<0)
last1030inv2 = ta.barssince(ta.crossunder(us10y30y,0))

// ---------------------------- Ticker ---------------------------------------//
// Important Spreads
position = input.string(position.bottom_center, "Important Spreads 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 Ticker = na
Ticker := table.new(position, 1, 4)
if barstate.islast 
    table.cell(Ticker, 0, 0, 
      text = "Important spreads:",
      text_size = size, 
      text_color = secondcol) 
    table.cell(Ticker, 0, 1, 
      text = '10Y/3M: ' + str.tostring(us10y3m) + ' bps ' + (us10y3m>0?'👍':'🚨'), // alt danger 💀 🚧 🚩 🎢
      text_size = size, 
      text_color = primarycol,
      tooltip=str.tostring(last103inv) + " days since last inversion. Inverted for " + (last103inv == 0 ? str.tostring(last103inv2) : "0") + " days.")
    table.cell(Ticker, 0, 2, 
      text = '10Y/2Y: ' + str.tostring(us10y2y) + ' bps ' + (us10y2y>0?'👍':'🚨'),
      text_size = size, 
      text_color = primarycol, 
      tooltip=str.tostring(last102inv) + " days since last inversion. Inverted for " + (last102inv == 0 ? str.tostring(last102inv2) + (last102inv2 >= 5 ? " 💀" : " 😬") : "0") + " days." + (last102inv == 0 ? "\nInvestors expecting recession." : na))
    table.cell(Ticker, 0, 3, 
      text = '10Y/30Y: ' + str.tostring(us10y30y) + ' bps ' + (us10y30y>0?'👍':'🚨'),
      text_size = size, 
      text_color = primarycol, 
      tooltip=str.tostring(last1030inv) + " days since last inversion. Inverted for " + (last1030inv == 0 ? str.tostring(last1030inv2) : "0") + " days." + (last1030inv == 0 ? "\nInvestors expect central-bank policy tightening to lead to inflation & slower economic growth." : na))

cctxtbb = (us10y > us10y[lb]) and (us3m > us3m[lb]) ? "Bear" : (us10y < us10y[lb]) and (us3m < us3m[lb]) ? "Bull" : "Undetermined"
cctxtst = (us10y - us3m) > (us10y[lb] - us3m[lb]) ? "Steepening" : (us10y - us3m) < (us10y[lb] - us3m[lb]) ? "Flattening" : na
cctxttt = ((us10y > us10y[lb]) and (us3m > us3m[lb])) and ((us10y - us3m) < (us10y[lb] - us3m[lb])) ? "Fed tightening. Market expects slower growth. Overheating: Seek commodities, high-yield bonds, & cyclical value. Expect: Rate hikes." : // Bear Flattening = Overheat
  ((us10y > us10y[lb]) and (us3m > us3m[lb])) and ((us10y - us3m) > (us10y[lb] - us3m[lb])) ? "Fed tightening. Market expects faster growth. Recovery: Seek stocks, corporate bonds, & cyclical growth." : //Bear Steepening = Recovery
  ((us10y < us10y[lb]) and (us3m < us3m[lb])) and ((us10y - us3m) > (us10y[lb] - us3m[lb])) ? "Fed loosening. Market expects faster growth. Reflation: Seek bonds, government bonds, & defensive growth. Expect: Rate cuts. " :  // Bull Steepening = Reflation
  ((us10y < us10y[lb]) and (us3m < us3m[lb])) and ((us10y - us3m) < (us10y[lb] - us3m[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)

// EoS made w/ ❤ by @BarefootJoey