AMASling - All Moving Average Sling Shot
// 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

//  -----------------------------------------------------------------------------
//
//  Title:    AMASling - All Moving Average Sling Shot
//  Authors:  @EltAlt
//  Revision: v1.00
//  Date:     20-May-2022
//
//  Description
//  =============================================================================
//  This indicator modifies the SlingShot System by Chris Moody to allow it to be based on 'any' Fast and Slow moving average pair.
//  Open Long / Close Long / Open Short / Close Short alerts can be generated for automated bot trading based on the SlingShot strategy:
//  • Conservative Entry = Fast MA above Slow MA, and previous bar close below Fast MA, and current price above Fast MA
//  • Conservative Entry = Fast MA below Slow MA, and previous bar close above Fast MA, and current price below Fast MA
//  • Aggressive Entry = Fast MA above Slow MA, and price below Fast MA
//  • Aggressive Exit = Fast MA below Slow MA, and price above Fast MA
//
//  Entries and exits can also be made based on moving average crossovers, I initially put this in to make it easy to compare to a more
//  standard strategy, but upon backtesting combining crossovers with the SlingShot appeared to produce better results on some charts.
//  Alerts can also be filtered to allow long deals only when the fast moving average is above the slow moving average (uptrend) and 
//  short deals only when the fast moving average is below the slow moving averages (downtrend).
//
//  If you have a strategy that can buy based on External Indicators you can use the 'Backtest Signal' which plots the values set in
//  the 'Long / Short Signals' section.
//
//  The Fast, Slow and Signal Moving Averages can be set to:
//  • Simple Moving Average (SMA)
//  • Exponential Moving Average (EMA)
//  • Weighted Moving Average (WMA)
//  • Volume-Weighted Moving Average (VWMA)
//  • Hull Moving Average (HMA) 
//  • Exponentially Weighted Moving Average (RMA) (SMMA)
//  • Linear regression curve Moving Average (LSMA)
//  • Double EMA (DEMA)
//  • Double SMA (DSMA)
//  • Double WMA (DWMA)
//  • Double RMA (DRMA)
//  • Triple EMA (TEMA)
//  • Triple SMA (TSMA)
//  • Triple WMA (TWMA)
//  • Triple RMA (TRMA)
//  • Symmetrically Weighted Moving Average (SWMA) ** length does not apply **
//  • Arnaud Legoux Moving Average (ALMA)
//  • Variable Index Dynamic Average (VIDYA)
//  • Fractal Adaptive Moving Average (FRAMA)
//
//  'Backtest Signal' and 'Deal State' are plotted to display.none, so change the Style Settings for the chart if you need to see them
//  for testing.
//
//  =============================================================================
//
//  I would gladly accept any suggestions to improve the script.
//  If you encounter any problems please share them with me.
//
//  Thanks to ChrisMoody for "CM Sling Shot System" which gave me the basis.
//
//  Changlog
//  =============================================================================
//
//  1.00 • Initial release
//
//
// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
//
//
// ============ "AMASling - All Moving Average Sling Shot"

indicator(title='AMASling - All Moving Average Sling Shot',
          shorttitle='AMASling',
          overlay = true, 
          timeframe='', 
          timeframe_gaps=true)


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

MATip = 'Simple Moving Average (SMA)\nExponential Moving Average (EMA)\nWeighted Moving Average (WMA)\nVolume-Weighted Moving Average (VWMA)\nHull Moving Average (HMA)\nExponentially Weighted Moving Average (RMA) (SMMA)\nLinear regression curve Moving Average (LSMA)\nDouble EMA (DEMA)\nDouble SMA (DSMA)\nDouble WMA (DWMA)\nDouble RMA (DRMA)\nTriple EMA (TEMA)\nTriple SMA (TSMA)\nTriple WMA (TWMA)\nTriple RMA (TRMA)\nSymmetrically Weighted Moving Average (SWMA)\n** length does not apply to SWMA **\nArnaud Legoux Moving Average (ALMA)\nVariable Index Dynamic Average (VIDYA)\nFractal Adaptive Moving Average (FRAMA)'
FastType   = input.string('EMA', title='Fast Moving Average Type', group='Moving Averages', options=['EMA', 'SMA', 'WMA', 'VWMA', 'HMA', 'RMA', 'LSMA', 'Double EMA', 'Double SMA', 'Double WMA', 'Double RMA', 'Triple EMA', 'Triple SMA', 'Triple WMA', 'Triple RMA', 'SWMA', 'ALMA', 'VIDYA', 'FRAMA'], tooltip = MATip)
FastSource = input (defval = close, title='Fast Source', inline='Fast', group='Moving Averages')
FastLength = input.int (38, title='Fast Length', inline='Fast', group='Moving Averages', minval=2, maxval=1000)

