// Based on the TMA Overlay by Arty
//
// **** modded by Oliver ****
// 2022-08-03
//   - added Filter to check MA3 Trend
//   - added Filter to check alignment of MA1 and MA2
//   - added Filter to check RSI Momentum
// 2022-08-07
//   - added Filter to check Stochastic RSI level
// 2022-08-07
//   - added Filter to check if RSI is overextended
// 2022-08-10
//   - added Option to use VWAP as Hightime Frame Trend indicator instead of MA3 (request by VorpalEgg)
// 2022-08-16
//   - added Option to filter on MACD (requested by Jakiś Typek)
// 2022-08-18
//  - added Option to show Stop Loss Positon based on lowest/highest price of last # of candles (requested by Jakiś Typek)

// **** ****
// This is a trimmed down version of the OG TMA Overlay with a focus on highlighting a couple specific candlestick patterns.
// This script is organized in a way that allows each pattern to be commented-out (disabled) or uncommented (enabled) pretty easily.
//
// The original request was for 3-Line-Strike only, so that is what will be enabled/uncommented by default,
// but the engulfing code will be left in as well so that it can be turned on easily if desired.
//
// TODO: Add in a trend filter based on the trend identification method Arty discusses in his video on this candle pattern. 
//
// NOTE: As-written, the symbols and 'alert()' functions are subject to repainting (firing without a confirmed signal at bar close).
//       This was done deliberately as a way to give people early-warning on live charts to help give them time to prepare for an entry,
//       which can be especially significant when scalping on low timeframe (1-5m) charts.
//       
//
//@version=5
indicator(title='Modded 3 Line Strike [TTF]', shorttitle='Modded 3LS [TTF]', overlay=true)

// Inputs

