RSI vs Longs/Shorts Margin Ratio Percentage Rank
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © EltAlt

//@version=5

// --------------------------------------------------------------------------------------------------------------------
//
// Authors:  @EltAlt
// Revision: v1.03
// Date:     2-August-2022
//
// Description
// ====================================================================================================================
//
// This indicator should be used on a symbol that also has long and short margined tokens available. By default it
// plots the RSI of the current token with a color based on percentage rank the RSI of BITFINEX:'token'LONGS divided
// by BITFINEX:'token'SHORTS. If you are using a chart that does not have BITFINEX LONGS and SHORTS tokens it will
// display the standard RSI.
//
// If you enter Manual Margined Long/Short Symbols you can select any Tradingview symbols for your Long and Short
// tokens. I don't think you will get meaningful results unless you select a long and short margined token such as
// BITFINEX:ETHUSDLONGS and BITFINEX:ETHUSDSHORTS. Even using margined tokens the results may not be meaningful, if
// there is not enough trade volume in the token, or if they are being manipulated, so you must backtest everything. 
//
// The four plot options are:
//     • Colored RSI - RSI plotted with colors based on the Longs/Shorts ratio
//     • Background Color - White RSI plot with Longs/Shorts ratio as background color
//     • RSI + Ratio - White RSI with Longs/Shorts ratio plotted in color
//     • RSI + Ratio Histogram - White RSI with Longs/Shorts ratio plotted as a histogram in color
//
// By default it also plots a short term moving average and it can also plot the raw ratio rather than the percentage
// rank if selected, the raw ratio can be more useful on longer timeframe charts.
//
// Alerts can be triggered and or shown on the chart when the Ratio is Above / Below specified values, with the
// option to filter by the RSI being beyond the displayed upper and lower limits and or the RSI above / below its
// moving average.
//
// Alerts can now be triggered on RSI divergences, thanks DevLucem for the library!
//
// ====================================================================================================================
//
// I would gladly accept any suggestions to improve the script.
// If you encounter any problems please share them with me.
//
// Changlog
// ====================================================================================================================
//
// 1.00 • First public release
// 1.01 • Added an automatic pair option
//      • Got rid of the average, not needed
//      • Added alerts
// 1.02 • Set long and short symbol defval to '' so BINANCE:BTCUSDLONGS doen't show up in the chart unless it is set.
//      • Since the manual settings now default to '' it will be automatic unless a manual setting is entered, so Auto
//        setting is no longer required, 
//      • If the currency is USD, UST or BTC it will automatically use the chart pair for longs and shorts.
//      • If the currency is EUR, GBP or JPY and the base is ETH or BTC it will automatically use the chart pair for
//        longs and shorts.
//      • Otherwise it will use the USD pair for longs and shorts.
// 1.03 • Added histogram style.
//      • Modified to just display the normal RSI where no BITFINEX LONGS exist.
//      • Added @DevLucem Divergence library to alert on RSI divergences.
//
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
//
//
// ============ "RSI vs Longs/Shorts Margin Ratio Percentage Rank"

indicator('RSI vs Longs/Shorts Margin Ratio Percentage Rank',
          shorttitle='RSI vs Longs/Shorts',
          overlay = false, 
          timeframe='', 
          timeframe_gaps=true)

import DevLucem/Divergence/1 as Divergence

// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Inputs

rsioption    = 'Colored RSI'
bkgoption    = 'Background Color'
ratoption    = 'RSI + Ratio'
histoption   = 'RSI + Ratio Histogram'
typetip      = 'The four plot options are:\n
                 Colored RSI - RSI plotted with colors based on the Longs/Shorts ratio\n
                 Background Color - White RSI plot with Longs/Shorts ratio as background color\n
                 RSI + Ratio - White RSI with Longs/Shorts ratio plotted in color\n
                 RSI + Ratio Histogram - White RSI with Longs/Shorts ratio plotted as a histogram in color'
symboltip    = 'Longs and shorts will automatically be found if you are on a chart that has them available, shuch as BTCJPY.\n
                 Automatic matching will be attempted for USD, UST, EUR, GBP, JPY or BTC Pairs.\n
                 If you are using a different base such as BUSD or AUD it will attempt to match with the USD pair.\n
                 Manually set tickers here if it is not matching anything or you want to manually match something else.\n
                 While you can select any Tradingview symbols here I do not think you will get meaningful results unless you 
                 select a long and short margined token such as BITFINEX:BTCUSDLONGS and BITFINEX:BTCUSDSHORTS. Even using 
                 margined tokens the results may not be meaningful if there is not enough trade volume in the token, so you 
                 must backtest everything.'

