TICK Scalping strategy, SPY 1 min
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © platsn

// How it works:
// When trading the indices, many rely on the TICK for market directions. This strategy is a trend following strategy that uses a combination of conditions using the following indicators:
// - TICK
// - RSI
// - VIX volatility index
// - EMA

// For entries, the conditions are:
// 1. TICK moving average crossover with a delayed signal line
// 2. Bullish or bearish RSI signal, RSI > 50 for bullish , < 50 for bearish
// 3. VIX must be above a certain threshold to take advantage of high market volatility
// 4. Price must be on top of EMA line for long, and below for short
// 5. Price has not moved significantly already (this delta can be set)

// For exits, there are 3 scenarios:
// 1. Stop loss set by a percentage of the daily ATR value
// 2. Trend changes on the TICK and the RSI
// 3. Bearish or bullish divergence on price with TICK

// This strategy automatically signal to close all trades at 3:50 pm EST at the end of the day.

// Extras:
// - There is an option to show P/L for reinvesting profits

//@version=5
strategy("TICK strategy, SPY 1 min", overlay=false, pyramiding=3, initial_capital=5000, commission_type=strategy.commission.cash_per_contract, commission_value = 0.0625) 

indicator_only = input.bool(false,"Use As Indicator Only", tooltip = "Disables strategy signals")

// ******************** Trade Period **************************************
startY = input(title='Start Year', defval=2011, group = "Trading window")
startM = input.int(title='Start Month', defval=1, minval=1, maxval=12, group = "Trading window")
startD = input.int(title='Start Day', defval=1, minval=1, maxval=31, group = "Trading window")
finishY = input(title='Finish Year', defval=2050, group = "Trading window")
finishM = input.int(title='Finish Month', defval=12, minval=1, maxval=12, group = "Trading window")
finishD = input.int(title='Finish Day', defval=31, minval=1, maxval=31, group = "Trading window")
timestart = timestamp(startY, startM, startD, 00, 00)
timefinish = timestamp(finishY, finishM, finishD, 23, 59)
t1 = time(timeframe.period, "0930-1545:23456")
window = time >= timestart and time <= timefinish and t1 ? true : false 
t2 = time(timeframe.period, "0930-1555:23456")
window2 = time >= timestart and time <= timefinish and t2 ? true : false 

trade_options = input.bool(defval=true,title="Trading Options only", group = "Trading Options")
option_amt = input.int(defval=10,title="# of options per trade", group = "Trading Options")
option_multiplier = input.float(0.007, title="Multiplier for trading options (adjust to approx. options P&L)", step=0.001, group = "Trading Options")
reinvest = input.bool(defval=false,title="Reinvest profit", group = "Trading Options")
reinvest_percent = input.float(defval=20, title = "Reinvest percentage", group="Trading Options")
trade_TICKdiverg = input.bool(true, title="Trade with TICK divergences", group = "Trading Options")
diverg_lookback = input.int(defval=5, title="TICK divergence lookback period (only if trading TICK divergence", group = "Trading Options")
trade_optimal = input.bool(true, title="Trade with optimal Stochastic", group = "Trading Options")
entry_lookback = input.int(defval=12, title="Lookback period for entry condition, waiting for Stochastic", group = "Trading Options")
k_entry_thres = input.int(defval=0, title="Stochastic RSI k value offset from 50 for entry threshold (ie. 10 means long entry under k=60", group = "Trading Options")
trade_chase = input.bool(true, title="Trade with Chasing entries", group = "Trading Options")
chase_lookback = input.int(defval=10, title="Chase entry lookback period", group = "Trading Options")
k_chase_thres = input.int(defval=30, title="Stochastic RSI k value offset from 50 for chase entry threshold (ie. 30 means long entry under k=80", group = "Trading Options")
trade_PT = input.bool(true, title="Trade with Price Target", group = "Trading Options")
no_longat10 = input.bool(false, title="No entry between 10 - 10:30", group = "Trading Options")
hold_ON = input.bool(true, title="Hold 1/2 position overnight on Tues/Thurs", group = "Trading Options")