i_version = input.string(title="Modded 3LS Version", defval="18.08.2022", group='Info')
//
//### Moving Averages
//
i_emaLen1      = input.int(21, "MA1 Length", step=1, group='Moving Averages', tooltip="default 21, use 42 to have smooth moving average")
i_emaLen2      = input.int(50, "MA2 Length", step=1, group='Moving Averages', tooltip="default 50, use 100 to have smooth moving average")
i_emaLen3      = input.int(200, "MA3 Length", step=1, group='Moving Averages', tooltip="default 200, use 400 to have smooth moving average")
i_trendSelVwap = input.bool(title='Use VWAP instead of MA3', defval=false, group='Moving Averages', tooltip="Plot VWAP instead of MA3 as a Higher Timeframe Trend")
i_vwapSrc      = input.string("hlc3", "Source", options = ["open", "high", "low", "close", "hl2", "hlc3", "ohlc4", "hlcc4"], group='Moving Averages')
i_vwapAnchor   = input.string("Daily", "Anchor", options = ["Daily", "Weekly", "Monthly"], group='Moving Averages')
//
//### RSI Settings
//
i_rsiLen     = input.int(14, "RSI Length", step=1, group='RSI', tooltip="")
//
//### Stochastic RSI Settings
//
i_stochK      = input.int(3, "K", step=1, group='Stochatic RSI', tooltip="")
i_stochD      = input.int(3, "D", step=1, group='Stochatic RSI', tooltip="")
i_stochLen    = input.int(14, "Stochastic Length", step=1, group='Stochatic RSI', tooltip="")
i_stochRsiLen = input.int(8, "Stoch RSI Length", step=1, group='Stochatic RSI', tooltip="increase to 14, if useing highertime frames >5min")
//
//### MACD Settings
//
i_macdFastLength   = input(title="Fast Length", defval=12, group="MACD")
i_macdSlowLength   = input(title="Slow Length", defval=26, group="MACD")
i_macdSrc          = input(title="Source", defval=close, group="MACD")
i_macdSignalLength = input.int(title="Signal Smoothing",  minval = 1, maxval = 50, defval = 9, group="MACD")
i_macdSmaSource    = input.string(title="Oscillator MA Type",  defval="EMA", options=["SMA", "EMA"], group="MACD")
i_macdSmaSignal    = input.string(title="Signal Line MA Type", defval="EMA", options=["SMA", "EMA"], group="MACD")
//
// ### Stop Loss Indication
//
//
i_showSlIndicator = input.bool(title='Show Stop Loss Indicator', defval=true, group='Stop Loss', tooltip="Plot Stop Loss Postion Indicator for selected Signals")
i_slSwingBars     = input.int(3, title="No of Previous Bars for SL Swing", step=1, group='Stop Loss', tooltip="Number of previous bars to look for Swing based Stop Loss Indicator calculation")
i_slPoints        = input.float(20.0, title="Additonal Points Distance for SL", step=1, group='Stop Loss', tooltip="Number of Points to be added to the Swing Price")
//
// ### Filters
//
i_filterTrend    = input.bool(title='Filter Trend with EMA3', defval=true, group='Filters', tooltip="Plot Signals only, if price follows Trend (Close>EMA3 buy, Close<EMA3 sell)")
i_filterEmas     = input.bool(title='Filter EMA1 and EMA2 in order', defval=true, group='Filters', tooltip="Plot Signals only, if EMA1 and EMA2 are alinged in order")
i_filterRsi      = input.bool(title='Filter RSI Momentum', defval=true, group='Filters', tooltip="Plot Signals only, if RSI Momentum alignes with Trend (RSI>50 buy, RSI<50 sell)")
i_rsiBullish     = input.int(49, title="RSI Bullish Momentum", step=1, group='Filters', tooltip="Filter Criteria for Buy Signals")
i_rsiBearish     = input.int(51, title="RSI Bearish Momentum", step=1, group='Filters', tooltip="Filter Criteria for Sell Signals")
i_filterRsiExtended   = input.bool(title='Filter RSI Overextension', defval=true, group='Filters', tooltip="Plot Signals only, if RSI is not Overextended")
i_rsiExtendedBullish  = input.int(75, title="RSI Overextended Bullish", step=1, group='Filters', tooltip="Filter Criteria for Buy Signals (RSI>75 no buy)")
i_rsiExtendedBearish  = input.int(25, title="RSI Overextended Bearish", step=1, group='Filters', tooltip="Filter Criteria for Sell Signals (RSI<25 no sell)")
i_filterStochRsiLevel = input.bool(title='Filter Stochastic RSI Level', defval=true, group='Filters', tooltip="Plot Signals only, if StochRSI matches level (StochRSI<20 buy, StochRSI>80 sell)")
i_filterstochRsiSrc   = input(close, title="Stochastic RSI Source", group="Filters", tooltip="Sourch for Stochastic RSI calculation")
i_filterStochBullish  = input.int(20, title='Stochastic Oversold', step=1, group='Filters', tooltip="Filter Criteria for Buy Signals")
i_filterStochBearish  = input.int(80, title='Stochastic Overbought', step=1, group='Filters', tooltip="Filter Criteria for Sell Signals")
i_filterMacdHist      = input.bool(title='Filter MACD Histogram', defval=true, group='Filters', tooltip="Plot Signals only, if # of consecutive bars on MACD Histogram are negative for buys or positive for sells")
i_macdHistConsecBars  = input.int(3, title="MACD Consecutive Bars", step=1, group='Filters', tooltip="Number of consecutive bars for MACD Histogram to be inline")

//
// ### 3 Line Strike
//
showBear3LS = input.bool(title='Show Bearish 3 Line Strike', defval=true, group='3 Line Strike', tooltip="The Bearish 3 Line Strike (3LS-Bear) is a candlestick pattern comprised of 3 bullish (green) candles, " +
         "followed by a bearish engulfing candle (see 'Big A$$ Candles' below).\n\n" +
         "This pattern tends to be best used as a signal of the end of a retracement period as part of a trend continuation strategy.\n\n" +
         "Default: Checked")
showBull3LS = input.bool(title='Show Bullish 3 Line Strike', defval=true, group='3 Line Strike', tooltip="The Bullish 3 Line Strike (3LS-Bull) is a candlestick pattern comprised of 3 bearish (red) candles, " +
         "followed by a bullish engulfing candle (see 'Big A$$ Candles' below).\n\n" +
         "This pattern tends to be best used as a signal of the end of a retracement period as part of a trend continuation strategy.\n\n" +
         "Default: Checked")
showMemeChars = input.bool(title="Plot 3 Line Strike meme symbols", defval=false, group="3 Line Strike", tooltip="If disabled (default), standard shapes will be plotted instead, which can then be further customized on the 'Styles' tab of the indicator settings.\n\n" +
         "If enabled, meme icons hand-selected by Arty himself (🍆 and 🍑) will be plotted for 3LS signals instead of the more typical shapes.\n\n" +
         "Default: Unchecked")