long         = input.symbol ('', 'Manual Margined Long Symbol', tooltip = symboltip)
short        = input.symbol ('', 'Manual Margined Short Symbol')
plottype     = input.string (rsioption , 'Plot Style', options=[rsioption, bkgoption, ratoption, histoption], tooltip = typetip)
plotbktab    = input.int (defval   = 60, maxval=100, minval=0, title   = 'Transparency Above MA', inline='0')
plotbktbe    = input.int (defval   = 75, maxval=100, minval=0, title   = 'Below MA', inline='0')
usepctrank   = input.bool (true, 'Use the percentrank of the ratio?', inline='1')
pctlbk       = input.int  (500, title='  Lookback', inline='1')
plotrange    = input.string ('RGB Spectrum', 'Color Type', options=['RGB Spectrum', 'RBlG Spectrum', 'RB Spectrum', 'RG Spectrum', 'Solid Color'], inline='2')
plotcolor    = input.color  (#FFFF00, 'Solid Color', inline='2')
plothilow    = input.bool (true, 'Plot High/Low Lines', inline='2')

length       = input.int (14, 'Length', inline='1', group='RSI Settings')
rsiupper     = input.int (70, 'Upper',  inline='1', group='RSI Settings')
rsilower     = input.int (30, 'Lower',  inline='1', group='RSI Settings')
plotma       = input.bool (true, 'Plot RSI MA', inline='2', group='RSI Settings')
malength     = input.int (5, 'Length', inline='2', group='RSI Settings')
matype       = input.string ('EMA', 'Type', options=['SMA', 'EMA', 'VWMA'], inline='2', group='RSI Settings')

alerts       = input.bool (true, 'Plot Visable Alerts', inline='1', group='Alerts')
a_rsibands   = input.bool (true, 'RSI Beyond Bands', inline='2', group='Alerts')
a_rsima      = input.bool (true, 'RSI MA Filter', inline='2', group='Alerts')
a_ratiobelow = input.int (5,  'Ratio Below', inline='3', group='Alerts')
a_ratioabove = input.int (95, 'Ratio Above', inline='3', group='Alerts')
a_div        = input.bool (true, 'Alert on RSI Divergences', group='Alerts')

// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Functions

f_color (_type, _solidcolor, _percent) =>
    _color = switch _type
        'RGB Spectrum'  => _percent >= 50 ? color.from_gradient(_percent, 50, 100, #0AFF00, #FF2900): color.from_gradient(_percent, 0, 49, #0000FF, #0AFF00)
        'RBlG Spectrum' => _percent >= 50 ? color.from_gradient(_percent, 50, 100, #000000, #FF2900): color.from_gradient(_percent, 0, 49, #0AFF00, #000000)
        'RB Spectrum'   => color.from_gradient(_percent, 0, 100, #0000FF, #FF2900)
        'RG Spectrum'   => color.from_gradient(_percent, 0, 100, #00FF00, #FF0000)
        'Solid Color'   => _solidcolor

f_ma (_type, _source, _length) => _type == 'VWMA' ? ta.vwma ( _source, _length ) : _type == 'EMA' ? ta.ema ( _source, _length ) : ta.sma ( _source, _length )

f_percentrank(_source, _lookback) =>
    _count = 0.0
    for _i = 1 to _lookback by 1
        _count += ( _source[_i] > _source ? 0 : 1 )
        _count
    _return = ( _count / _lookback) * 100


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Calculations

ticker      = syminfo.ticker
currency    = syminfo.currency
base        = syminfo.basecurrency
btfxticker  = (currency == 'USD' 
               or currency == 'UST' 
               or currency == 'BTC'
               or (currency == 'EUR' and (base == 'BTC' or base == 'ETH'))
               or (currency == 'GBP' and (base == 'BTC' or base == 'ETH'))
               or (currency == 'JPY' and (base == 'BTC' or base == 'ETH'))
               ) ?
                 ticker
              : base + 'USD'
longs       = request.security (long  == '' ? ticker.new ('BITFINEX', btfxticker+'LONGS')  : long,  timeframe.period, close, ignore_invalid_symbol=true)
shorts      = request.security (short == '' ? ticker.new ('BITFINEX', btfxticker+'SHORTS') : short, timeframe.period, close, ignore_invalid_symbol=true)
plotratio   = longs and plottype == ratoption 
plothist    = longs and plottype == histoption
plotbkgrd   = shorts and plottype == bkgoption
rsiclose    = ta.rsi (close, length)
rsiratio    = ta.rsi (longs / shorts, length)
rsima       = f_ma (matype, rsiclose, malength)
pctrank     = usepctrank ? f_percentrank (rsiratio, pctlbk) : na
ratioplot   = usepctrank ? pctrank : rsiratio
ratio_color = longs ? f_color (plotrange, plotcolor, ratioplot) : color.white
bottcolor   = plotrange == 'RG Spectrum' or plotrange == 'RBlG Spectrum' ? color.green : color.blue

[regular_bullish_div, hidden_bullish_div] = Divergence.bullish(rsiclose)
[regular_bearish_div, hidden_bearish_div] = Divergence.bearish(rsiclose)


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Alerts

hghalert = (ratioplot > a_ratioabove)
           and (a_rsibands ? rsiclose > rsiupper : true)
		   and (a_rsima ? rsiclose < rsima : true)

lowalert = (ratioplot < a_ratiobelow)
           and (a_rsibands ? rsiclose < rsilower : true)
		   and (a_rsima ? rsiclose > rsima : true)

alertcondition (hghalert, title='RSI vs Longs/Shorts High Alert!', message='High Alert')
alertcondition (lowalert, title='RSI vs Longs/Shorts Low Alert!',  message='Low Alert')

alertcondition (a_div and regular_bullish_div, title='RSI vs Longs/Shorts Regular Bullish Divergence Alert!', message='Regular Bullish Divergence')
alertcondition (a_div and hidden_bullish_div, title='RSI vs Longs/Shorts Hidden Bullish Divergence Alert!', message='Hidden Bullish Divergence')
alertcondition (a_div and regular_bearish_div, title='RSI vs Longs/Shorts Regular Bearish Divergence Alert!', message='Regular Bearish Divergence')
alertcondition (a_div and hidden_bearish_div, title='RSI vs Longs/Shorts Hidden Bearish Divergence Alert!', message='Hidden Bearish Divergence')


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Plots

plotshape((rsiclose < rsilower) and (rsiclose > rsima) and (regular_bullish_div or hidden_bullish_div), 'spot bottom', shape.diamond, location.bottom, regular_bullish_div ? color.lime : color.new(color.lime, 50), size=size.tiny)
plotshape((rsiclose > rsiupper) and (rsiclose < rsima) and (regular_bearish_div or hidden_bearish_div), 'spot top', shape.diamond, location.top, regular_bearish_div ? color.red : color.new(color.red, 50), size=size.tiny)

plotshape (alerts ? hghalert : na, style=shape.triangledown, color=color.red,  location=location.top,    size=size.tiny, title='High Alert')
plotshape (alerts ? lowalert : na, style=shape.triangleup,   color=color.lime, location=location.bottom, size=size.tiny, title='Low Alert')

bgcolor (plotbkgrd ? color.new(ratio_color, rsiclose > rsima ? plotbktab : plotbktbe) : na)

p_bandTop = hline (plothilow ? 100 : na, '100', color.red, hline.style_solid)
p_bandBot = hline (plothilow ?   0 : na,   '0', bottcolor,  hline.style_solid)
p_bandUpp = hline (rsiupper, 'Upper Band', color.new(color.silver,50), hline.style_dashed)
p_bandLow = hline (rsilower, 'Lower Band', color.new(color.silver,50), hline.style_dashed)
fill(p_bandUpp, p_bandLow, color.new(color.silver,96), title = 'Background')

plot(plotratio or plotbkgrd ? na : rsiclose, 'Ratio Color RSI', color=ratio_color, linewidth=2)

p_ratio = plot(plotratio ? ratioplot : na, 'Long / Short Ratio', color=ratio_color)
p_hist = plot(plothist ? ratioplot : na, 'Long / Short Ratio', color.new(ratio_color, rsiclose > rsima ? plotbktab : plotbktbe), style=plot.style_area, histbase=50)
p_rsi = plot(plotratio or plotbkgrd or plothist ? rsiclose : na, 'RSI', color=color.white, linewidth=2)
fill(p_ratio, p_rsi, color=color.new(ratio_color, rsiclose < rsima ? plotbktbe : plotbktab), fillgaps=true) 

plot(plotma ? rsima : na, 'RSI MA', color=color.silver, linewidth=1)
