Gap Rider
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Emanuele Ruscio


//@version=4
study("Gap Rider", overlay=true)


//Configure Days pf the week variable

isMon() => dayofweek(time('D')) == dayofweek.monday
isTue() => dayofweek(time('D')) == dayofweek.tuesday
isWed() => dayofweek(time('D')) == dayofweek.wednesday
isThu() => dayofweek(time('D')) == dayofweek.thursday
isFri() => dayofweek(time('D')) == dayofweek.friday
isSat() => dayofweek(time('D')) == dayofweek.saturday
isSun() => dayofweek(time('D')) == dayofweek.sunday

//Configure Days of the week Input
showMon = input(true, title="Show Monday?", group = "========= Days of the Week =========")
showTue = input(true, title="Show Tuesday?", group = "========= Days of the Week =========")
showWed = input(true, title="Show Wednesday?", group = "========= Days of the Week =========")
showThu = input(true, title="Show Thursday?", group = "========= Days of the Week =========")
showFri = input(true, title="Show Friday?", group = "========= Days of the Week =========")


//Inputs
//==============================================================================

Lookback = input(10, title = "Loockback length for Statistics", group = "========= Lookback =========")
Select_Gap_Up = input(title = "Show Gap-up Flags", defval = true, group = "========= Gap Select =========", inline = "x")
Select_Gap_Down = input(title = "Show Gap-down Flags", defval = true, group = "========= Gap Select =========", inline = "x")

ATRThreshold_toggle = input(title = "Limit Gaps Within ATR Values", defval = false, group = "========= ATR Configure =========")
ATRUpper = input(title = "ATR Upper Threshold Value", defval = 50, step = 0.5, minval = 0, group = "========= ATR Configure =========", tooltip = " When 'Limit Gaps Within ATR Values' is turned on, only show Gaps below this value, and above Lower Threshold")
ATRLower = input(title = "ATR Lower Threshold Value", defval = 20, step = 0.5, minval = 0, group = "========= ATR Configure =========", tooltip = " When 'Limit Gaps Within ATR Values' is turned on, only show Gaps above this value, and below Upper Threshold")
ATRlength = input(title="Length", defval=14, minval=1, group = "========= ATR Configure =========")
smoothing = input(title="Smoothing", defval="RMA", options=["RMA", "SMA", "EMA", "WMA"], group = "========= ATR Configure =========")


Percent_Points = input(title = "Percent or Points for Gap Spread", defval = "Points", options = ["Percent", "Points"], group = "========= Gap Measurement =========")
gap_minimum = input(5, title = "Minimum difference in Price for Gap to occur (Flag)", step = 0.01, group = "========= Gap Measurement =========")
Confirm_Spread = input(5, title = "Minimum Distance for Gap Confirmation (Box)", step = 0.01, group = "========= Breakeven Measurement=========")
Large_Gap_toggle = input(title = "Only Show Large Gaps", defval = false, group = "========= Large Gap =========", tooltip = "Only show Gaps that form outside of the previous candle")






ATR_Lower_Limit = ATRThreshold_toggle ? ATRLower : 0
ATR_Upper_Limit = ATRThreshold_toggle ? ATRUpper : 99999

ma_function(source, ATRlength) =>
	if smoothing == "RMA"
		rma(source, ATRlength)
	else
		if smoothing == "SMA"
			sma(source, ATRlength)
		else
			if smoothing == "EMA"
				ema(source, ATRlength)
			else
				wma(source, ATRlength)
//plot(ma_function(tr(true), ATRlength), title = "ATR", color=color.new(#B71C1C, 0))

ATR = ma_function(tr(true), ATRlength)

//Variables
gap_value = abs(open - close[1])
var gap_min = 0.0
if (Percent_Points == "Percent")
    gap_min := close * (gap_minimum/100) 
else
    gap_min := gap_minimum 


Large_Gap = Large_Gap_toggle ? open > high[1] or open < low[1] : true
//==============================================================================
//INDICATOR 1 - Trigger
//==============================================================================
//Gap logic
gap       = open != close[1] and ((isMon() and showMon) or (isTue() and showTue) or (isWed() and showWed) or (isThu() and showThu) or (isFri() and showFri)) and gap_value > gap_min and ATR > ATR_Lower_Limit and ATR < ATR_Upper_Limit and Large_Gap
gap_up    = open > close[1] and ((isMon() and showMon) or (isTue() and showTue) or (isWed() and showWed) or (isThu() and showThu) or (isFri() and showFri)) and gap_value > gap_min and Select_Gap_Up and gap
gap_down  = open < close[1] and ((isMon() and showMon) or (isTue() and showTue) or (isWed() and showWed) or (isThu() and showThu) or (isFri() and showFri)) and gap_value > gap_min and Select_Gap_Down and gap