//
//### Engulfing Candles
//
showBearEngulfing= input.bool(title='Show Bearish Big A$$ Candles', defval=false, group='Big A$$ Candles', tooltip="Bearish 'Big A$$ Candles' are the same as Bearish Engulfing candles.")
showBullEngulfing= input.bool(title='Show Bullish Big A$$ Candles', defval=false, group='Big A$$ Candles', tooltip="Bullish 'Big A$$ Candles' are the same as Bullish Engulfing candles.")
//
//### Alerts
//
// This won't actually DO anything, just popping it to note the behavior of the "Any alert() function call" alert type
void = input.bool(title="The new 'Any alert() function call' (dynamic) alerts will be based on what signals are enabled in the indicator settings.", defval=true, tooltip="This does nothing - it's only here to clarify alert functionality.")
//
// End Inputs ###

// Function definitions...
//
// Derive candle "color".  For simplicity, I originally wanted to make this a true/false and have any true doji candles (where open and close are exactly the same)
// either inherit the previous value, or invalidate the setup (which was the original behavior), but that behavior wasn't working as expected.  So for now, we're
// going to do a hack and make this a numeric return instead, where...
//
// -1 -> Red/Bearish
//  0 -> Doji
// +1 -> Green/BUllish
//
getCandleColorIndex(barIndex) =>
    int ret = na
    if (close[barIndex] > open[barIndex])
        ret := 1
    else if (close[barIndex] < open[barIndex])
        ret := -1
    else
        ret := 0
    ret

//
// Check for engulfing candles
isEngulfing(checkBearish) =>
    // In an effort to try and make this a bit more consistent, we're going to calculate and compare the candle body sizes
    // to inform the engulfing or not decision, and only use the open vs close comparisons to identify the candle "color"
    ret = false
    sizePrevCandle = close[1] - open[1] // negative numbers = red, positive numbers = green, 0 = doji
    sizeCurrentCandle = close - open    // negative numbers = red, positive numbers = green, 0 = doji
    isCurrentLagerThanPrevious = (math.abs(sizeCurrentCandle) > math.abs(sizePrevCandle)) ? true : false
    // We now have the core info to evaluate engulfing candles
    switch checkBearish
        true => 
            // Check for bearish engulfing (green candle followed by a larger red candle)
            isGreenToRed = ((getCandleColorIndex(0) < 0) and (getCandleColorIndex(1) > 0)) ? true : false
            ret := (isCurrentLagerThanPrevious and isGreenToRed) ? true : false
        false => 
            // Check for bullish engulfing (red candle followed by a larger green candle)
            isRedToGreen = ((getCandleColorIndex(0) > 0) and (getCandleColorIndex(1) < 0)) ? true : false
            ret := (isCurrentLagerThanPrevious and isRedToGreen) ? true : false
        => ret := false // This should be impossible to trigger...
    ret
//
// Helper functions that wraps the isEngulfing above...
isBearishEngulfuing() =>
    ret = isEngulfing(true)
    ret
//
isBullishEngulfuing() =>
    ret = isEngulfing(false)
    ret
//
// Functions to check for 3 consecutive candles of one color, followed by an engulfing candle of the opposite color
//
// Bearish 3LS = 3 green candles immediately followed by a bearish engulfing candle
is3LSBear() =>
    ret = false
    is3LineSetup = ((getCandleColorIndex(1) > 0) and (getCandleColorIndex(2) > 0) and (getCandleColorIndex(3) > 0)) ? true : false
    ret := (is3LineSetup and isBearishEngulfuing()) ? true : false
    ret
//
// Bullish 3LS = 3 red candles immediately followed by a bullish engulfing candle
is3LSBull() =>
    ret = false
    is3LineSetup = ((getCandleColorIndex(1) < 0) and (getCandleColorIndex(2) < 0) and (getCandleColorIndex(3) < 0)) ? true : false
    ret := (is3LineSetup and isBullishEngulfuing()) ? true : false
    ret

// ### Moving Averages
ema1 = ta.ema(close, i_emaLen1)
ema2 = ta.ema(close, i_emaLen2)
ema3 = ta.ema(close, i_emaLen3)

plot(ema1, color=color.rgb(178, 181, 190, 0), title='EMA1')
plot(ema2, color=color.rgb(91, 156, 246, 0), title='EMA2')

anchorSource = switch i_vwapSrc
	"open"  => open
	"high"  => high
	"low"   => low
	"close" => close
	"hl2"   => hl2
	"hlc3"  => hlc3
	"ohlc4" => ohlc4
	"hlcc4" => hlcc4
anchorTimeframe = switch i_vwapAnchor
	"Daily"   => "1D"
	"Weekly"  => "1W"
	"Monthly" => "1M"