// ***************************************************************************************************** Daily ATR *****************************************************
atrlen = input.int(4, minval=1, title="ATR period", group = "Daily ATR")
SL_atr_perc = input.float(9, minval=1, maxval=100, step=0.5, title="% ATR to use for SL", group = "Daily ATR")
PT_atr_perc = input.float(17.5, minval=1, maxval=100, step=0.5, title="% ATR to use for PT (only if trading w/ PT)", group = "Daily ATR")
 
SL_atr = SL_atr_perc * 0.01
PT_atr = PT_atr_perc * 0.01
datr = request.security(syminfo.tickerid, "1D", ta.rma(ta.tr, atrlen))
d_SL_atr = datr * SL_atr
d_PT_atr = datr * PT_atr

// plot(datr,"Daily ATR")
// plot(d_SL_atr, "Daily % ATR")

//*********************************************************** VIX volatility index ****************************************

VIX = request.security("VIX", timeframe.period, close)
vix_thres = input.float(20.0, "VIX Threshold for entry", step=0.5, group="VIX Volatility Index")

// ***************************************************** TICK Indictor *****************************************************

malen1 = input.int(21, title = "Fast EMA period", group = "TICK Indicator")
malen2 = input.int(102, title = "Slow EMA period", group = "TICK Indicator")
malen3 = input.int(135, title = "Hull MA period", group = "TICK Indicator")
malen4 = input.int(25, title = "Hull MA Smoothing", group = "TICK Indicator")

//get TICK data
// tickO = request.security("USI:TICK", timeframe.period, open)
tickH = request.security("USI:TICK", "5", high)
tickC = request.security("USI:TICK", "5", close)
tickL = request.security("USI:TICK", "5", low)
tickAVG = ta.ema(tickC,malen1)
tickAVG_L = ta.ema(tickC,malen2)

f_hma(_src, _length) =>
    _return = ta.wma(2 * ta.wma(_src, _length / 2) - ta.wma(_src, _length), math.round(math.sqrt(_length)))
    _return

tickHMA = f_hma(tickC, malen3)
tickHMA_MA = ta.sma(tickHMA, malen4)

tick_bull = tickAVG > tickAVG[5]
tick_bear = tickAVG < tickAVG[5]

//Plot Tick data
plot(barstate.isconfirmed? tickC : na, title='TICK', color = color.new(color.white,30) , style=plot.style_line, linewidth = 1) //color = tickC > 0 ? color.green : color.red
plot(tickAVG, title='TICK 9 EMA', color = color.yellow, style=plot.style_line, linewidth = 1)
plot(tickAVG_L, title='TICK 21 EMA', color = color.new(color.red,70), style=plot.style_line, linewidth = 1)
plot(tickHMA, title='TICK 21 HMA', color = color.blue, style=plot.style_line, linewidth = 2)
plot(tickHMA_MA, title='TICK HMA Smoothed', color = color.purple, style=plot.style_line, linewidth = 2)
hline(0, color=color.white, linewidth = 1, linestyle = hline.style_solid)
hline(1000, color=color.green, linestyle=hline.style_solid, linewidth = 2)
hline(500, color=color.gray, linestyle=hline.style_dashed)
hline(-1000, color=color.red, linestyle=hline.style_solid, linewidth = 2)
hline(-500, color=color.gray, linestyle=hline.style_dashed)

// plotshape(ta.crossunder(tickHMA,tickAVG) ,"Cross down", color=color.red, style=shape.triangledown, location = location.top, size =size.tiny)
// plotshape(ta.crossover(tickHMA,tickAVG) ,"Cross up", color=color.green, style=shape.triangleup, location = location.bottom, size =size.tiny)
plotshape(ta.crossunder(tickHMA,tickHMA_MA) ,"Cross down", color=color.red, style=shape.triangledown, location = location.top, size =size.tiny)
plotshape(ta.crossover(tickHMA,tickHMA_MA) ,"Cross up", color=color.green, style=shape.triangleup, location = location.bottom, size =size.tiny)