gap_confirm_up   = gap_up   and abs(close - open) >  Confirm_Spread
gap_confirm_down = gap_down and abs(close - open) >  Confirm_Spread
Gap_Complete_up =   gap_confirm_up   and gap_up   ? close > open and abs(open - close[1]) > gap_min ? "Win" : close < open and abs(open - close[1]) > gap_min ? "Lose" : na : na
Gap_Complete_down = gap_confirm_down and gap_down ? close < open and abs(open - close[1]) > gap_min ? "Win" : close > open and abs(open - close[1]) > gap_min ? "Lose" : na : na
No_Gap = not gap

Gap_Neutral = gap_value > gap_min and (Gap_Complete_up == na and Gap_Complete_down == na) and ((isMon() and showMon) or (isTue() and showTue) or (isWed() and showWed) or (isThu() and showThu) or (isFri() and showFri)) and (gap_up or gap_down)


//☐ ☑ ☒  


//==============================================================================
//Plot Gap Flag
gap_color = gap_up ? color.green : gap_down ? color.red : color.yellow
plotchar(series = gap and (gap_up or gap_down), char = "⚑", color = gap_color, title = "Gap Flag")

//Plot Gap Boxes
plotchar(title = "Winning Gap-up Box", series = Gap_Complete_up == "Win" and gap_up , char = "☑", color = color.green, location = location.belowbar)
plotchar(title = "Losing Gap-up Box", series = Gap_Complete_up == "Lose" and gap_up, char = "☒", color = color.red,   location = location.belowbar)
plotchar(title = "Break Even Gap Box", series =Gap_Neutral and (gap_up or gap_down) , char = "☐", color = color.yellow, location = location.belowbar)
plotchar(title = "Winning Gap-down Box", series = Gap_Complete_down == "Win" and gap_down , char = "☑", color = color.green, location = location.belowbar)
plotchar(title = "Losing Gap-down Box" , series = Gap_Complete_down == "Lose" and gap_down, char = "☒", color = color.red,   location = location.belowbar)
 
 //==============================================================================
 
//=========Consecutive Wins=========
Candles_Since_Win = barssince(Gap_Complete_up == "Win" or Gap_Complete_down == "Win")
Candles_Since_Lose = barssince(Gap_Complete_up == "Lose" or Gap_Complete_down == "Lose" or Gap_Neutral)
Candles_Since_Gap = barssince(No_Gap)


//Iterate Tally
var Win_Tally = 0
Consec_Win = if Candles_Since_Win == 0
    Win_Tally := Win_Tally + 1 
else if Candles_Since_Lose == 0 
    Win_Tally := 0 
else if Candles_Since_Gap == 0 
    Win_Tally := Win_Tally[1]

//Configure Array
var c = array.new_int(Lookback, 0)

for i =  (Lookback - 1) to 0
    array.shift(c)
    array.push(c,Consec_Win[i])

//=========Consecutive Losses=========
Candles_Since_Win_2 = barssince(Gap_Complete_up == "Win" or Gap_Complete_down == "Win" or Gap_Neutral)
Candles_Since_Lose_2 = barssince(Gap_Complete_up == "Lose" or Gap_Complete_down == "Lose")

//Iterate Tally
var Loss_Tally = 0
Consec_Loss = if Candles_Since_Lose_2 == 0
    Loss_Tally := Loss_Tally + 1 
else if Candles_Since_Win_2 == 0 
    Loss_Tally := 0 
else if Candles_Since_Gap == 0 
    Loss_Tally := Loss_Tally[1]

//Configure Array
var d = array.new_int(Lookback, 0)

for i =  (Lookback - 1) to 0
    array.shift(d)
    array.push(d,Consec_Loss[i])
    
    
    
//=========Consecutive Push=========
Candles_Since_Win_3 = barssince(Gap_Complete_up == "Win" or Gap_Complete_down == "Win")
Candles_Since_Lose_3 = barssince(Gap_Complete_up == "Lose" or Gap_Complete_down == "Lose")
Candles_Since_Push = barssince(Gap_Neutral and gap)

//Iterate Tally
var Push_Tally = 0
Consec_Push = if Candles_Since_Push == 0
    Push_Tally := Push_Tally + 1 
else if Candles_Since_Lose_3 == 0 or Candles_Since_Win_3 == 0 
    Push_Tally := 0 
