//version=5

// Revision: 3
// Author: CHIMKOO
// Strategy: Enter long when recent swing high breaks out, using recent swing low as stop level. Move stops up as higher lows print to act
// as trailing stops. Ride trend as long as it is there and the higher lows aren't breached.
// Conditions/Variables
// 1. Manually configure which dates to back test
// 2. Can add a filter to only take setups that are above (or below for shorts) user-defined moving average(s) (helps avoid trading counter trend)
// 3. Can ignore signals that are too extended and not consolidating enough based on ADR and/or RSI.
// 4. Color background of trades - helps to easily see at a glance if the strategy should be long or not.

// === CALL STRATEGY/STUDY, PROGRAMATICALLY ENTER STRATEGY PARAMETERS HERE SO YOU DON'T HAVE TO CHANGE THEM EVERY TIME YOU RUN A TEST ===
// (STRATEGY ONLY) - Comment out srategy() when in a study()
strategy('Breakout Trend Follower', overlay=true, initial_capital=10000, currency='USD', calc_on_every_tick = true,
default_qty_type=strategy.percent_of_equity, default_qty_value=100, commission_type=strategy.commission.percent, commission_value=0.1)

// === BACKTEST RANGE ===
Start = input.time(defval=timestamp('01 Jan 2019 06:00 +0000'), title='Backtest Start Date', group = "backtest window")
Finish = input.time(defval=timestamp('01 Jan 2100 00:00 +0000'), title='Backtest End Date', group = "backtest window")

// === USER INPUTS ===
pvtLb = input.int(defval=3, title='Pivot Lookback', minval=1, group = "support & resistance levels",
tooltip = "Number of bars to left and right to deterine local pivot.")
showPivotPoints = input.bool(title = "Show Historical Pivot Points?", defval = false, group = "support & resistance levels",
tooltip = "Toggle this on to see the historical pivot points that were used.")
currentColorS = input.color(color.new(color.orange,50), title = "Current Range S/R Colors:    Support",group = "support & resistance levels", inline = "lineColor")
currentColorR = input.color(color.new(color.blue,50), title = " Resistance", group = "support & resistance levels", inline = "lineColor")
useMaFilter = input.bool(title='Use Current Timeframe Moving Average for Filtering?', defval=true, group = "moving average filters",
tooltip='Signals will be ignored when price is under this moving average. The intent is to keep you out of bear periods and only buying when price is showing strength.')
maType = input.string(defval='SMA', options=['EMA', 'SMA'], group = "moving average filters",title='MA Type For Filtering')
maLength = input.int(defval=50, title='MA Period for Filtering', minval=1, group = "moving average filters", inline = "1ma")
ma1Color = input.color(color.new(color.purple, 60), title = " Color", group = "moving average filters", inline = "1ma")
useMaFilter2 = input.bool(title='Use Multi-Timeframe Moving Average for Filtering?', defval=false, group = "moving average filters",
tooltip='Signals will be ignored when price is under this moving average. The intent is to keep you out of bear periods and only buying when price is showing strength. Other timeframe allows you to use Daily moving average for higher level view of market strength.')
tfSet = input.timeframe(defval='D', title='Moving Average Time Frame', group = "moving average filters",
tooltip='Allows you to set a different time frame moving average to filter your signals by.')
ma2Type = input.string(defval='SMA', options=['EMA', 'SMA'], title='MA Type For Filtering', group = "moving average filters")
ma2Length = input.int(defval=50, title='MA Period for Filtering', minval=1, group = "moving average filters", inline = "2ma")
ma2Color = input.color(color.new(color.yellow, 60), title = " Color", group = "moving average filters", inline = "2ma")

// === MOVING AVERAGE CALCULATIONS ===
// Declare function to be able to swap out EMA/SMA
ma(maType, src, length) =>
maType == 'EMA' ? ta.ema(src, length) : ta.sma(src, length) //Ternary Operator (if maType equals EMA, then do ema calc, else do sma calc)
maFilter = ma(maType, close, maLength)
maFilter2 = request.security(syminfo.tickerid, tfSet, ma(ma2Type, close, ma2Length))
plot(useMaFilter ? maFilter : na, title='Trend Filter MA', color=ma1Color, linewidth=2, style=plot.style_line)
plot(useMaFilter2 ? maFilter2 : na, title='Trend Filter MA', color=ma2Color, linewidth=2, style=plot.style_line)

// === USE RSI FOR FILTERING ===
// The idea here is that you want to buy in a consolodating range for best risk/reward. With the RSI filter, you make sure that the previous bars' RSI
// level is not overbought/overextended before deciding to take a position on.
useRsiFilter = input.bool(title='Use RSI for Filtering?', defval=false, group = "rsi filtering",
tooltip='Signals will be ignored if the RSI level is above a user-defined level for overbought. This allows the user to ensure they are not buying something that is too extended and instead focus on names that are consolidating more.')
rsiTf = input.timeframe(defval='', title='RSI Timeframe', group = "rsi filtering",
tooltip='Allows you to set a different time frame for RSI to filter your signals by.')
rsiOB = input.int(defval=70, title='RSI Overbought Level', minval=1, group = "rsi filtering")
rsiVal = request.security(syminfo.tickerid, rsiTf, ta.rsi(close, 14))