//Background
bgcolor(tickAVG_L > 0 ? color.new(color.green, 90) : color.new(color.red, 90))
bgcolor(tickC > 499 ? color.new(color.green, 70) : na)
bgcolor(tickC < -499 ? color.new(color.red, 70) : na)
bgcolor(tickC > 999 ?color.new(color.green,30) : na)
bgcolor(tickC < -999 ? color.new(color.red,30) : na)


lbR = input(title='Pivot Lookback Right', defval=5, group = "TICK Divergence")
lbL = input(title='Pivot Lookback Left', defval=10, group = "TICK Divergence")

rangeUpper = input(title='Max of Lookback Range', defval=300, group = "TICK Divergence")
rangeLower = input(title='Min of Lookback Range', defval=25, group = "TICK Divergence")
plotBull = input(title='Plot Bullish', defval=true, group = "TICK Divergence")
plotHiddenBull = input(title='Plot Hidden Bullish', defval=false, group = "TICK Divergence")
plotBear = input(title='Plot Bearish', defval=true, group = "TICK Divergence")
plotHiddenBear = input(title='Plot Hidden Bearish', defval=false, group = "TICK Divergence")

bearColor = color.red
bullColor = color.green
hiddenBullColor = color.new(color.green, 80)
hiddenBearColor = color.new(color.red, 80)
textColor = color.white
noneColor = color.new(color.white, 100)

plFound = na(ta.pivotlow(tickC, lbL, lbR)) ? false : true
phFound = na(ta.pivothigh(tickC, lbL, lbR)) ? false : true

_inRange(cond) =>
    bars = ta.barssince(cond == true)
    rangeLower <= bars and bars <= rangeUpper


//----------------------------------------------- TICK Divergence -------------------------------
// Regular Bullish

// Osc: Higher Low
oscHL = tickC[lbR] > ta.valuewhen(plFound, tickC[lbR], 1) and _inRange(plFound[1])

// Price: Lower Low
priceLL = low[lbR] < ta.valuewhen(plFound, low[lbR], 1)

bullCond = plotBull and priceLL and oscHL and plFound

plot(plFound ? tickC[lbR] : na, offset=-lbR, title='Regular Bullish', linewidth=2, color=bullCond ? bullColor : noneColor)

plotshape(bullCond ? tickC[lbR] : na, offset=-lbR, title='Regular Bullish Label', text=' Bull ', style=shape.labelup, location=location.absolute, color=color.new(bullColor, 0), textcolor=color.new(textColor, 0))

//------------------------------------------------------------------------------
// Hidden Bullish

// Osc: Lower Low
oscLL = tickC[lbR] < ta.valuewhen(plFound, tickC[lbR], 1) and _inRange(plFound[1])

// Price: Higher Low
priceHL = low[lbR] > ta.valuewhen(plFound, low[lbR], 1)

hiddenBullCond = plotHiddenBull and priceHL and oscLL and plFound

plot(plFound ? tickC[lbR] : na, offset=-lbR, title='Hidden Bullish', linewidth=2, color=hiddenBullCond ? hiddenBullColor : noneColor)

plotshape(hiddenBullCond ? tickC[lbR] : na, offset=-lbR, title='Hidden Bullish Label', text=' H Bull ', style=shape.labelup, location=location.absolute, color=color.new(bullColor, 0), textcolor=color.new(textColor, 0))


//------------------------------------------------------------------------------
// Regular Bearish

// Osc: Lower High
oscLH = tickC[lbR] < ta.valuewhen(phFound, tickC[lbR], 1) and _inRange(phFound[1])

