DR/IDR Case Study [TFO]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © tradeforopp

//@version=5
indicator("DR/IDR Case Study [TFO]", "DR/IDR Case Study [TFO]", true, max_lines_count = 500, max_labels_count = 500, max_boxes_count = 500)

var g1 = "DR Session"
dr_timeframe = input.timeframe("5", "Timeframe", tooltip = "This timeframe will be used to define the DR and IDR, even on different chart timeframes", group = g1)
session = input.session("0930-1030", "Defining Range", tooltip = "New York local time", group = g1)
session_ext = input.session("1030-1600", "Session", tooltip = "New York local time", group = g1)
dr_criteria = input.string(defval = 'Wick', title = "Success Criteria", options = ['Wick', 'Close'], tooltip = "Close will check if price invalidated the DR session by closing through the opposing side of the range. Wick will use any break of the opposing side, not just a close", group = g1)

var g2 = "Style"
dr_style = input.string(defval = 'Solid', title = "DR Style", options = ['Solid', 'Dotted', 'Dashed'], inline = "DR", group = g2)
dr_color = input.color(color.gray, "", inline = "DR", group = g2)
idr_style = input.string(defval = 'Dashed', title = "IDR Style", options = ['Solid', 'Dotted', 'Dashed'], inline = "IDR", group = g2)
idr_color = input.color(color.green, "", inline = "IDR", group = g2)
table_position = input.string('Top Right', "Table Position", options = ['Bottom Center', 'Bottom Left', 'Bottom Right', 'Middle Center', 'Middle Left', 'Middle Right', 'Top Center', 'Top Left', 'Top Right'], group = g2)

// Variables
var dr_not_broken = true

var line drh = na
var line drl = na
var line idrh = na
var line idrl = na
var box dr_box = na

var float lod = na
var float hod = na
var float clod = na
var float chod = na

var int track_ruling = 0
var int track_total = 0

dr_time = not na(time(dr_timeframe, session, "America/New_York"))
dr_extension = not na(time(dr_timeframe, session_ext, "America/New_York"))

dr_style := switch dr_style
    'Solid' => line.style_solid
    'Dotted' => line.style_dotted
    'Dashed' => line.style_dashed

idr_style := switch idr_style
    'Solid' => line.style_solid
    'Dotted' => line.style_dotted
    'Dashed' => line.style_dashed

table_position := switch table_position
    "Bottom Center" => position.bottom_center
    "Bottom Left" => position.bottom_left
    "Bottom Right" => position.bottom_right
    "Middle Center" => position.middle_center
    "Middle Left" => position.middle_left
    "Middle Right" => position.middle_right
    "Top Center" => position.top_center
    "Top Left" => position.top_left
    "Top Right" => position.top_right

[dr_tf_open, dr_tf_high, dr_tf_low, dr_tf_close] = request.security(syminfo.tickerid, dr_timeframe, [open, high, low, close], barmerge.gaps_off, barmerge.lookahead_on)

// Initiate new DR
if dr_time and not dr_time[1]
    dr_not_broken := true
    lod := dr_tf_low
    hod := dr_tf_high
    clod := dr_tf_close
    chod := dr_tf_close

    drh := line.new(bar_index, dr_tf_high, bar_index + 1, dr_tf_high, color = dr_color, style = dr_style)
    drl := line.new(bar_index, dr_tf_low, bar_index + 1, dr_tf_low, color = dr_color, style = dr_style)
    idrh := line.new(bar_index, dr_tf_close, bar_index + 1, dr_tf_close, color = idr_color, style = idr_style)
    idrl := line.new(bar_index, dr_tf_close, bar_index + 1, dr_tf_close, color = idr_color, style = idr_style)
    dr_box := box.new(bar_index, math.max(dr_tf_close, dr_tf_open), bar_index + 1, math.min(dr_tf_close, dr_tf_open), border_color = na, bgcolor = color.new(color.gray, 70))

