Entry Assistant & News Alert
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © ivanroyloewen

//@version=5
indicator("Entry & News", overlay = true)



// Entry Assistant



//-------------------------------------------------------------------------------------------------------------------------------( General Inputs / Variables ) ---------------------------------------------

var direction = "Direction"

// are you planning to go Long ( displays data to assist entering Long positions)
var going_Long = input(title = "Long", defval = true, inline = direction, group = direction)
// are you planning to go Short ( displays data to assist entering Short positions)
var going_Short = input(title = "Short", defval = false, inline = direction, group = direction)

var show = "Show"

// display the stop loss line
var show_Stop = input(title = "Stop", defval = true, inline = show, group = show)
// display the BOC line
var show_BOC = input(title = "BOC", defval = true, inline = show, group = show)
// display the entry signal ( when current candle breaks previous candle )
var show_Entry_Signal = input(title = "Entry", defval = true, inline = show, group = show)
// only display the last three digits of the price ( on the price label )
var show_Only_Last_Three_Digits = input(title = "Three Digits Only", defval = true, inline = show, group = show)

//-------------------------------------------------------------------------------------------------------------------------------( Long Inputs ) ------------------------------------------------------------

var variables_Long = "Long Variables"

// how much price below the current candle do you want to Stop line placed
var pips_Below_Candle_Stop_Long = input.float(title = "Pips spacing below candle for stop loss", defval = 1.0, minval = 0, maxval = 1000, group = variables_Long)
// how much price above the Long BOC line until Long entry signal is displayed
var pips_Above_BOC_Entry_Long = input.float(title = "Pips spacing above BOC for entry signal", defval = 0.1, minval = 0, maxval = 1000, group = variables_Long)
// how many candles ahead on the x axis is the Long price label placed
var label_Distance_Long = input.int(title = "Label horizontal distance", defval = 20, minval = 0, maxval = 100, group = variables_Long)

// Long Colors
var colors_Long = "Long Colors"