// Price: Higher High
priceHH = high[lbR] > ta.valuewhen(phFound, high[lbR], 1)

bearCond = plotBear and priceHH and oscLH and phFound

plot(phFound ? tickC[lbR] : na, offset=-lbR, title='Regular Bearish', linewidth=2, color=bearCond ? bearColor : noneColor)

plotshape(bearCond ? tickC[lbR] : na, offset=-lbR, title='Regular Bearish Label', text=' Bear ', style=shape.labeldown, location=location.absolute, color=color.new(bearColor, 0), textcolor=color.new(textColor, 0))

//------------------------------------------------------------------------------
// Hidden Bearish

// Osc: Higher High
oscHH = tickC[lbR] > ta.valuewhen(phFound, tickC[lbR], 1) and _inRange(phFound[1])

// Price: Lower High
priceLH = high[lbR] < ta.valuewhen(phFound, high[lbR], 1)

hiddenBearCond = plotHiddenBear and priceLH and oscHH and phFound

plot(phFound ? tickC[lbR] : na, offset=-lbR, title='Hidden Bearish', linewidth=2, color=hiddenBearCond ? hiddenBearColor : noneColor)

plotshape(hiddenBearCond ? tickC[lbR] : na, offset=-lbR, title='Hidden Bearish Label', text=' H Bear ', style=shape.labeldown, location=location.absolute, color=color.new(bearColor, 0), textcolor=color.new(textColor, 0))


//-------------------------------------------------------- RSI ------------------------------------

src = close //request.security(syminfo.ticker, "5",close)
rsi_len = input.int(70, minval=1, title="RSI Length", group = "RSI")
rsi_ma_len = input.int(6, minval=1, title="RSI Signal Length", group = "RSI")
rsi_up = ta.rma(math.max(ta.change(src), 0), rsi_len)
rsi_down = ta.rma(-math.min(ta.change(src), 0), rsi_len)
rsi = rsi_down == 0 ? 100 : rsi_up == 0 ? 0 : 100 - (100 / (1 + rsi_up / rsi_down))
rsi_ma = ta.wma(rsi,rsi_ma_len)

rsi_avg = rsi_ma//math.avg(rsi,rsi_ma)

rsi_bull = rsi_avg > rsi_avg[1]
rsi_bear = rsi_avg < rsi_avg[1]

rsi_thres = input.int(15,"RSI Overbought/Oversold levels +/- from 50", group = "RSI")

rsi_len2 = input.int(14, minval=1, title="Fast RSI Length", group = "RSI")
rsi_up2 = ta.rma(math.max(ta.change(src), 0), rsi_len2)
rsi_down2 = ta.rma(-math.min(ta.change(src), 0), rsi_len2)
rsi2 = rsi_down2 == 0 ? 100 : rsi_up2 == 0 ? 0 : 100 - (100 / (1 + rsi_up2 / rsi_down2))

//-------------------------------------------------------- Stochastic RSI ------------------------------------

// Stochastic Main Parameters
smoothK = input.int(3, minval=1, title='Stoch K', group = "Stochastic RSI")
smoothD = input.int(3, minval=1, title='Stoch D', group = "Stochastic RSI")
lengthRSI = input.int(14, "RSI Length", minval=1, group = "Stochastic RSI")
lengthStoch = input.int(14, "Stochastic Length", minval=1, group = "Stochastic RSI")
rsi_stoch = ta.rsi(src, lengthRSI)
k = ta.sma(ta.stoch(rsi_stoch, rsi_stoch, rsi_stoch, lengthStoch), smoothK)
d = ta.sma(k, smoothD)

//-------------------------------------------------------- Moving Average ------------------------------------

ma_src = close

emalen1 = input.int(20, minval=1, title='Filtering EMA', group= "Moving Averages")
ema1 = ta.ema(ma_src, emalen1)


//------------------------------------------------------------- Trade options ------------------------
profit = strategy.netprofit
trade_amount = math.floor(strategy.initial_capital / close) 