// Manage current DR
if not na(drh)
    if dr_time or dr_extension
        line.set_x2(drh, bar_index)
        line.set_x2(drl, bar_index)
        line.set_x2(idrh, bar_index)
        line.set_x2(idrl, bar_index)
        
        if dr_tf_low < lod
            lod := dr_tf_low
        if dr_tf_high > hod
            hod := dr_tf_high
        if dr_tf_close < clod
            clod := dr_tf_close
        if dr_tf_close > chod
            chod := dr_tf_close

    if dr_time
        box.set_right(dr_box, bar_index)

        if dr_tf_high > line.get_y1(drh)
            line.set_y1(drh, dr_tf_high)
            line.set_y2(drh, dr_tf_high)
            
        if dr_tf_low < line.get_y1(drl)
            line.set_y1(drl, dr_tf_low)
            line.set_y2(drl, dr_tf_low)
        
        body_high = math.max(dr_tf_close, dr_tf_open)
        if body_high > line.get_y1(idrh)
            line.set_y1(idrh, body_high)
            line.set_y2(idrh, body_high)
            box.set_top(dr_box, body_high)
            
        body_low = math.min(dr_tf_close, dr_tf_open)
        if body_low < line.get_y1(idrl)
            line.set_y1(idrl, body_low)
            line.set_y2(idrl, body_low)
            box.set_bottom(dr_box, body_low)


// Check if DR was broken (5m close through DRH/DRL)
break_high = false
break_low = false

if dr_extension and dr_not_broken
    if dr_tf_close > line.get_y1(drh)
        break_high := true
        dr_not_broken := false
    if dr_tf_close < line.get_y1(drl)
        break_low := true
        dr_not_broken := false

if break_high
    label.new(bar_index, dr_tf_high, "Close Through DRH", style = label.style_label_down, color = color.blue, textcolor = color.white)
if break_low
    label.new(bar_index, dr_tf_low, "Close Through DRL", style = label.style_label_up, color = color.blue, textcolor = color.white)

// Check if DR rule succeeded
// ------------------------------
// Note that part of the claimed 88% success rate is due to the fact that it's NOT accounting for days with high impact news drivers
// I am NOT accounting for high impact news days in this indicator
// ------------------------------
high_more_recent = ta.barssince(break_high) < ta.barssince(break_low)
if not dr_not_broken[1] and dr_extension[1] and not dr_extension
    if high_more_recent
        ruling_criteria = dr_criteria == 'Wick' ? lod[1] : clod[1]
        ruling = line.get_y1(drl[1]) <= ruling_criteria
        label_string = ruling ? "Held" : "Not Held"
        label.new(bar_index - 1, dr_tf_low[1], "DRL " + label_string, style = label.style_label_up, color = ruling ? color.green : color.red, textcolor = color.white)
        track_total := track_total + 1
        if ruling
            track_ruling := track_ruling + 1
    else
        ruling_criteria = dr_criteria == 'Wick' ? hod[1] : chod[1]
        ruling = line.get_y1(drh[1]) >= ruling_criteria
        label_string = ruling ? "Held" : "Not Held"
        label.new(bar_index - 1, dr_tf_high[1], "DRH " + label_string, style = label.style_label_down, color = ruling ? color.green : color.red, textcolor = color.white)
        track_total := track_total + 1
        if ruling
            track_ruling := track_ruling + 1

var table stats = table.new(table_position, 10, 10)

if barstate.islast
    table.cell(stats, 0, 0, "DR Success Rate: ", bgcolor = color.new(color.white, 20))
    table.cell(stats, 1, 0, str.tostring(track_ruling / track_total * 100, format.percent), bgcolor = color.new(color.white, 20))

    table.cell(stats, 0, 1, "Total Sessions: ", bgcolor = color.new(color.white, 30))
    table.cell(stats, 1, 1, str.tostring(track_total), bgcolor = color.new(color.white, 30))