anchor = timeframe.change(anchorTimeframe)
vwap = ta.vwap(anchorSource, anchor)

ma3 = if i_trendSelVwap
    vwap
else
    ema3

plot(ma3, color=color.rgb(149, 117, 205, 0), title='MA3', linewidth=2)

// ### RSI
rsi = ta.rsi(close, i_rsiLen)

// ### Filter Trend
var f_trendBullish = true
var f_trendBearish = true

if (i_filterTrend)
    f_trendBullish := close[1] > ma3
    f_trendBearish := close[1] < ma3

// ### Filter EMAs
var f_emaInOrderBullish = true
var f_emaInOrderBearish = true

if (i_filterEmas)
    f_emaInOrderBullish := ema1 > ema2
    f_emaInOrderBearish := ema1 < ema2

// ### Filter RSI
var f_rsiBullish = true
var f_rsiBearish = true

if (i_filterRsi)
    f_rsiBullish := rsi >= i_rsiBullish
    f_rsiBearish := rsi <= i_rsiBearish

// Filter Signals, if RSI is overextended.
var f_rsiNotExtendedBullish = true
var f_rsiNotExtendedBearish = true

// ### Filter RSI Overextended
if (i_filterRsiExtended)
    f_rsiNotExtendedBullish := rsi <= i_rsiExtendedBullish
    f_rsiNotExtendedBearish := rsi >= i_rsiExtendedBearish


// ### Filter Stochastic RSI
var f_stochRsiBullish = true
var f_stochRsiBearish = true

stochRsi = ta.rsi(i_filterstochRsiSrc, i_stochRsiLen)
stochK = ta.sma(ta.stoch(stochRsi, stochRsi, stochRsi, i_stochLen), i_stochK)
stochD = ta.sma(stochK, i_stochD)

if (i_filterStochRsiLevel)
    f_stochRsiBullish := stochK < i_filterStochBullish and stochD < i_filterStochBullish
    f_stochRsiBearish := stochK > i_filterStochBearish and stochD > i_filterStochBearish

// Todo check, if cross over happend at the last to candles
//ta.crossover(stochK, stochD)
//ta.crossunder(stochK, stochD)

// ### Filter MACD 
var f_macdHistogramBullish = true
var f_macdHistogramBearish = true

macdFastMa = i_macdSmaSource == "SMA" ? ta.sma(i_macdSrc, i_macdFastLength) : ta.ema(i_macdSrc, i_macdFastLength)
macdSlowMa = i_macdSmaSource == "SMA" ? ta.sma(i_macdSrc, i_macdSlowLength) : ta.ema(i_macdSrc, i_macdSlowLength)
macd = macdFastMa - macdSlowMa
macdSignal = i_macdSmaSignal == "SMA" ? ta.sma(macd, i_macdSignalLength) : ta.ema(macd, i_macdSignalLength)
macdHist = macd - macdSignal

if (i_filterMacdHist)
    f_macdHistogramBullish := ta.barssince(macdHist > 0.0) >= i_macdHistConsecBars
    f_macdHistogramBearish := ta.barssince(macdHist < 0.0) >= i_macdHistConsecBars

// ### Stop Loss Indicator
swingPriceH = ta.highest(high[1], i_slSwingBars) 
swingPriceL = ta.lowest(low[1], i_slSwingBars) 

atr = ta.atr(7)
atrPriceH = high + atr
atrPriceL = low - atr

// TODO: Add Option to calculate SL based on ATR

slPriceH = swingPriceH + syminfo.mintick * i_slPoints
slPriceL = swingPriceL - syminfo.mintick * i_slPoints

// ### 3 Line Strike
is3LSBearSig = is3LSBear() and f_trendBearish and f_emaInOrderBearish and f_rsiBearish and f_rsiNotExtendedBearish and f_stochRsiBearish and f_macdHistogramBearish
is3LSBullSig = is3LSBull() and f_trendBullish and f_emaInOrderBullish and f_rsiBullish and f_rsiNotExtendedBullish and f_stochRsiBullish and f_macdHistogramBullish