BSLE()=>
    strategy.opentrades > 0 ? (bar_index - strategy.opentrades.entry_bar_index(strategy.opentrades-1)) : na
    
BSLExit()=>
    strategy.opentrades == 0 ? (bar_index - strategy.closedtrades.exit_bar_index(strategy.closedtrades-1)) : na

min_hold = input.int(6,"Minimum bars to hold order", group = "Trading Options")

if trade_options 
    if strategy.netprofit > 0 and reinvest// and strategy.closedtrades.profit(strategy.closedtrades-1) > 0
        trade_amount := math.floor(strategy.initial_capital * option_multiplier) * (option_amt + math.floor(((profit*reinvest_percent*0.01)/strategy.initial_capital)*10))
    else
        trade_amount := math.floor(strategy.initial_capital * option_multiplier) * option_amt

// **************************************************************************************************************************************** Trade Pauses ****************************************
bool trade_pause = false
bool trade_pause2 = false

trade_pause := false

if no_longat10 and time(timeframe.period, "1000-1030:23456")
    trade_pause2 := true
else
    trade_pause2 := false
     

// ---------------------------------------------------------------- Entry Conditions ------------------------------------------------------------

L_entry0 = ta.crossover(tickHMA, tickHMA_MA) and rsi_avg > 50  and rsi < 50 + rsi_thres and VIX > vix_thres and tickAVG > 0 and close > ema1 
S_entry0 = ta.crossunder(tickHMA,tickHMA_MA) and rsi_avg < 50  and rsi > 50 - rsi_thres and VIX > vix_thres and tickAVG < 0 and close < ema1

L_entry1 = L_entry0
S_entry1 = S_entry0 

BSL = ta.barssince(L_entry0)
BSS = ta.barssince(S_entry0)

// plot(BSL, "Bars since long entry condition")
// plot(BSS, "Bars since short entry condition")

if trade_optimal and strategy.opentrades == 0
    if BSL < entry_lookback
        L_entry1 := k < 50 + k_entry_thres and k > k[1] 
    else
        L_entry1 := L_entry0
    if BSS < entry_lookback
        S_entry1 := k > 50 - k_entry_thres and k < k[1]
    else
        S_entry1 := S_entry0

// plotshape(L_entry0 ,"Long entry condition", color=color.blue, style=shape.triangleup, location = location.bottom, size =size.tiny)
// plotshape(S_entry0 ,"Short entry condition", color=color.orange, style=shape.triangledown, location = location.top, size =size.tiny)

L_entry2 = bullCond and tickHMA > tickHMA_MA and rsi > rsi_ma and VIX > vix_thres and tickAVG > 0 and close > ema1 
S_entry2 = bearCond and tickHMA < tickHMA_MA and rsi < rsi_ma and VIX > vix_thres and tickAVG < 0 and close < ema1 

L_entry3 = BSLExit() < chase_lookback and close > close[BSLExit()] and tickHMA > tickHMA_MA and tickHMA > tickHMA[1] and rsi_avg > 50  and rsi < 50 + rsi_thres and VIX > vix_thres and tickAVG > tickAVG[1] and close > ema1 and k < 50 + k_chase_thres 
S_entry3 = BSLExit() < chase_lookback and close < close[BSLExit()] and tickHMA < tickHMA_MA and tickHMA < tickHMA[1] and rsi_avg < 50  and rsi > 50 - rsi_thres and VIX > vix_thres and tickAVG < tickAVG[1] and close < ema1 and k > 50 - k_chase_thres 

