Fear & Greed Index (Zeiierman)
// This work is licensed under a Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) https://creativecommons.org/licenses/by-nc-sa/4.0/
// © Zeiierman {

//@version=5
indicator("Fear & Greed Index (Zeiierman)",max_bars_back = 500,max_lines_count = 500,precision = 1)
//~~}

// ~~ Vars {
b        = bar_index
xy       = array.new<chart.point>()
var poly = array.new<polyline>(20,na)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Funcs {
Scales(input) => ((input - ta.min(input)) / (ta.max(input) - ta.min(input))) * 100
Scale(input)  => ((input - ta.lowest(input,100)) / (ta.highest(input,100) - ta.lowest(input,100))) * 100

createZone(color _zoneColor, float _startAngle, float _endAngle) =>
    int leftIndex = math.round((_startAngle + 90.0) / 0.01) 
    int rightIndex = math.round((_endAngle + 90.0) / 0.01) 
    zone = xy.slice(leftIndex, rightIndex)
    zone.push(chart.point.from_index(b, 0))
    poly.pop().delete()
    poly.unshift(polyline.new(zone, false, true, line_color=color.new(_zoneColor, 80), fill_color=color.new(_zoneColor, 50)))
    zone
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Visual {
// Create zones
if barstate.islast
    int x1 = na
    float y1 = na
    for i = -90.0 to 90 by 0.01
        x2 = b + math.round(100 * math.sin(math.toradians(i)))
        y2 = 100 * math.cos(math.toradians(i))
        xy.push(chart.point.from_index(x1, y1))
        xy.push(chart.point.from_index(x2, y2))
        x1 := x2
        y1 := y2
    
    // Color
    colors = array.from(
     color.rgb(255, 0, 0),
     color.rgb(233, 16, 16),
     color.rgb(201, 34, 34),
     color.rgb(197, 51, 51),
     color.rgb(197, 69, 69),
     color.rgb(199, 89, 89),
     color.rgb(227, 113, 113),
     color.rgb(232, 134, 134),
     color.rgb(232, 157, 134),
     color.rgb(255, 185, 80),
     color.rgb(255, 185, 80),
     color.rgb(144, 243, 177),
     color.rgb(144, 243, 177),
     color.rgb(117, 225, 153),
     color.rgb(97, 193, 129),
     color.rgb(79, 189, 116),
     color.rgb(49, 169, 89),
     color.rgb(38, 193, 89),
     color.rgb(21, 224, 88),
     color.rgb(0, 255, 85))
    
    // Loop through zones
    int   zoneCount = 20
    float zoneWidth = 360 / zoneCount

    for i = 0 to zoneCount-1
        startAngle = -90.0 + (i * zoneWidth)
        endAngle = startAngle + zoneWidth
        createZone(colors.get(i), startAngle, endAngle)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Fear & Greed {
//S&P 500 125-day moving average
[spxPrice, spxAverage] = request.security("SPX", "D", [close, ta.sma(close, 125)])
sp125 = Scales(spxPrice - spxAverage)

//STOCK PRICE STRENGTH - 52 week high vs 52 week low
week52 = request.security("NYSE:NYA","W",close-math.avg(ta.highest(high,52),ta.lowest(low,52)))/100
hl52   = Scales(week52)

//STOCK PRICE BREADTH - McClellan
adv  = ta.sma(request.security("ADVN", "D", close),19)
dec  = ta.sma(request.security("DECN", "D", close),19)
mcsi = Scales(ta.sma(adv-dec, 19))

//PUT AND CALL OPTIONS - Put and Call 5-day average
putCallRatio = ta.sma(request.security("USI:PCC", "D", close), 5)
putcall      = Scale(-putCallRatio)

//MARKET VOLATILITY - VIX vs 50day average
vix   = request.security("CBOE:VIX", "D", close)
vixMA = ta.sma(vix, 50)
vix50 = Scale(-(vix-vixMA))

//SAFE HAVEN DEMAND - SPX vs Bonds
stockReturns  = ta.sma(spxPrice - spxPrice[20], 20)
[bond,bond20] = request.security("US10Y", "D", [close,close[20]])
safe = Scales(stockReturns-(bond-bond20))

// JUNK BOND DEMAND - Yield spread: junk bonds vs. investment grade
junkBondYield = request.security("AMEX:JNK", "D", close)
treasuryYield = request.security("US10Y", "D", close)
yieldSpread   = Scale(junkBondYield - treasuryYield)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Arrow {
combined = math.avg(sp125,hl52,vix50,mcsi,putcall,safe,yieldSpread)
new      = combined>50?200-(combined*2):combined*2
loc      = combined>50?b+math.round((combined*2)-100):b-math.round(100-(combined*2))

l1   = line.new(b,0,loc,new,color=chart.fg_color,width=3,style=line.style_arrow_right)
lab1 = label.new(b,0,"",style=label.style_circle,color=chart.fg_color,size=size.small)
lab2 = label.new(loc,new,str.tostring(math.round(combined)),style=loc>b?label.style_label_left:label.style_label_right,color=color(na),textcolor=chart.fg_color,size=size.normal)

(l1[1]).delete()
(lab1[1]).delete()
(lab2[1]).delete()
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}

// ~~ Labels {
EF = label.new(b-100,0,"Extreme Fear",color=color(na),textcolor=color.red,style=label.style_label_right)
EG = label.new(b+100,0,"Extreme Greed",color=color(na),textcolor=color.green,style=label.style_label_left)
F  = label.new(b-65,85,"Fear",color=color(na),textcolor=color.red,style=label.style_label_right)
G  = label.new(b+65,85,"Greed",color=color(na),textcolor=color.green,style=label.style_label_left)
N  = label.new(b,100,"Neutral",color=color(na),textcolor=chart.fg_color) 

(EF[1]).delete()
(EG[1]).delete()
(F[1]).delete()
(G[1]).delete()
(N[1]).delete()
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~}