Crypto/DXY Scoring
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © KioseffTrading

//@version=5
indicator("Crypto/DXY Scoring", precision = 10, overlay = false, format = format.inherit, max_labels_count = 500, max_boxes_count = 500, max_lines_count = 500)

import HeWhoMustNotBeNamed/arraymethods/1
import RicardoSantos/MathOperator/2


sym   = input.symbol(defval = "DXY",           title = "Comparison Symbol")
back  = input.int   (defval = 3000,            title = "Bars Back", minval = 5)
zlook = input.int   (defval = 20,              title = "General Z-Score Lookback (Affects Columns)", minval = 10)
lenco = input.int   (defval = 100,             title = "Comparator Lookback", minval = 10)
zord  = input.string(defval = "Hedges' G",     title = "Comparison Calc", options = ["Hedges' G", "Z Compare", "Glass's D", "Cliff D"])
col1  = input.color( defval = #6929F2,       title = "Chart Crypto Color", inline = "a")
col2  = input.color( defval = #14D990,       title = "Comparison Symbol Color", inline = "a")
dcol  = input.color( defval = #18d720,       title = "Reversal Up Col   ", inline = "b")
ucol  = input.color( defval = color.red,     title = "Reversal Down Col      ", inline = "b")


show   = input.bool (defval = true,            title = "Show Alternative RRG"     , group = "Quadrant"               )
len    = input.int  (defval = 100, minval = 5, title = "Relative Strength Length" , group = "Quadrant", inline = "1" )
lenx   = input.int  (defval = 5,   minval = 5, title = "RS MOM Length"            , group = "Quadrant", inline = "1" )
neon   = input.bool (defval = true,            title = "Show Neon"                , group = "Quadrant"               )
impcol = input.color(defval = color.blue,    title = "Improving Color   "       , group = "Quadrant", inline = "20")
leacol = input.color(defval = color.green,   title = "Leading Color"            , group = "Quadrant", inline = "20")
detcol = input.color(defval = color.yellow,  title = "Deteriorating Color"      , group = "Quadrant", inline = "21"),  barCond = float(last_bar_index - bar_index)
lagcol = input.color(defval = color.red,     title = "Lagging Color"            , group = "Quadrant", inline = "21"),  recent  = barCond.under_equal(back)



[index, i2, i1] = request.security(sym, timeframe.period, [math.log(close / close[zlook]), math.log(close / close[5]), math.log(close / close[1])])


type RSvalues 

    matrix <float> performances
    matrix <float> rs 
    matrix <float> roc


var rsv = RSvalues.new(matrix.new<float>(29, 0), matrix.new<float>(29, 0), matrix.new<float>(29, 0))
var z3 = array.new_float() , var z4 = array.new_float(), var avgs = matrix.new<float>(4, 0)

var stringArr = array.from("BTC" , "ETH"  , "BNB" , "XRP", 
                           "ADA" , "DOGE" , "SOL" , "TRX", 
                           "DOT" , "MATIC", "SHIB", "TON", 
                           "LTC" , "WBTC" , "BCH" , "LEO", 
                           "AVAX", "XLM"  , "LINK", "UNI", 
                           "OKB" , "XMR"  , "ATOM", "ETC", 
                           "HBAR", "ICP"  , "FIL" , "MNT", 
                                         "APT"
                                     )

req( string ) => 
    
    logClose = request.security( string , timeframe.period, math.log(close / close[1]))

method float(int id) => 
    
    float(id)

if barCond.under_equal(len.float().multiply(2))

    if rsv.performances.columns().float().over(len) 

        rsv.performances.remove_col(0)
        rsv.rs          .remove_col(0)
        rsv.roc         .remove_col(0)

    rsv.performances.add_col(rsv.performances.columns(), array.from( req("BTCUSD" ), req("ETHUSD"  ),  req("BNBUSD" ),  req("XRPUSD"), 
                                                                     req("ADAUSD" ), req("DOGEUSD" ),  req("SOLUSD" ),  req("TRXUSD"), 
                                                                     req("DOTUSD" ), req("MATICUSD"),  req("SHIBUSD"),  req("TONUSD"), 
                                                                     req("LTCUSD" ), req("WBTCUSD" ),  req("BCHUSD" ),  req("LEOUSD"), 
                                                                     req("AVAXUSD"), req("XLMUSD"  ),  req("LINKUSD"),  req("UNIUSD"), 
                                                                     req("OKBUSD") , req("XMRUSD"  ),  req("ATOMUSD"),  req("ETCUSD"), 
                                                                     req("HBARUSD"), req("ICPUSD"  ),  req("FILUSD" ),  req("MNTUSD"), 
                                                                                             req("APTUSD")))
    
    rsv.rs .add_col(rsv.rs.columns())
    rsv.roc.add_col(rsv.roc.columns())
    rows = rsv.performances.rows() - 1 

    if rsv.rs.columns().float().equal(1)

        for i = 0 to rows

            rsv.rs .set(i,  rsv.rs.columns() - 1, rsv.performances.row(i).last().divide(rsv.performances.col(0).avg()))
            rsv.roc.set(i, rsv.roc.columns() - 1, 0)
    
    else
        
        for i = 0 to rows
            rsv.rs.set(i, rsv.rs.columns() - 1, rsv.performances.row(i).avg())

        indexAvg = rsv.rs.col(rsv.rs.columns() - 1).avg()

        for i = 0 to rows 

            rsv.rs.set (i, rsv.rs .columns() - 1, rsv.rs.get(i, rsv.rs.columns() - 1) / indexAvg)
            rsv.roc.set(i, rsv.roc.columns() - 1, rsv.rs.get(i, rsv.rs.columns() - 1) / rsv.rs.get(i, rsv.rs.columns() - 2) - 1)
 
 
method append (matrix <float> id) => 

    if recent

        id.add_col(id.columns(), array.from(math.log(close / close[zlook]), index, i2, math.log(close / close[1])))

        if id.columns().float().over(lenco)
            id.remove_col(0)

method zScoreInd  (array <float> id) => 
    
    if recent

        (id.last() - id.avg()) / id.stdev()

method gcorrect (matrix <float> id) => 

    if recent

        n  = id.columns()

        pooled  = math.sqrt(((n - 1) * id.row(0).variance() + (n - 1) * id.row(1).variance()) / (n + n - 2))
        
        correct = ((n - 3) / (n - 2.25)) * math.sqrt((n - 2) / n)

        ((id.row(0).avg() - id.row(1).avg()) / pooled) * correct



method zcomp(matrix<float> id) =>

    if recent

        n      = id.columns()

        pooled = math.sqrt((math.pow(id.row(0).stdev(), 2) + math.pow(id.row(1).stdev(), 2)) / 2)

        z = (id.row(0).avg() - id.row(1).avg()) / pooled 


method glass (matrix<float> id) =>

    if recent

        (id.row(0).avg() - id.row(1).avg()) / id.row(0).stdev()



method cliff (matrix<float> id) => 

    if recent 

        ngt = 0, nlt = 0 

        sampleA = id.row(0), sampleB = id.row(1)

        n = sampleA.size()

        for i = 0 to n - 1
            for x = 0 to n - 1
                if sampleA.get(i) > sampleB.get(x)
                    ngt += 1
                else if sampleA.get(i) < sampleB.get(x)
                    nlt += 1

        cliffd = (ngt - nlt) / math.pow(n, 2)


avgs.append()

zArr      = array.from(avgs.row(0).zScoreInd(), avgs.row(1).zScoreInd())
var i2arr = array.new_float(0, 0), i2arr.push(i2) 

method plusMinusAdd(matrix<float> id) => 

    if recent

        last = i2arr.zScoreInd().over(0) 

        [val, val2] = switch last

            true => [i2arr.zScoreInd() + math.max(zArr.first(), zArr.last(), 0), 0]
            =>      [0, math.min(zArr.first(), zArr.last(), 0) + i2arr.zScoreInd()]

        id.add_col(id.columns(), array.from(val, val2))

        if id.columns().float().over(1000)
            id.remove_col(0)

method hedge(matrix<float> id) => 
    
    if recent

        id.add_col(id.columns(), array.from(math.log(close / close[1]), i1))

        if id.columns().float().over(len)
            id.remove_col(0)

        id.row(0).covariance(id.row(1)) / id.row(1).variance()


method add100  (float id)         => id + 100
method quick   (color id, transp) => color.new(id, transp)

method determine (bool id, a, b) => 

    switch id 

        true => a 
        =>      b


var hedge  = matrix.new<float>(2, 0)
var colArr = matrix.new<float>(2, 0)

colArr.plusMinusAdd(), colArr0 = colArr.row(0), colArr1 = colArr.row(1)

finCol = if recent 
    
    switch 

        i2arr.zScoreInd()  <= 0 =>   colArr1.last().add100()
        =>                           colArr0.last().add100()

else 

    100

nine = colArr0.percentile_nearest_rank(90) 
ten  = colArr1.percentile_nearest_rank(10) 

[first100, last100] = switch recent

    true => [zArr.first().add100(), zArr.last ().add100()]
    =>      [100, 100]


plotcandle(100, first100, first100, first100, 
                             color       = col1.quick(75), 
                             bordercolor = col1.quick(10), 
                             display     = display.pane
                             )

plotcandle(100, last100, last100, last100,
                             color       = col2.quick(75), 
                             bordercolor = col2.quick(10), 
                             display     = display.pane
                             )

hedge100 = hedge.hedge().add100()

plo = switch

    hedge100.over(100) => math.min(104, hedge100)
    =>                    math.max( 96, hedge100)

plotVal = switch zord 

    "Hedges' G" => avgs.gcorrect()
    "Z Compare" => avgs.zcomp()
    "Glass's D" => avgs.glass()
    "Cliff D"   => avgs.cliff()

p1  = plot(plotVal.add100(), color = color.white, style = plot.style_stepline_diamond, display = display.pane, linewidth = 1),

                             p2  = plot(100,                               display = display.none), 
                             p3  = plot(plo,                               display = display.none),

                             fill(p2, p3,  100, 96,  #00000000, hedge100.under(100).determine(color.lime.quick(50), #00000000)),
                             fill(p2, p3,  104, 100, hedge100.over(100).determine(color.red.quick(50), #00000000), #00000000)

allOver() => zArr.last().over(0)  and i2arr.zScoreInd().over (0) and zArr.last().over (zArr.first())
allUndr() => zArr.last().under(0) and i2arr.zScoreInd().under(0) and zArr.last().under(zArr.first())

[finGradientBG, finGradientBO] = switch 

    allOver() => [color.from_gradient(colArr0.last(), 0, nine, #00000000, ucol.quick(75)), 
                  color.from_gradient(colArr0.last(), 0, nine, #00000000, ucol.quick(10))]

    allUndr() => [color.from_gradient(colArr1.last(), ten, 0, dcol.quick(75), #00000000), 
                  color.from_gradient(colArr1.last(), ten, 0, dcol.quick(10), #00000000)] 
    
    =>           [color(na), color(na)]
             
plotcandle(
        
         i2arr.zScoreInd().over(0).determine(math.max(zArr.first(), zArr.last(), 0).add100(), math.min(zArr.first(), zArr.last(), 0).add100()),

                                     finCol, finCol, finCol, 
                                     color       = finGradientBG, 
                                     bordercolor = finGradientBO, 
                                     wickcolor   = na, 
                                     display     = display.pane
                                     ), 
                             
                             styl = neon.determine(label.style_text_outline, label.style_none)

type quadrants

    float  xcoord 
    float  ycoord
    string symbol


method coordinateSet (matrix <quadrants> id, xval, yval, sval) => 

    id.add_col(id.columns(), array.from(
                        
                             quadrants.new(xcoord = xval.add100()),
                             quadrants.new(ycoord = yval.add100()), 
                             quadrants.new(symbol = sval         )
                             ))

method normalize (matrix <quadrants> id, int , float , int2, float2, color ) =>


    if id.columns().float().over(0)

        values = array.from(-1e8, 1e8, -1e8, 1e8)

        for i = 0 to id.columns() - 1

            values.set(0, math.max(values.first(), id.get(0, i).xcoord))
            values.set(1, math.min(values.get (1), id.get(0, i).xcoord))
            values.set(2, math.max(values.get (2), id.get(1, i).ycoord))
            values.set(3, math.min(values.last (), id.get(1, i).ycoord))


        xmin = values.get(1), xrange = values.first() - xmin
        ymin = values.last(), yrange = values.get(2)  - ymin

        for i = 0 to id.columns() - 1

            id.set(0, i, quadrants.new(xcoord = ((id.get(0, i).xcoord - xmin) / xrange)   * (int   -  int2 )  + int2))
            id.set(1, i, quadrants.new(ycoord = ((id.get(1, i).ycoord - ymin) / yrange)   * (float - float2 ) + float2))
            

            label.new(int(bar_index + id.get(0, i).xcoord), id.get(1, i).ycoord, text = id.get(2, i).symbol, 
                         textcolor = neon.determine(color.white, color ), 
                         color     = color ,
                         size      = size.tiny, 
                         style     = styl
                         )
    

if barstate.islast 

    box.all.flush(), label.all.flush(), line.all.flush()

    for i = 0 to 1

        [ycoord, col] = switch 
            
            i.float().equal(0) => [nine.add100(), ucol.quick(50)]
            i.float().equal(1) => [ten .add100(), dcol.quick(50)]

        line.new(bar_index - 1, ycoord, bar_index, ycoord, color = col, style = line.style_dotted, extend = extend.left)

    if show

        rslast  = rsv.rs.col(rsv.rs.columns() - 1)
    
        q1 = matrix.new<quadrants>(3, 0), q2 = matrix.new<quadrants>(3, 0)
        q3 = matrix.new<quadrants>(3, 0), q4 = matrix.new<quadrants>(3, 0)
    
        for i = 0 to rsv.roc.rows() - 1
    
            cols = rsv.roc.columns()
            xval = rslast .get    (i)
    
            yval = rsv.roc.row(i).slice(cols-lenx, cols).avg()
    
            switch 
            
                xval.over_equal(0) and yval.over_equal(0) => q1.coordinateSet(xval, yval, stringArr.get(i))
                xval.over_equal(0) and yval.under     (0) => q2.coordinateSet(xval, yval, stringArr.get(i))
                xval.under     (0) and yval.over_equal(0) => q3.coordinateSet(xval, yval, stringArr.get(i))
                xval.under     (0) and yval.under     (0) => q4.coordinateSet(xval, yval, stringArr.get(i))
    
        q1.normalize(190, 103.50, 60, 102.50, leacol), q3.normalize(190, 99.50, 60, 98.50 , detcol)
        q2.normalize(190, 101.50, 60, 100.50, impcol), q4.normalize(190, 97.50, 60, 96.50 , lagcol)
    
        for i = 0 to 3
            
            [x, y, x2, y2, col] = switch 
            
                i.float().equal(0) => [50,  100, 200, 102 , impcol.quick(75)]
                i.float().equal(1) => [50,  102, 200, 104 , leacol.quick(75)]
                i.float().equal(2) => [50,  96 , 200, 98  , lagcol.quick(75)] 
                i.float().equal(3) => [50,  98 , 200, 100 , detcol.quick(75)] 
    
            box.new(bar_index + x, y, bar_index + x2, y2, col, bgcolor = col, border_color = col, border_style = line.style_solid, border_width = 2)
    
            [x3, y3, text, colx] = switch 
            
                i.float().equal(0) => [25, 101 , "Improving"    , impcol]
                i.float().equal(1) => [25, 103 , "Leading"      , leacol]
                i.float().equal(2) => [25, 97  , "Lagging"      , lagcol]
                i.float().equal(3) => [25, 99  , "Deteriorating", detcol]
    
            label.new(bar_index + x3, y3, text, color = colx, textcolor = neon.determine(color.white, colx), size = size.small, style = styl)
                
        label.new(bar_index + 125 , 95.5, "RS", color     = #000000,  
                                              textcolor = color.white, 
                                              size      = size.small, 
                                              style     = styl
                                              )
        
        label.new(bar_index + 210, 100, text     = "R\nS\n\nM\no\nm\ne\nn\nt\nu\nm", 
                                              color     = #000000, 
                                              textcolor = color.white, 
                                              size      = size.small, 
                                              style     = styl
                                              )
        
        box.new(bar_index + 50, 104, bar_index + 200, 96, 
                                             bgcolor      = na, 
                                             border_color = color.white, 
                                             border_width = 2
                                             )

    