if not indicator_only
    if L_entry1 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed
        strategy.entry("Long", strategy.long, trade_amount, comment="Long") 
        
    if trade_TICKdiverg and L_entry2 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed and strategy.opentrades == 0 //limit to 1 entry, ignoring pyramiding
        strategy.entry("Long", strategy.long, trade_amount, comment="Long, TICK divergence")

    if trade_chase and L_entry3 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed and strategy.opentrades == 0 //limit to 1 entry, ignoring pyramiding
        strategy.entry("Long", strategy.long, trade_amount, comment="Long, Chase")

    if S_entry1 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed
        strategy.entry("Short", strategy.short, trade_amount, comment="Short")

    if trade_TICKdiverg and S_entry2 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed and strategy.opentrades == 0 //limit to 1 entry, ignoring pyramiding
        strategy.entry("Short", strategy.short, trade_amount, comment="Short, TICK divergence")

    if trade_chase and S_entry3 and window and not(trade_pause) and not(trade_pause2) and barstate.isconfirmed and strategy.opentrades == 0 //limit to 1 entry, ignoring pyramiding
        strategy.entry("Short", strategy.short, trade_amount, comment="Short, Chase")

// ---------------------------------------------------------------- SL / PT -------------------------------------------------------------------

long_SL = ta.lowest(low,5) - d_SL_atr
short_SL = ta.highest(high,5) + d_SL_atr

if strategy.opentrades > 0 
    long_SL := math.min(long_SL[BSLE()], low[1] - d_SL_atr)
    short_SL := math.max(short_SL[BSLE()], high[1] + d_SL_atr)

// plot(long_SL, "Long SL")
// plot(short_SL,"Short SL")

long_PT = close + d_PT_atr
short_PT = close - d_PT_atr

if strategy.opentrades > 0 
    long_PT := strategy.opentrades.entry_price(0) + d_PT_atr
    short_PT := strategy.opentrades.entry_price(0) - d_PT_atr

// ---------------------------------------------------------------- Exit Conditions ------------------------------------------------------------

L_exit1 = tickHMA < tickHMA_MA and rsi_bear
S_exit1 = tickHMA > tickHMA_MA and rsi_bull

L_exit2 = bearCond
S_exit2 = bullCond

L_exit3 = tickH > 1000
S_exit3 = tickL < -1000

L_exit4 = strategy.opentrades.profit(strategy.opentrades-1) > 0 and tickHMA < tickHMA[1] and rsi2 < 50
S_exit4 = strategy.opentrades.profit(strategy.opentrades-1) > 0 and tickHMA > tickHMA[1] and rsi2 > 50

if BSLE() > min_hold and window2
    if trade_PT
        strategy.exit("Exit", "Long", stop = long_SL, limit = long_PT, comment = "SL/PT hit") 
        strategy.exit("Exit", "Short", stop = short_SL, limit = short_PT, comment = "SL/PT hit")
    else
        strategy.exit("Exit", "Long", stop = long_SL, comment = "SL hit") 
        strategy.exit("Exit", "Short", stop = short_SL, comment = "SL hit")
    strategy.close("Long", when = L_exit1, comment = "Exit, TICK/RSI reversal") 
    strategy.close("Short", when = S_exit1, comment = "Exit, TICK/RSI reversal")
    strategy.close("Long", when = L_exit2, comment = "Exit, Bearish Divergence") 
    strategy.close("Short", when = S_exit2, comment = "Exit, Bullish Divergence")
    strategy.close("Long", when = L_exit3, comment = "Exit, TICK overbought") 
    strategy.close("Short", when = S_exit3, comment = "Exit, TICK oversold")
    // strategy.close("Long", when = L_exit4, comment = "Exit, TICK/RSI reversal 2") 
    // strategy.close("Short", when = S_exit4, comment = "Exit, TICK/RSI reversal 2")
    
if hold_ON
    if time(timeframe.period, "1550-1551:246")
        strategy.close_all()
    if time(timeframe.period, "1550-1551:35") 
        strategy.close("Long", qty_percent = 50, comment="Close 1/2 EOD")
        strategy.close("Short", qty_percent = 50, comment="Close 1/2 EOD")

else
    if time(timeframe.period, "1550-1551:23456")
        strategy.close_all()