SlowType   = input.string ('EMA', title='Slow Moving Average Type', group='Moving Averages', options=['EMA', 'SMA', 'WMA', 'VWMA', 'HMA', 'RMA', 'LSMA', 'Double EMA', 'Double SMA', 'Double WMA', 'Double RMA', 'Triple EMA', 'Triple SMA', 'Triple WMA', 'Triple RMA', 'SWMA', 'ALMA', 'VIDYA', 'FRAMA'], tooltip = MATip)
SlowSource = input (defval = close, title='Slow Source', inline='Slow', group='Moving Averages')
SlowLength = input.int (62, title='Slow Length', inline='Slow', group='Moving Averages', minval=2, maxval=1000)

longUptrendOnly    = input.bool (true,  inline='1', group='Filters', title='Long Only in Uptrend')
shortDowntrendOnly = input.bool (true,  inline='1', group='Filters', title='Short Only in Downtrend')

signalTip = 'Here you can set the signals that will enable backtester to open and close deals.\nIf the backtester you are using can only work with long OR short deals you may need to save diffent chart layouts for longs and shorts.\nIf \'Generate Close Signals for Long / Short Positions\' is not selected only the \'Open Long\' and \'Close Long\' settings will plot.'
generateClose    = input.bool (false, inline='0', group='Long / Short Signals', title='Generate Close Signals for Long / Short Positions', tooltip = signalTip)
longOpenSignal   = input.int  (1,    inline='1', group='Long / Short Signals', title='Open Long = ')
longCloseSignal  = input.int  (2,    inline='1', group='Long / Short Signals', title='Close Long = ')
shortOpenSignal  = input.int  (-1,   inline='2', group='Long / Short Signals', title='Open Short = ')
shortCloseSignal = input.int  (-2,   inline='2', group='Long / Short Signals', title='Close Short = ')

slingshotTip = 'SlingShot entries are based on CM_SlingShotSystem by Chris Moody. To replicate the SlingShot method exactly set:\nLong Only in Uptrend\nShort Only in Downtrend\nFast MA = EMA close 38\nSlow MA = EMA close 62\nSignal Length = 1\n'
slingSigsTip = 'Buy Agressive Entry = Fast MA above Slow MA, and price below Fast MA\nSell Agressive Exit = Fast MA below Slow MA, and price above Fast MA\nBuy Conservative Entry = Fast MA above Slow MA, and previous bar close below Fast MA, and current price above Fast MA\nSell Conservative Entry = Fast MA below Slow MA, and previous bar close above Fast MA, and current price below Fast MA'
aggFilterTip = 'Aggressive signals are often repeated for a few bars, this will show only the first of a cluster of aggressive signals.'
buySlingConLong   = input.bool (false,  inline='1', group='Buy / Sell SlingShot System', title='Buy Conservative SlingShot Entry', tooltip = slingSigsTip)
sellSlingConShort = input.bool (false,  inline='1', group='Buy / Sell SlingShot System', title='Sell Conservative SlingShot Entry')
buySlingAggLong   = input.bool (false,  inline='2', group='Buy / Sell SlingShot System', title='Buy Aggressive SlingShot Entry',    tooltip = slingshotTip)
sellSlingAggShort = input.bool (false,  inline='2', group='Buy / Sell SlingShot System', title='Sell Aggressive SlingShot Exit')
filterAggReps     = input.bool (false,  inline='3', group='Buy / Sell SlingShot System', title='First Aggressive Signal Only', tooltip = aggFilterTip)

crossoverTip = 'Generate buy signals when the fast moving average crosses up on the slow moving average, or sell signals when the fast moving average crosses down on the slow moving average.\nThis is the same as the histogram crossing zero.'
buyCrossover   = input.bool (true,  inline='1', group='Buy / Sell Moving Averages', title='Buy Moving Average Crossover', tooltip = crossoverTip)
sellCrossunder = input.bool (true,  inline='1', group='Buy / Sell Moving Averages', title='Sell Moving Average Crossunder')