// the color of the Long Stop line
var line_Color_Long = input.color(title = "Stop", defval = #5b9cf6, inline = colors_Long)
// the color of the Long BOC line
var line_BOC_Color_Long = input.color(title = "BOC", defval = #ac774f, inline = colors_Long)
// the color of the Long entry signal
var entry_Signal_Color_Long = input.color(title = "Entry", defval = color.green, inline = colors_Long)
// the color of the Long label
var label_Color_Long = input(title = "Label", defval = #3179f5, inline = colors_Long)
// the color of the Long label text
var labe_Text_Color_Long = input(title = "Text", defval = color.white, inline = colors_Long)

//-------------------------------------------------------------------------------------------------------------------------------( Short Inputs ) -----------------------------------------------------------

var variables_Short = "Short Variables"

// how much price above the current candle do you want to Stop line placed
var pips_Below_Candle_Stop_Short = input.float(title = "Pips spacing above candle for stop loss", defval = 1.0, minval = 0, maxval = 1000, group = variables_Short )
// how much price below the Short BOC line until Short entry signal is displayed
var pips_Above_BOC_Entry_Short = input.float(title = "Pips spacing below BOC for entry signal", defval = 0.1, minval = 0, maxval = 1000, group = variables_Short )
// how many candles ahead on the x axis is the Short price label placed
var label_Distance_Short = input.int(title = "Label horizontal distance", defval = 20, minval = 0, maxval = 100, group = variables_Short )

var colors_Short = "Short Colors"

// the color of the Short Stop line
var line_Color_Short = input.color(title = "Stop", defval = #5b9cf6, inline = colors_Short)
// the color of the Short BOC line
var line_BOC_Color_Short = input.color(title = "BOC", defval = #ac774f, inline = colors_Short)
// the color of the Short entry signal
var entry_Signal_Color_Short = input.color(title = "Entry", defval = color.green, inline = colors_Short)
// the color of the Short label
var label_Color_Short = input(title = "Label", defval = #3179f5, inline = colors_Short)
// the color of the Short label text
var label_Text_Color_Short = input(title = "Text", defval = color.white, inline = colors_Short)

//-------------------------------------------------------------------------------------------------------------------------------( Price Calculation ) -----------------------------------------------------------

var price_Digit_Count = str.length(str.tostring(syminfo.mintick))

// the time frame, calculated by the time between the current and previous candle
time_Frame = time - time[1]

// rounds numbers
Truncate(number, decimals) =>
    factor = math.pow(10, decimals)
    int(number * factor) / factor

// get the price value from the given inputs
pips_Price_Value_Stop_Long = syminfo.mintick * Truncate(pips_Below_Candle_Stop_Long, 1) * 10
pips_Price_Value_BOC_Long = syminfo.mintick * Truncate(pips_Above_BOC_Entry_Long, 1) * 10

// calculate the new line positions with the pips price value
price_Stop_Long = low - pips_Price_Value_Stop_Long
price_BOC_Entry_Long = high[1] + pips_Price_Value_BOC_Long

// the position where the Long label will be initially placed
long_Label_Point = chart.point.new(time = time, index = bar_index, price = price_Stop_Long)
long_Label_Text = str.tostring(price_Stop_Long)
// check if the current label text has less character than the instruments max digits price ( this means a "0" zero has been rounded off )
if str.length(long_Label_Text) < price_Digit_Count
    // get the amount of zeros that have been rounded off
    int amount_Of_Zeros_Rounded_Off = price_Digit_Count - str.length(long_Label_Text)
    // add as many zeros back on that have been rounded off
    for i = 1 to amount_Of_Zeros_Rounded_Off by 1
        // add the zero back onto the label text
        long_Label_Text += "0"

// get the price value from the given inputs
pips_Price_Value_Stop_Short = syminfo.mintick * Truncate(pips_Below_Candle_Stop_Short, 1) * 10
pips_Price_Value_BOC_Short = syminfo.mintick * Truncate(pips_Above_BOC_Entry_Short, 1) * 10

// calculate the new line positions with the pips price value
price_Stop_Short = high + pips_Price_Value_Stop_Short
price_BOC_Entry_Short = low[1] - pips_Price_Value_BOC_Short

// the position where the Short label will be initially placed
short_Label_Point = chart.point.new(time = time, index = bar_index, price = price_Stop_Short)
short_Label_Text = str.tostring(price_Stop_Short)
// check if the current label text has less character than the instruments max digits price ( this means a "0" zero has been rounded off )
if str.length(short_Label_Text) < price_Digit_Count
    // get the amount of zeros that have been rounded off
    int amount_Of_Zeros_Rounded_Off = price_Digit_Count - str.length(short_Label_Text)
    // add as many zeros back on that have been rounded off
    for i = 1 to amount_Of_Zeros_Rounded_Off by 1
        // add the zero back onto the label text
        short_Label_Text += "0"
//-------------------------------------------------------------------------------------------------------------------------------( Handle Long Data / Drawings ) --------------------------------------------

// if you are going Long
if going_Long
    // draw the Long Stop line below the current candle
    stop_Line_Long = line.new(bar_index[1], price_Stop_Long, bar_index, price_Stop_Long, xloc.bar_index, extend.right, line_Color_Long, line.style_solid, 2)
    // delete the previous Long Stop line
    line.delete(stop_Line_Long[1])
    // if you are showing the Long BOC ( Break Of Candle ) line
    if show_BOC
        // draw the Long BOC line at the previous candles high
        line_Long_BOC = line.new(bar_index[1], high[1], bar_index, high[1], xloc.bar_index, extend.right, line_BOC_Color_Long, line.style_solid, 2)
        // delete the previous Long BOC line
        line.delete(line_Long_BOC[1])
    // set the default value to ERR ( error )
    last_Three_Characters = "ERR"
    // only show the last three digits of the stop loss price
    if show_Only_Last_Three_Digits
        // remove the decimal when only showing three digshow_Only
        long_Label_Text := str.replace( long_Label_Text , target = ".", replacement = "")
    // check for this condition to prevent compialation errors
    if str.length(long_Label_Text) - 3 < 0 == false
        // get the last 3 characters from the Long Stop line price
        last_Three_Characters := str.substring(long_Label_Text, str.length(long_Label_Text) - 3)
    // draw the Long label displaying the price of the Long line ( display only last 3 characters if specified )
    label_Long = label.new(point = long_Label_Point, style = label.style_label_up, text = show_Only_Last_Three_Digits ? last_Three_Characters : long_Label_Text, color = label_Color_Long, textcolor = labe_Text_Color_Long, xloc = xloc.bar_time, size = size.huge)
    // adjust the Long label x position based off of input
    label_Long.set_x(time + label_Distance_Long * time_Frame)
    // delete the previous Long label
    label.delete(label_Long[1])
    // draw a box when the current price has broken above the Long BOC line ( plus the additional pips from input)
    BOC_Signal = box.new(bar_index[1], high , bar_index, price_BOC_Entry_Long, border_color = na, bgcolor = entry_Signal_Color_Long, extend = extend.right)
    // delete the previous Long BOC signal box
    box.delete(BOC_Signal[1])
    // if show entry signal is false or the current candles high is not above the previous candles high
    if show_Entry_Signal == false or high > price_BOC_Entry_Long == false
        // delete the current Long BOC signal box
        box.delete(BOC_Signal)

//-------------------------------------------------------------------------------------------------------------------------------( Handle Short Data / Drawings ) -------------------------------------------

// if you are going Short
if going_Short
    // draw the Short Stop line above the current candle
    stop_Line_Short = line.new(bar_index[1], price_Stop_Short, bar_index, price_Stop_Short, xloc.bar_index, extend.right, line_Color_Short, line.style_solid, 2)
    // delete the previous Short Stop line
    line.delete(stop_Line_Short[1])
    // if you are showing the BOC ( Break Of Candle ) line
    if show_BOC
        // draw the Short BOC line at the previous candles low
        line_Short_BOC = line.new(bar_index[1], low[1], bar_index, low[1], xloc.bar_index, extend.right, line_BOC_Color_Short, line.style_solid, 2)
        // delete the previous Short BOC line
        line.delete(line_Short_BOC[1])
    // set the default value to ERR ( error )
    last_Three_Characters = "ERR"
    // only show the last three digits of the stop loss price
    if show_Only_Last_Three_Digits
        // remove the decimal when only showing three digshow_Only
        short_Label_Text := str.replace( short_Label_Text , target = ".", replacement = "")
    // check for this condition to prevent compialation errors
    if str.length(short_Label_Text) - 3 < 0 == false// or str.length(short_Label_Text) - 3 < str.length(short_Label_Text) - 1
        // get the last 3 characters from the Short Stop line price
        last_Three_Characters := str.substring(short_Label_Text, str.length(short_Label_Text) - 3)
    // draw the Short label displaying the price of the Short line ( display only last 3 characters if specified )
    label_Short = label.new(point = short_Label_Point, style = label.style_label_down, text = show_Only_Last_Three_Digits ? last_Three_Characters : short_Label_Text, color = label_Color_Short, textcolor = label_Text_Color_Short, xloc = xloc.bar_time, size = size.huge)
    // adjust the Short label x position based off of input
    label_Short.set_x(time + label_Distance_Short * time_Frame)
    // delete the previous Short label  
    label.delete(label_Short[1])
    // draw a box when the current price has broken below the Short BOC line ( minus the additional pips from input)
    BOC_Signal = box.new(bar_index[1], price_BOC_Entry_Short, bar_index, low, border_color = na, bgcolor = entry_Signal_Color_Short, extend = extend.right)
    // delete the previous Short BOC signal box
    box.delete(BOC_Signal[1])
    // if show entry signal is false or the current candles low is not below the previous candles low
    if show_Entry_Signal == false or low < price_BOC_Entry_Short == false
        // delete the current Short BOC signal box
        box.delete(BOC_Signal)
    //------------------------------------------------------------------------------------------------------------------------------- ( Execute Functions) ----------------------------------------------------



// News Alert




//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------( inputs )

var news = " News Alert"

// the offest of your personal timezone from Eastern Standard Time ( New York Time )
var time_Zone_Offset = input.int(defval = 0, title = "Timezone offset in hours from EST", maxval = 24, minval = -24, group = news)

// the inputs for the first news alert
var news_Time_1 = "News Time 1"
var news_Hour_1 = input.int(defval = 00, title = "Hour", group = news_Time_1, inline = news_Time_1, minval = 0, maxval = 24)
var news_Minute_1 = input.int(defval = 00, title = "Minute", group = news_Time_1, inline = news_Time_1, minval = 0, maxval = 45, step = 15)
var show_News_1 = input.bool(defval = false, title = "Show News 1", group = news_Time_1, inline = news_Time_1)

// the inputs for the second news alert
var news_Time_2 = "News Time 2"
var news_Hour_2 = input.int(defval = 00, title = "Hour", group = news_Time_2, inline = news_Time_2, minval = 0, maxval = 24)
var news_Minute_2 = input.int(defval = 00, title = "Minute", group = news_Time_2, inline = news_Time_2, minval = 0, maxval = 45, step = 15)
var show_News_2 = input.bool(defval = false, title = "Show News 2", group = news_Time_2, inline = news_Time_2)

// the inputs for the third news alert
var news_Time_3 = "News Time 3"
var news_Hour_3 = input.int(defval = 00, title = "Hour", group = news_Time_3, inline = news_Time_3, minval = 0, maxval = 24)
var news_Minute_3 = input.int(defval = 00, title = "Minute", group = news_Time_3, inline = news_Time_3, minval = 0, maxval = 45, step = 15)
var show_News_3 = input.bool(defval = false, title = "Show News 3", group = news_Time_3, inline = news_Time_3)

// the inputs for the warning period before and after the news release
var news_Warning = "News Warning Period"
var minutes_Warning_Before_News = input.int(defval = 10, title = "Minutes Before", group = news_Warning, inline = news_Warning, minval = 0, maxval = 65, step = 5)
var minutes_Warning_After_News = input.int(defval = 10, title = "Minutes After", group = news_Warning, inline = news_Warning, minval = 0, maxval = 65, step = 5)

// the inputs for the news line settings
var line_Settings = "News Line Settings"
var line_Color = input.color(defval = color.rgb(255, 255, 255), title = "Color", inline = line_Settings, group = line_Settings)
var line_Style = input.string(defval = "Dotted", title="Style", options=["Dotted", "Dashed", "Solid"], group = line_Settings, inline = line_Settings)
var line_Width = input.int(defval = 1, title = "Width", minval = 1, maxval = 10, inline = line_Settings, group = line_Settings)

// the inputs for the warning area surrounding the news release
var box_Settings = "News Area Settings"
var box_Background_Color = input.color(defval = color.rgb(0, 0, 255, 75), title = "Background Color", inline = box_Settings, group = box_Settings)
var box_Show = input.bool(defval = true, title = "Show Area", inline = box_Settings, group = box_Settings)

// set the line style using the string input
var line_Style_Selected = line.style_dotted

if line_Style == "Dotted"
    line_Style_Selected := line.style_dotted
else if line_Style == "Dashed"
    line_Style_Selected := line.style_dashed
else if line_Style == "Solid"
    line_Style_Selected := line.style_solid

//---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------( time calculations )

// the current time of the current bar
current_Bar_Time = time

// News 1 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// get the time stamps for the first news warning, release, before and after
news_Time_Stamp_Release_1 = timestamp(year, month, dayofmonth, news_Hour_1 + time_Zone_Offset, news_Minute_1, 00)
news_Time_Stamp_Before_1 = timestamp(year, month, dayofmonth, news_Hour_1 + time_Zone_Offset, news_Minute_1 - minutes_Warning_Before_News, 00)
news_Time_Stamp_After_1 = timestamp(year, month, dayofmonth, news_Hour_1 + time_Zone_Offset, news_Minute_1 + minutes_Warning_After_News, 00)

// create bools to indicate where price is currently relatated to the first news warning, used for calculations
bool bar_Is_In_News_1 = current_Bar_Time >= news_Time_Stamp_Before_1 and current_Bar_Time <= news_Time_Stamp_After_1 and show_News_1
bool bar_Is_Before_News_1 = current_Bar_Time < news_Time_Stamp_Release_1 and current_Bar_Time >= news_Time_Stamp_Before_1
bool bar_Is_News_Release_1 = current_Bar_Time == news_Time_Stamp_Release_1
bool bar_Is_News_Ongoing_1 = current_Bar_Time > news_Time_Stamp_Release_1 and current_Bar_Time < news_Time_Stamp_After_1
bool bar_Is_Last_News_Candle_1 = current_Bar_Time == news_Time_Stamp_After_1

// draw the news warning objects if showing the first news warning
if show_News_1
    // draw the news warning area
    news_Box_1 = box.new(left = news_Time_Stamp_Before_1, top = (open * 2) + open, right = news_Time_Stamp_After_1, bottom = (open * -2) + open, xloc = xloc.bar_time, border_color = na, bgcolor = box_Background_Color)
    box.delete(news_Box_1[1])
    // delete the box if box show = false
    if box_Show == false
        box.delete(news_Box_1)
    // draw the news release line
    news_Line_1 = line.new(news_Time_Stamp_Release_1, -1, news_Time_Stamp_Release_1, 1, xloc.bar_time, extend.both, line_Color, line_Style_Selected, line_Width)
    line.delete(news_Line_1[1])

// News 2 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// get the time stamps for the second news warning, release, before and after
news_Time_Stamp_Release_2 = timestamp(year, month, dayofmonth, news_Hour_2 + time_Zone_Offset, news_Minute_2, 00)
news_Time_Stamp_Before_2 = timestamp(year, month, dayofmonth, news_Hour_2 + time_Zone_Offset, news_Minute_2 - minutes_Warning_Before_News, 00)
news_Time_Stamp_After_2 = timestamp(year, month, dayofmonth, news_Hour_2 + time_Zone_Offset, news_Minute_2 + minutes_Warning_After_News, 00)

// create bools to indicate where price is currently relatated to the second news warning, used for calculations
bool bar_Is_In_News_2 = current_Bar_Time >= news_Time_Stamp_Before_2 and current_Bar_Time <= news_Time_Stamp_After_2 and show_News_2
bool bar_Is_Before_News_2 = current_Bar_Time < news_Time_Stamp_Release_2 and current_Bar_Time >= news_Time_Stamp_Before_2
bool bar_Is_News_Release_2 = current_Bar_Time == news_Time_Stamp_Release_2
bool bar_Is_News_Ongoing_2 = current_Bar_Time > news_Time_Stamp_Release_2 and current_Bar_Time < news_Time_Stamp_After_2
bool bar_Is_Last_News_Candle_2 = current_Bar_Time == news_Time_Stamp_After_2

// draw the news warning objects if showing the second news warning
if show_News_2
    // draw the news warning area
    news_Box_1 = box.new(left = news_Time_Stamp_Before_2, top = (open * 2) * open, right = news_Time_Stamp_After_2, bottom = (open * -2) + open, xloc = xloc.bar_time, border_color = na, bgcolor = box_Background_Color)
    box.delete(news_Box_1[1])
    // delete the box if box show = false
    if box_Show == false
        box.delete(news_Box_1)
    // draw the news release line
    news_Line_2 = line.new(news_Time_Stamp_Release_2, -1, news_Time_Stamp_Release_2, 1, xloc.bar_time, extend.both, line_Color, line_Style_Selected, line_Width)
    line.delete(news_Line_2[1])

// News 3 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// get the time stamps for the third news warning, release, before and after
news_Time_Stamp_Release_3 = timestamp(year, month, dayofmonth, news_Hour_3 + time_Zone_Offset, news_Minute_3, 00)
news_Time_Stamp_Before_3 = timestamp(year, month, dayofmonth, news_Hour_3 + time_Zone_Offset, news_Minute_3 - minutes_Warning_Before_News, 00)
news_Time_Stamp_After_3 = timestamp(year, month, dayofmonth, news_Hour_3 + time_Zone_Offset, news_Minute_3 + minutes_Warning_After_News, 00)

// create bools to indicate where price is currently relatated to the third news warning, used for calculations
bool bar_Is_In_News_3 = current_Bar_Time >= news_Time_Stamp_Before_3 and current_Bar_Time <= news_Time_Stamp_After_3 and show_News_3
bool bar_Is_Before_News_3 = current_Bar_Time < news_Time_Stamp_Release_3 and current_Bar_Time >= news_Time_Stamp_Before_3
bool bar_Is_News_Release_3 = current_Bar_Time == news_Time_Stamp_Release_3
bool bar_Is_News_Ongoing_3 = current_Bar_Time > news_Time_Stamp_Release_3 and current_Bar_Time < news_Time_Stamp_After_3
bool bar_Is_Last_News_Candle_3 = current_Bar_Time == news_Time_Stamp_After_3

// draw the news warning objects if showing the third news warning
if show_News_3
    // draw the news warning area
    news_Box_1 = box.new(left = news_Time_Stamp_Before_3, top = (open * 2) * open, right = news_Time_Stamp_After_3, bottom = (open * -2) + open, xloc = xloc.bar_time, border_color = na, bgcolor = box_Background_Color)
    // delete old boxes
    box.delete(news_Box_1[1])
    // delete the box if box show = false
    if box_Show == false
        box.delete(news_Box_1)
    // draw the news release line
    news_Line_3 = line.new(news_Time_Stamp_Release_3, -1, news_Time_Stamp_Release_3, 1, xloc.bar_time, extend.both, line_Color, line_Style_Selected, line_Width)
    // delete old lines
    line.delete(news_Line_3[1])

// All News ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

// create bools to indicate where price is currently relatated to all the news warning, used for calculations
bool bar_Is_In_News = bar_Is_In_News_1 or bar_Is_In_News_2 or bar_Is_In_News_3
bool bar_Is_Before_News = bar_Is_Before_News_1 or bar_Is_Before_News_2 or bar_Is_Before_News_3
bool bar_Is_News_Release = bar_Is_News_Release_1 or bar_Is_News_Release_2 or bar_Is_News_Release_3
bool bar_Is_News_Ongoing = bar_Is_News_Ongoing_1 or bar_Is_News_Ongoing_2 or bar_Is_News_Ongoing_3
bool bar_Is_Last_News_Candle = bar_Is_Last_News_Candle_1 or bar_Is_Last_News_Candle_2 or bar_Is_Last_News_Candle_3

//------------------------------------------------------------------------------------------------------------------------------------------------------------------------------( News Table )

// create a table that will be used to display the news warning before, after and during the news release
news_Table = table.new(position = position.bottom_right, columns = 1, rows = 2, bgcolor = color.red, frame_color = color.white, frame_width = 2)

// if price is currently in a news warning area
if bar_Is_In_News == true
    // indacate that there is currently active news
    table.cell(table_id = news_Table, column = 0, row = 0, text = "Active News")
    table.cell_set_text_size(news_Table, 0, 0, size.large)
    table.cell_set_text_color(news_Table, 0, 0, color.white)
    if bar_Is_News_Release
        // indicate that it is the news release
        table.cell(table_id = news_Table, column = 0, row = 1, text = "Release")
        table.cell_set_text_size(news_Table, 0, 1, size.large)
        table.cell_set_text_color(news_Table, 0, 1, color.white)
    else if bar_Is_News_Ongoing
        // indicate if news is ongoing ( after the news release )
        table.cell(table_id = news_Table, column = 0, row = 1, text = "Ongoing")
        table.cell_set_text_size(news_Table, 0, 1, size.large)
        table.cell_set_text_color(news_Table, 0, 1, color.white)
    else if bar_Is_Last_News_Candle
        // indate if news is on the last candle before the warning ends
        table.cell(table_id = news_Table, column = 0, row = 1, text = "Last Candle")
        table.cell_set_text_size(news_Table, 0, 1, size.large)
        table.cell_set_text_color(news_Table, 0, 1, color.white)
    else if bar_Is_Before_News
        // indicate if the news release is coming soon
        table.cell(table_id = news_Table, column = 0, row = 1, text = "Incoming")
        table.cell_set_text_size(news_Table, 0, 1, size.large)
        table.cell_set_text_color(news_Table, 0, 1, color.white)

else
    // indicate that price is currently not in a news warning
    table.set_bgcolor(news_Table, bgcolor = color.green)
    table.cell(table_id = news_Table, column = 0, row = 0, text = " Clear ")
    table.cell_set_text_size(news_Table, 0, 0, size.normal)
    table.cell_set_text_color(news_Table, 0, 0, color.white)

// delete old tables
table.delete(news_Table[1])