// === PLOT SWING HIGH/LOW AND MOST RECENT LOW TO USE AS STOP LOSS EXIT POINT ===
// Get High and Low Pivot Points
ph = ta.pivothigh(high, pvtLb, pvtLb)
pl = ta.pivotlow(low, pvtLb, pvtLb)
highLevel = ta.valuewhen(ph, high[pvtLb], 0)
lowLevel = ta.valuewhen(pl, low[pvtLb], 0)
barsSinceHigh = ta.barssince(ph) + pvtLb
barsSinceLow = ta.barssince(pl) + pvtLb
timeSinceHigh = time[barsSinceHigh]
timeSinceLow = time[barsSinceLow]

//Removes color when there is a change to ensure only the levels are shown (i.e. no diagonal lines connecting the levels)
pvthis = fixnan(ph)
pvtlos = fixnan(pl)
hipc = ta.change(pvthis) != 0 ? na : color.maroon
lopc = ta.change(pvtlos) != 0 ? na : color.green

// Display Pivot lines
plot(showPivotPoints ? pvthis : na, color=hipc, linewidth=1, offset=-pvtLb, title="Top Levels")
plot(showPivotPoints ? pvthis : na, color=hipc, linewidth=1, offset=0, title="Top Levels 2")
plot(showPivotPoints ? pvtlos : na, color=lopc, linewidth=1, offset=-pvtLb, title="Bottom Levels")
plot(showPivotPoints ? pvtlos : na, color=lopc, linewidth=1, offset=0, title="Bottom Levels 2")

// == PLOT SUPPORT/RESISTANCE LINES FOR CURRENT CHART TIMEFRAME ==
// Use a function to define the lines
f_line(x1, y1, y2, _color) =>
var line id = na
line.delete(id)
id := line.new(x1, y1, time, y2, xloc.bar_time, extend.right, _color)

highLine = f_line(timeSinceHigh, highLevel, highLevel, currentColorR)
lowLine = f_line(timeSinceLow, lowLevel, lowLevel, currentColorS)

// === USE ADR FOR FILTERING ===
// The idea here is that you want to buy in a consolodating range for best risk/reward. So here you can compare the current distance between
// support/resistance vs. the ADR and make sure you aren't buying at a point that is too extended.
useAdrFilter = input.bool(title = "Use ADR for Filtering?", defval = false, group = "adr filtering",
tooltip = "Signals will be ignored if the distance between support and resistance is larger than a user-defined percentage of ADR (or monthly volatility
in the stock screener). This allows the user to ensure they are not buying something that is too extended and instead focus on names that are consolidating more.")
adrPerc = input.int(defval = 120, title = "% of ADR Value", minval = 1, group = "adr filtering")
tableLocation = input.string(defval="Bottom", options=["Top", "Bottom"], title = "ADR Table Visibility", group = "adr filtering",
tooltip = "Place ADR table on the top of the pane, the bottom of the pane, or off.")
adrValue = request.security(syminfo.tickerid, "D", ta.sma((high-low)/math.abs(low) * 100, 21), barmerge.gaps_off, barmerge.lookahead_on) // Monthly Volatility in Stock Screener (also ADR)
adrCompare = (adrPerc * adrValue) / 100

// == ADR TABLE ==
tablePos = tableLocation == "Top" ? position.top_right : position.bottom_right
var table adrTable = table.new(tablePos, 2, 1, border_width = 3)
lightTransp = 90
avgTransp = 80
heavyTransp = 70
posColor = color.rgb(38, 166, 154)
negColor = color.rgb(240, 83, 80)
volColor = color.new(#999999, 0)

f_fillCellVol(_table, _column, _row, _value) =>
_transp = math.abs(_value) > 7 ? heavyTransp : math.abs(_value) > 4 ? avgTransp : lightTransp
_cellText = str.tostring(_value, "0.00") + "%\n" + "ADR"
table.cell(_table, _column, _row, _cellText, bgcolor = color.new(volColor, _transp), text_color = volColor, width = 6)

srDistance = (highLevel - lowLevel)/highLevel * 100

f_fillCellCalc(_table, _column, _row, _value) =>
_c_color = _value >= adrCompare ? negColor : posColor
_transp = _value >= adrCompare*0.8 and _value <= adrCompare*1.2 ? lightTransp :
_value >= adrCompare*0.5 and _value < adrCompare*0.8 ? avgTransp :
_value < adrCompare*0.5 ? heavyTransp :
_value > adrCompare*1.2 and _value <= adrCompare*1.5 ? avgTransp :
_value > adrCompare*1.5 ? heavyTransp : na
_cellText = str.tostring(_value, "0.00") + "%\n" + "Range"
table.cell(_table, _column, _row, _cellText, bgcolor = color.new(_c_color, _transp), text_color = _c_color, width = 6)

if barstate.islast
f_fillCellVol(adrTable, 0, 0, adrValue)
f_fillCellCalc(adrTable, 1, 0, srDistance)

// Conditions for entry and exit
buyConditions = (useMaFilter ? highLevel > maFilter : true) and
(useMaFilter2 ? highLevel > maFilter2 : true) and
(useAdrFilter ? highLevel - lowLevel < adrValue : true) and
(useRsiFilter ? rsiVal[1] < rsiOB : true) and
time > Start and time < Finish

// (STRATEGY ONLY) Comment out for Study
strategy.entry('Long', strategy.long, stop=highLevel, when=buyConditions)
// strategy.entry("Long", strategy.long, stop = buyLevel2, when = time > Start and time < Finish and high > maFilterCheck)
strategy.exit('Exit Long', from_entry='Long', stop=lowLevel)
strategy.cancel('Long', when=not(buyConditions)) // Cancels resting orders when new swing high is printed below filters.
Technical Indicators

Disclaimer