else if Candles_Since_Gap == 0 
    Push_Tally := Push_Tally[1]

//Configure Array
var e = array.new_int(Lookback, 0)

for i =  (Lookback - 1) to 0
    array.shift(e)
    array.push(e,Consec_Push[i])
    

    
//========= Drawdown Calc=========

//Iterate Tally

// Tally = 0
// var DD_Tally = 0
// DD_Push = if gap and (Gap_Complete_up == "Win" or Gap_Complete_down == "Win" and (gap_up or gap_down))
//     if DD_Tally == 0
//         DD_Tally := 0
//     else    
//         DD_Tally := DD_Tally + 1 
// else if gap and (Gap_Complete_up == "Lose" or Gap_Complete_down == "Lose" and (gap_up or gap_down))
//     DD_Tally := DD_Tally -1 
// else
//     DD_Tally := DD_Tally + 0



//Configure Array
var f = array.new_int(Lookback, 0)

// for i =  (Lookback - 1) to 0
//     array.shift(f)
//     array.push(f,DD_Push[i])
    
    
    
DD_Tally_2 = 0   
for i = (Lookback - 1) to 0
    DD_Push = if gap[i] and (Gap_Complete_up[i] == "Win" or Gap_Complete_down[i] == "Win" and (gap_up[i] or gap_down[i]))
        if DD_Tally_2 == 0
            DD_Tally_2 := DD_Tally_2 + 0
        else    
            DD_Tally_2 := DD_Tally_2 + 1 
    else if gap[i] and (Gap_Complete_up[i] == "Lose" or Gap_Complete_down[i] == "Lose" and (gap_up[i] or gap_down[i]))
        DD_Tally_2 := DD_Tally_2 - 1 
    else if Gap_Neutral[i] and (gap_up[i] or gap_down[i])
        DD_Tally_2 := DD_Tally_2 + 0
    else
        DD_Tally_2 := DD_Tally_2
    array.shift(f)
    array.push(f,DD_Push)
    

    
// lab = ""
// for i = 0 to array.size(f) - 1
//     lab := lab + tostring(array.get(f, i)) + " "
     
// l = label.new(bar_index, close, lab)
// label.delete(l[1])  
//==============================================================================

// Initiate Gaps Statistics
positive_gaps = 0
negative_gaps = 0
total_gaps = 0
Profit_gaps = 0
Push_gaps = 0
Lose_gaps = 0
Gap_Up_Wins = 0
Gap_up_Even = 0
Gap_Up_Loss = 0
Gap_down_Wins = 0
Gap_down_Even = 0
Gap_down_Loss = 0
Consecutive_Win = 0

//Calcualte Variables For Stats Table
for i = 0 to (Lookback - 1)
    if gap_up[i] and gap[i]
        positive_gaps := positive_gaps + 1
    if gap_down[i] and gap[i]
        negative_gaps := negative_gaps + 1
    if gap[i] and (gap_up[i] or gap_down[i])
        total_gaps := total_gaps + 1
    if (Gap_Complete_up[i] == "Win") or (Gap_Complete_down[i] == "Win")
        Profit_gaps := Profit_gaps + 1
    if Gap_Neutral[i] and gap[i]
        Push_gaps := Push_gaps + 1
    if (Gap_Complete_up[i] == "Lose") or (Gap_Complete_down[i] == "Lose")
        Lose_gaps := Lose_gaps + 1
    if gap_up[i] and Gap_Complete_up[i] == "Win"
        Gap_Up_Wins := Gap_Up_Wins + 1
    if gap_up[i] and Gap_Neutral[i]
        Gap_up_Even := Gap_up_Even + 1
    if gap_up[i] and Gap_Complete_up[i] == "Lose"
        Gap_Up_Loss := Gap_Up_Loss + 1  
    if gap_down[i] and Gap_Complete_down[i] == "Win"
        Gap_down_Wins := Gap_down_Wins + 1  
    if gap_down[i] and Gap_Neutral[i]
        Gap_down_Even := Gap_down_Even + 1 
    if gap_down[i] and Gap_Complete_down[i] == "Lose"
        Gap_down_Loss := Gap_down_Loss + 1 
        

//Plot Vertical line at lookback period
Line = line.new(bar_index[Lookback], low[Lookback] - close[Lookback]/20, bar_index[Lookback], high[Lookback] + close[Lookback]/20, color = color.red, style = line.style_dotted)
line.delete(Line[1])

//==============================================================================