almaTip = 'Only required if ALMA is used as one of the moving averages.'
offset_alma = input       (0.85, title='ALMA Offset', inline='1', group='Alma', tooltip = almaTip)
sigma_alma  = input.float (6,    title='ALMA Sigma',  inline='1', group='Alma')

framaTip = 'Only required if FRAMA is used as one of the moving averages.'
FC = input.int(1,   minval=1, title='FRAMA lower shift limit (FC)', inline='1', group='Frama', tooltip = framaTip)
SC = input.int(198, minval=1, title='FRAMA upper shift limit (SC)', inline='1', group='Frama')

alertsTip = 'By default SlingShot Conservative signals are 25% brighter than other signals.'
plotAlerts    = input.bool (true,  inline='1', group='Plot Options', title='Plot Alerts', tooltip = alertsTip)
plotMA        = input.bool (true,  inline='1', group='Plot Options', title='Plot Moving Averages')


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

getCMO(src, length) =>
    mom     = ta.change(src)
    upSum   = math.sum(math.max(mom, 0), length)
    downSum = math.sum(-math.min(mom, 0), length)
    out     = (upSum - downSum) / (upSum + downSum)
    out

vidya(src, length) =>
    alpha  = 2 / (length + 1)
    cmo    = math.abs(getCMO(src, length))
    vidya  = 0.0
    vidya := src * alpha * cmo + nz(vidya[1]) * (1 - alpha * cmo)
    vidya

frama(x, y, z, v) =>
    // x = source , y = length , z = FC , v = SC
    HL        = (ta.highest(high, y) - ta.lowest(low, y)) / y
    HL1       = (ta.highest(high, y / 2) - ta.lowest(low, y / 2)) / (y / 2)
    HL2       = (ta.highest(high, y / 2)[y / 2] - ta.lowest(low, y / 2)[y / 2]) / (y / 2)
    D         = (math.log(HL1 + HL2) - math.log(HL)) / math.log(2)
    dim       = HL1 > 0 and HL2 > 0 and HL > 0 ? D : nz(D[1])
    w         = math.log(2 / (v + 1))
    alpha     = math.exp(w * (dim - 1))
    alpha1    = alpha > 1 ? 1 : alpha < 0.01 ? 0.01 : alpha
    oldN      = (2 - alpha1) / alpha1
    newN      = (v - z) * (oldN - 1) / (v - 1) + z
    newalpha  = 2 / (newN + 1)
    newalpha1 = newalpha < 2 / (v + 1) ? 2 / (v + 1) : newalpha > 1 ? 1 : newalpha
    frama     = 0.0
    frama    := (1 - newalpha1) * nz(frama[1]) + newalpha1 * x
    frama

calcMA(_type, _src, _length) =>
    switch _type 
        'EMA'        => ta.ema(_src, _length)
        'SMA'        => ta.sma(_src, _length)
        'WMA'        => ta.wma(_src, _length)
        'VWMA'       => ta.vwma(_src, _length)
        'HMA'        => ta.hma(_src, _length)
        'RMA'        => ta.rma(_src, _length)
        'LSMA'       => ta.linreg(_src, _length, 0)
        'Double EMA' => 2 * ta.ema(_src, _length) - ta.ema(ta.ema(_src, _length), _length)
        'Double SMA' => 2 * ta.sma(_src, _length) - ta.sma(ta.sma(_src, _length), _length)
        'Double WMA' => 2 * ta.wma(_src, _length) - ta.wma(ta.wma(_src, _length), _length)
        'Double RMA' => 2 * ta.rma(_src, _length) - ta.rma(ta.rma(_src, _length), _length)
        'Triple EMA' => 3 * (ta.ema(_src, _length) - ta.ema(ta.ema(_src, _length), _length)) + ta.ema(ta.ema(ta.ema(_src, _length), _length), _length)
        'Triple SMA' => 3 * (ta.sma(_src, _length) - ta.sma(ta.sma(_src, _length), _length)) + ta.sma(ta.sma(ta.sma(_src, _length), _length), _length)
        'Triple WMA' => 3 * (ta.wma(_src, _length) - ta.wma(ta.wma(_src, _length), _length)) + ta.wma(ta.wma(ta.wma(_src, _length), _length), _length)
        'Triple RMA' => 3 * (ta.rma(_src, _length) - ta.rma(ta.rma(_src, _length), _length)) + ta.rma(ta.rma(ta.rma(_src, _length), _length), _length)
        'SWMA'       => ta.swma(_src)        // No Length for SWMA
        'ALMA'       => ta.alma(_src, _length, offset_alma, sigma_alma)
        'VIDYA'      => vidya(_src, _length)
        'FRAMA'      => frama(_src, _length, FC, SC)


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