// Meme plots for the 3LS signal
plotchar(showBull3LS and showMemeChars ? is3LSBullSig : na, char="🍆", color=color.rgb(245, 124, 0, 50), location=location.belowbar, size=size.tiny, text='', title='3 Line Strike Up (Meme Icon)', editable=false)
plotchar(showBear3LS and showMemeChars ? is3LSBearSig : na, char="🍑", color=color.rgb(245, 124, 0, 50), location=location.abovebar, size=size.tiny, text='', title='3 Line Strike Down (Meme Icon)', editable=false)
//
// Standard plots for the 3LS signal
plotshape(showBull3LS and not showMemeChars ? is3LSBullSig : na, style=shape.triangleup, color=color.rgb(245, 124, 0, 50), location=location.belowbar, size=size.small, text='', title='3 Line Strike Up')
plotshape(showBear3LS and not showMemeChars ? is3LSBearSig : na, style=shape.triangledown, color=color.rgb(245, 124, 0, 50), location=location.abovebar, size=size.small, text='', title='3 Line Strike Down')
//


// 
// # Alerts
//
// Old-style Alert Conditions
alertcondition(showBull3LS and is3LSBullSig, title='Bullish 3 Line Strike', message='{{exchange}}:{{ticker}} {{interval}} - Bullish 3 Line Strike')
alertcondition(showBear3LS and is3LSBearSig, title='Bearish 3 Line Strike', message='{{exchange}}:{{ticker}} {{interval}} - Bearish 3 Line Strike')
//
// New-style alerts
if (showBull3LS and is3LSBullSig)
    m = syminfo.tickerid + ' ' + timeframe.period + ' - Bullish 3 Line Strike'
    alert(message=str.tostring(m), freq=alert.freq_once_per_bar_close)
if (showBear3LS and is3LSBearSig)
    m = syminfo.tickerid + ' ' + timeframe.period + ' - Bearish 3 Line Strike'
    alert(message=str.tostring(m), freq=alert.freq_once_per_bar_close)   

// End ###

//### Engulfing Candles

//If current bar open is less than equal to the previous bar close AND current bar open is less than previous bar open AND current bar close is greater than previous bar open THEN True
isBullEngulfingSig = isBullishEngulfuing() and f_trendBullish and f_emaInOrderBullish and f_rsiBullish and f_rsiNotExtendedBullish and f_stochRsiBullish and f_macdHistogramBullish
//If current bar open is greater than equal to previous bar close AND current bar open is greater than previous bar open AND current bar close is less than previous bar open THEN True
isBearEngulfingSig = isBearishEngulfuing() and f_trendBearish and f_emaInOrderBearish and f_rsiBearish and f_rsiNotExtendedBearish and f_stochRsiBearish and f_macdHistogramBearish

//bullishEngulfing/isBearEngulfingSig return a value of 1 or 0; if 1 then plot on chart, if 0 then don't plot
plotshape(showBullEngulfing ? isBullEngulfingSig : na, style=shape.triangleup, location=location.belowbar, color=color.rgb(245, 124, 0, 50), size=size.tiny, title='Big A$$ Candle Up')
plotshape(showBearEngulfing ? isBearEngulfingSig : na, style=shape.triangledown, location=location.abovebar, color=color.rgb(245, 124, 0, 50), size=size.tiny, title='Big A$$ Candle Down')

// Alerts
//
// Old-style alert conditions...
alertcondition(showBullEngulfing and isBullEngulfingSig, title='Bullish Engulfing', message='{{exchange}}:{{ticker}} {{interval}} - Bullish candle engulfing previous candle')
alertcondition(showBearEngulfing and isBearEngulfingSig, title='Bearish Engulfing', message='{{exchange}}:{{ticker}} {{interval}} - Bearish candle engulfing previous candle')
//
// New-style alert() functions
// New-style alerts
if (showBullEngulfing and isBullEngulfingSig)
    m = syminfo.tickerid + ' ' + timeframe.period + ' - Bullish candle engulfing previous candle'
    alert(message=str.tostring(m), freq=alert.freq_once_per_bar_close)
if (showBearEngulfing and isBearEngulfingSig)
    m = syminfo.tickerid + ' ' + timeframe.period + ' - Bearish candle engulfing previous candle'
    alert(message=str.tostring(m), freq=alert.freq_once_per_bar_close)  

// End ###

// ### Plot Stop Loss Indicator
float swingPrice = float(na)

if ( i_showSlIndicator and ( (showBear3LS and is3LSBearSig) or (showBearEngulfing and isBearEngulfingSig)) )
    swingPrice := slPriceH 
else if (i_showSlIndicator and ( (showBull3LS and is3LSBullSig) or (showBullEngulfing and isBullEngulfingSig)) )
    swingPrice := slPriceL
else
    swingPrice := float(na)
    
plot(swingPrice, color=color.yellow, title='Stop Loss', linewidth=3, style=plot.style_cross)  