//Configure Stats Table
Test_Table = table.new(position = position.bottom_right, columns = 1, rows = 17, bgcolor = #000000, frame_color = color.white, frame_width = 2)

table.cell(Test_Table, column = 0, row = 0, text_color = color.white, text_halign = text.align_center,  text ="================== Statistics ==================")
table.cell(Test_Table, column = 0, row = 1, text_color = color.white, text_halign = text.align_left,  text ="Total number of Gaps: "              + tostring(total_gaps)    + " (" + tostring(round((total_gaps/Lookback)*100,2))         + "% of sessions)")
table.cell(Test_Table, column = 0, row = 2, text_color = color.white, text_halign = text.align_left,  text ="Total number of Gaps-up: "           + tostring(positive_gaps) + " (" + tostring(round((positive_gaps/Lookback)*100,2))      + "% of sessions)")
table.cell(Test_Table, column = 0, row = 3, text_color = color.white, text_halign = text.align_left,  text ="Total number of Gaps-down: "         + tostring(negative_gaps) + " (" + tostring(round((negative_gaps/Lookback)*100,2))      + "% of sessions)")
table.cell(Test_Table, column = 0, row = 4, text_color = color.green, text_halign = text.align_left,  text ="Total number of Profitable Gaps: "   + tostring(Profit_gaps)   + " (" + tostring(round((Profit_gaps/total_gaps)*100,2))        + "% of Gaps)")
table.cell(Test_Table, column = 0, row = 5, text_color = color.yellow, text_halign = text.align_left,  text ="Total number of Break Even Gaps: "   + tostring(Push_gaps)     + " (" + tostring(round((Push_gaps/total_gaps)*100,2))          + "% of Gaps)")
table.cell(Test_Table, column = 0, row = 6, text_color = color.red, text_halign = text.align_left,  text ="Total number of Losing Gaps: "       + tostring(Lose_gaps)     + " (" + tostring(round((Lose_gaps/total_gaps)*100,2))          + "% of Gaps)")
table.cell(Test_Table, column = 0, row = 7, text_color = color.white, text_halign = text.align_left,  text ="Total number of Winning Gaps-Up: "   + tostring(Gap_Up_Wins)   + " (" + tostring(round((Gap_Up_Wins/positive_gaps)*100,2))   + "% of Gaps-up)")
table.cell(Test_Table, column = 0, row = 8, text_color = color.white, text_halign = text.align_left,  text ="Total number of Neutral Gaps-Up: "   + tostring(Gap_up_Even)   + " (" + tostring(round((Gap_up_Even/positive_gaps)*100,2))   + "% of Gaps-up)")
table.cell(Test_Table, column = 0, row = 9, text_color = color.white, text_halign = text.align_left,  text ="Total number of Losing Gaps-Up: "    + tostring(Gap_Up_Loss)   + " (" + tostring(round((Gap_Up_Loss/positive_gaps)*100,2))   + "% of Gaps-up)")
table.cell(Test_Table, column = 0, row = 10, text_color = color.white, text_halign = text.align_left,  text ="Total number of Winning Gaps-Down: " + tostring(Gap_down_Wins) + " (" + tostring(round((Gap_down_Wins/negative_gaps)*100,2)) + "% of Gaps-down)")
table.cell(Test_Table, column = 0, row = 11, text_color = color.white, text_halign = text.align_left, text ="Total number of Neutral Gaps-Down: " + tostring(Gap_down_Even) + " (" + tostring(round((Gap_down_Even/negative_gaps)*100,2)) + "% of Gaps-down)")
table.cell(Test_Table, column = 0, row = 12, text_color = color.white, text_halign = text.align_left, text ="Total number of Losing Gaps-Down: "  + tostring(Gap_down_Loss) + " (" + tostring(round((Gap_down_Loss/negative_gaps)*100,2)) + "% of Gaps-down)")
table.cell(Test_Table, column = 0, row = 13, text_color = color.green, text_halign = text.align_left, text ="Max Consecutive Winning Gaps: "      + tostring(array.max(c)))
table.cell(Test_Table, column = 0, row = 14, text_color = color.yellow, text_halign = text.align_left, text ="Max Consecutive Break Even: "        + tostring(array.max(e)))
table.cell(Test_Table, column = 0, row = 15, text_color = color.red, text_halign = text.align_left, text ="Max Consecutive Losing Gaps: "       + tostring(array.max(d)))
table.cell(Test_Table, column = 0, row = 16, text_color = color.red, text_halign = text.align_left, text ="Max Drawdown: "       + tostring(array.min(f)))