MA_fast = calcMA(FastType, FastSource, FastLength)
MA_slow = calcMA(SlowType, SlowSource, SlowLength)
uptrend = MA_fast > MA_slow
crossover  = ta.crossover(MA_fast, MA_slow)
crossunder = ta.crossunder(MA_fast, MA_slow)


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Logic

// ------------ SlingShot

bool slingAggLong  = MA_fast > MA_slow and close < MA_fast
bool slingAggShort = MA_fast < MA_slow and close > MA_fast
bool slingConLong  = MA_fast > MA_slow and close[1] < MA_fast and close > MA_fast
bool slingConShort = MA_fast < MA_slow and close[1] > MA_fast and close < MA_fast

// ------------ Order Logic

var int dealstate = 0

bool openLong   = false
bool closeLong  = false
bool openShort  = false
bool closeShort = false

backtestBuy = (buyCrossover        and crossover)
               or (buySlingConLong and slingConLong)
               or (buySlingAggLong and slingAggLong and not (slingAggLong[1] and filterAggReps))
               
backtestSell = (sellCrossunder       and crossunder)
               or (sellSlingConShort and slingConShort)
               or (sellSlingAggShort and slingAggShort and not (slingAggShort[1] and filterAggReps))
               
if backtestBuy and not backtestSell
    if uptrend or not longUptrendOnly
        if generateClose and dealstate == -1
            closeShort := true
            dealstate := 0
        else
            openLong := true
            dealstate := 1
    else
        if generateClose and dealstate == -1
            closeShort := true
            dealstate := 0

if backtestSell and not backtestBuy
    if not uptrend or not shortDowntrendOnly
        if generateClose and dealstate <= 0
            openShort := true
            dealstate := -1
        else
            closeLong := true
            dealstate := 0
    else
        if generateClose and dealstate == 1
            closeLong := true
            dealstate := 0


// ▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒
// ============ Plot

plotFast = plot(plotMA ? MA_fast : na, title= 'Fast Moving Average', color = color.lime )//MA_fast > MA_slow ? color.lime : MA_fast < MA_slow ? color.red : color.yellow)
plotSlow = plot(plotMA ? MA_slow : na, title= 'Slow Moving Average', color = color.red)//MA_fast > MA_slow ? color.lime : MA_fast < MA_slow ? color.red : color.yellow)
fill(plotFast, plotSlow, color=color.new(color.silver, 75))

plot(closeShort ? shortCloseSignal : openLong ? longOpenSignal : closeLong ? longCloseSignal : openShort ? shortOpenSignal : 0, 'Backtest Signal', color=openLong or closeShort ? color.lime : openShort or closeLong? color.red : color.gray, display=display.none)
plot(dealstate, title='Deal State', display=display.none, color = (dealstate > 0 ? color.lime : dealstate < 0 ? color.red : color.gray))


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

alertcondition (openLong,   title='AMASling Open Long!',   message='Buy signal, put your JSON here to open longs or start bots.')
alertcondition (openShort,  title='AMASling Open Short!',  message='Sell signal, put your JSON here to open shorts or stop bots.')
alertcondition (closeShort, title='AMASling Close Short!', message='Buy signal, put your JSON here to close shorts or start bots.')
alertcondition (closeLong,  title='AMASling Close Long!',  message='Sell signal, put your JSON here to close longs or stop bots.')

plotshape (plotAlerts ? openLong   : na, style=shape.triangleup,   color=slingConLong  ? color.lime : color.new(color.lime, 50), location=location.bottom, size=size.tiny, title='Open Long')
plotshape (plotAlerts ? closeShort : na, style=shape.square,       color=slingConLong  ? color.red  : color.new(color.red, 50),  location=location.bottom, size=size.tiny, title='Close Short')
plotshape (plotAlerts ? openShort  : na, style=shape.triangledown, color=slingConShort ? color.red  : color.new(color.red, 50),  location=location.bottom, size=size.tiny, title='Open Short')
plotshape (plotAlerts ? closeLong  : na, style=shape.square,       color=slingConShort ? color.lime : color.new(color.lime, 50), location=location.bottom, size=size.tiny, title='Close Long')
