Quantitative Risk Navigator [kikfraben]
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © kikfraben
// Updated last on Nov 14 2023
// @version = 5

indicator("Quantitative Risk Navigator [kikfraben]", "QRN", false, precision = 2)

// Get Base Symbol
symbol = input.symbol("BTCUSD", "Base Symbol", group = "General Inputs")
src = input.source(close, "Source", group = "General Inputs")
base = request.security(symbol, "D", src)

// Get Risk Free Rate
risk_free_rate = input.float(0, "Risk Free Rate", group = "General Inputs")

// Get Lengths
length_1 = input(30, "Length 1", group = "Length Inputs", tooltip = "Defines the length for Alpha 1, Beta 1, Correlation 1, Sharpe 1, Sortino 1, Omega 1")
length_2 = input(60, "Length 2", group = "Length Inputs", tooltip = "Defines the length for Alpha 2, Beta 2, Correlation 2, Sharpe 2, Sortino 2, Omega 2")
length_3 = input(90, "Length 3", group = "Length Inputs", tooltip = "Defines the length for Alpha 3, Beta 3, Correlation 3, Sharpe 3, Sortino 3, Omega 3")

// Get Beta
beta(src, base, length) =>
    returns = src / src[1] - 1
    base_returns = base / base[1] - 1
    returns_array = array.new_float(0)
    base_returns_array = array.new_float(0)

    for x = 0 to length
        array.push(returns_array, returns[x])
        array.push(base_returns_array, base_returns[x])

    array.covariance(returns_array, base_returns_array) / array.variance(base_returns_array)

beta_1 = beta(src, base, length_1)
beta_2 = beta(src, base, length_2)
beta_3 = beta(src, base, length_3)
beta_avg = math.avg(beta_1, beta_2, beta_3)

// Get Alpha
alpha(src, base, length) =>
    returns = src / src[1] - 1
    base_returns = base / base[1] - 1
    returns_array = array.new_float(0)
    base_returns_array = array.new_float(0)
    
    for x = 0 to length
        array.push(returns_array, returns[x])
        array.push(base_returns_array, base_returns[x])

    array.sum(returns_array) - (risk_free_rate + beta(src, base, length) * (array.sum(base_returns_array) - risk_free_rate))

alpha_1 = alpha(src, base, length_1)
alpha_2 = alpha(src, base, length_2)
alpha_3 = alpha(src, base, length_3)
alpha_avg = math.avg(alpha_1, alpha_2, alpha_3)

// Get Correlation
correlation_1 = ta.correlation(src, base, length_1)
correlation_2 = ta.correlation(src, base, length_2)
correlation_3 = ta.correlation(src, base, length_3)
correlation_avg = math.avg(correlation_1, correlation_2, correlation_3)

// Get Omega Ratio
omega(src, length) =>
    returns = src / src[1] - 1
    negative_returns_array = array.new_float(0)
    positive_returns_array = array.new_float(0)
    for x = 0 to length
        if returns[x] <= 0.0
            array.push(negative_returns_array, returns[x])
        else
            array.push(positive_returns_array, returns[x])

    postive_returns = array.sum(positive_returns_array)
    negative_returns = array.sum(negative_returns_array) * (-1)
    
    math.round(postive_returns / negative_returns, 2)

omega_1 = omega(src, length_1)
omega_2 = omega(src, length_2)
omega_3 = omega(src, length_3)
omega_avg = math.avg(omega_1, omega_2, omega_3)

// Get Sharpe Ratio
sharpe(src, length) =>
    returns = src / src[1] - 1
    returns_array = array.new_float(0)
    for x = 0 to length
        array.push(returns_array, returns[x])
    
    standard_deviation = array.stdev(returns_array)
    mean = array.avg(returns_array)
    
    math.round(mean / standard_deviation * math.sqrt(length), 2)

sharpe_1 = sharpe(src, length_1)
sharpe_2 = sharpe(src, length_2)
sharpe_3 = sharpe(src, length_3)
sharpe_avg = math.avg(sharpe_1, sharpe_2, sharpe_3)

// Get Sortino Ratio
sortino(src, length) =>
    returns = src / src[1] - 1
    returns_array = array.new_float(0)
    negative_returns_array = array.new_float(0)
    for x = 0 to length
        array.push(returns_array, returns[x])
        if returns[x] <= 0.0
            array.push(negative_returns_array, returns[x])


    standard_deviation_negative_returns = array.stdev(negative_returns_array)
    mean = array.avg(returns_array)
    
    math.round(mean / standard_deviation_negative_returns * math.sqrt(length), 2)

sortino_1 = sortino(src, length_1)
sortino_2 = sortino(src, length_2)
sortino_3 = sortino(src, length_3)
sortino_avg = math.avg(sortino_1, sortino_2, sortino_3)

// Get Plot Options
plot_alpha_avg = input.bool(false, "Alpha AVG", inline = "1", group = "Plot Options")
plot_alpha_1 = input.bool(false, "Alpha 1", inline = "1", group = "Plot Options")
plot_alpha_2 = input.bool(false, "Alpha 2", inline = "1", group = "Plot Options")
plot_alpha_3 = input.bool(false, "Alpha 3", inline = "1", group = "Plot Options")

plot_beta_avg = input.bool(false, "Beta AVG", inline = "2", group = "Plot Options")
plot_beta_1 = input.bool(false, "Beta 1", inline = "2", group = "Plot Options")
plot_beta_2 = input.bool(false, "Beta 2", inline = "2", group = "Plot Options")
plot_beta_3 = input.bool(false, "Beta 3", inline = "2", group = "Plot Options")

plot_correlation_avg = input.bool(false, "Correl AVG", inline = "3", group = "Plot Options")
plot_correlation_1 = input.bool(false, "Correl 1", inline = "3", group = "Plot Options")
plot_correlation_2 = input.bool(false, "Correl 2", inline = "3", group = "Plot Options")
plot_correlation_3 = input.bool(false, "Correl 3", inline = "3", group = "Plot Options")

plot_sharpe_avg = input.bool(false, "Sharpe AVG", inline = "4", group = "Plot Options")
plot_sharpe_1 = input.bool(false, "Sharpe 1", inline = "4", group = "Plot Options")
plot_sharpe_2 = input.bool(false, "Sharpe 2", inline = "4", group = "Plot Options")
plot_sharpe_3 = input.bool(false, "Sharpe 3", inline = "4", group = "Plot Options")

plot_sortino_avg = input.bool(false, "Sortino AVG", inline = "5", group = "Plot Options")
plot_sortino_1 = input.bool(false, "Sortino 1", inline = "5", group = "Plot Options")
plot_sortino_2 = input.bool(false, "Sortino 2", inline = "5", group = "Plot Options")
plot_sortino_3 = input.bool(false, "Sortino 3", inline = "5", group = "Plot Options")

plot_omega_avg = input.bool(false, "Omega AVG", inline = "6", group = "Plot Options")
plot_omega_1 = input.bool(false, "Omega 1", inline = "6", group = "Plot Options")
plot_omega_2 = input.bool(false, "Omega 2", inline = "6", group = "Plot Options")
plot_omega_3 = input.bool(false, "Omega 3", inline = "6", group = "Plot Options")

plot_table = input.bool(true, "Metrics Table", inline = "7", group = "Plot Options")

table_y_position = input.string(defval = "middle", title = "Table Position", options = ["top", "middle",
 "bottom"], inline = "8", group = "Plot Options")
table_x_position = input.string(defval = "center", title = "", options = ["left", "center", "right"],
 inline = "8", group = "Plot Options")

// Get Colors
up_color = input.color(#3fa8c9, title = "Bullish Color", group = "Color Inputs", inline = "1")
down_color = input.color(#c93f3f, title = "Bearish Color", group = "Color Inputs", inline = "1")
background_color = color.new(color.white, 95)
txt_color = color.white
txt_color1 = color.new(color.white, 25)
table_color = color.new(color.white, 70)

alpha_color = input.color(color.red, "Alpha Color", group = "Color Inputs", inline = "2")
beta_color = input.color(color.orange, "Beta Color", group = "Color Inputs", inline = "2")
correlation_color = input.color(color.yellow, "Correl Color", group = "Color Inputs", inline = "2")
sharpe_color = input.color(color.purple, "Sharpe Color", group = "Color Inputs", inline = "3")
sortino_color = input.color(color.blue, "Sortino Color", group = "Color Inputs", inline = "3")
omega_color = input.color(color.green, "Omega Color", group = "Color Inputs", inline = "3")

// Get Plots
plot(plot_alpha_1 ? alpha_1 : na, color = color.new(alpha_color, 60))
plot(plot_alpha_2 ? alpha_2 : na, color = color.new(alpha_color, 40))
plot(plot_alpha_3 ? alpha_3 : na, color = color.new(alpha_color, 20))
plot(plot_alpha_avg ? alpha_avg : na, color = alpha_color)

plot(plot_beta_1 ? beta_1 : na, color = color.new(beta_color, 60))
plot(plot_beta_2 ? beta_2 : na, color = color.new(beta_color, 40))
plot(plot_beta_3 ? beta_3 : na, color = color.new(beta_color, 20))
plot(plot_beta_avg ? beta_avg : na, color = beta_color)

plot(plot_correlation_1 ? correlation_1 : na, color = color.new(correlation_color, 60))
plot(plot_correlation_2 ? correlation_2 : na, color = color.new(correlation_color, 40))
plot(plot_correlation_3 ? correlation_3 : na, color = color.new(correlation_color, 20))
plot(plot_correlation_avg ? correlation_avg : na, color = correlation_color)

plot(plot_sharpe_1 ? sharpe_1 : na, color = color.new(sharpe_color, 60))
plot(plot_sharpe_2 ? sharpe_2 : na, color = color.new(sharpe_color, 40))
plot(plot_sharpe_3 ? sharpe_3 : na, color = color.new(sharpe_color, 20))
plot(plot_sharpe_avg ? sharpe_avg : na, color = sharpe_color)

plot(plot_sortino_1 ? sortino_1 : na, color = color.new(sortino_color, 60))
plot(plot_sortino_2 ? sortino_2 : na, color = color.new(sortino_color, 40))
plot(plot_sortino_3 ? sortino_3 : na, color = color.new(sortino_color, 20))
plot(plot_sortino_avg ? sortino_avg : na, color = sortino_color)

plot(plot_omega_1 ? omega_1 : na, color = color.new(omega_color, 60))
plot(plot_omega_2 ? omega_2 : na, color = color.new(omega_color, 40))
plot(plot_omega_3 ? omega_3 : na, color = color.new(omega_color, 20))
plot(plot_omega_avg ? omega_avg : na, color = omega_color)

// Get Metrics Table
if plot_table == true
    metrics = table.new(table_y_position + "_" + table_x_position, 10, 10, frame_color = table_color, border_color = table_color, frame_width = 2, border_width = 2)
    table.merge_cells(metrics, 0, 0, 4, 0)

    table.cell(metrics, 0, 0, "Quantitative Risk Navigator", text_color = color.yellow, bgcolor = background_color)
    table.cell(metrics, 0, 1, "Metric", text_color = txt_color, bgcolor = background_color)
    table.cell(metrics, 1, 1, "Length 1", text_color = txt_color, bgcolor = background_color)
    table.cell(metrics, 2, 1, "Length 2", text_color = txt_color, bgcolor = background_color)
    table.cell(metrics, 3, 1, "Length 3", text_color = txt_color, bgcolor = background_color)
    table.cell(metrics, 4, 1, "Average", text_color = txt_color, bgcolor = background_color)
    table.cell(metrics, 0, 2, "Alpha", text_color = txt_color1, bgcolor = background_color)
    table.cell(metrics, 0, 3, "Beta", text_color = txt_color1, bgcolor = background_color)
    table.cell(metrics, 0, 4, "Correlation", text_color = txt_color1, bgcolor = background_color)
    table.cell(metrics, 0, 5, "Sharpe", text_color = txt_color1, bgcolor = background_color)
    table.cell(metrics, 0, 6, "Sortino", text_color = txt_color1, bgcolor = background_color)
    table.cell(metrics, 0, 7, "Omega", text_color = txt_color1, bgcolor = background_color)

    table.cell(metrics, 1, 2, str.tostring(alpha_1, "#.##"), text_color = txt_color, bgcolor = alpha_1 >= 0 ? (alpha_1 <= 0.25 ? color.new(up_color, 60) : (alpha_1 <= 0.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (alpha_1 >= -0.25 ? color.new(down_color, 60) : (alpha_1 >= -0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 2, str.tostring(alpha_2, "#.##"), text_color = txt_color, bgcolor = alpha_2 >= 0 ? (alpha_2 <= 0.25 ? color.new(up_color, 60) : (alpha_2 <= 0.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (alpha_2 >= -0.25 ? color.new(down_color, 60) : (alpha_2 >= -0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 2, str.tostring(alpha_3, "#.##"), text_color = txt_color, bgcolor = alpha_3 >= 0 ? (alpha_3 <= 0.25 ? color.new(up_color, 60) : (alpha_3 <= 0.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (alpha_3 >= -0.25 ? color.new(down_color, 60) : (alpha_3 >= -0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 2, str.tostring(alpha_avg, "#.##"), text_color = txt_color, bgcolor = alpha_avg >= 0 ? (alpha_avg <= 0.25 ? color.new(up_color, 60) : (alpha_avg <= 0.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (alpha_avg >= -0.25 ? color.new(down_color, 60) : (alpha_avg >= -0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))

    table.cell(metrics, 1, 3, str.tostring(beta_1, "#.##"), text_color = txt_color, bgcolor = beta_1 >= 1 ? (beta_1 <= 1.25 ? color.new(up_color, 60) : (beta_1 <= 1.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (beta_1 >= 0.75 ? color.new(down_color, 60) : (beta_1 >= 0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 3, str.tostring(beta_2, "#.##"), text_color = txt_color, bgcolor = beta_2 >= 1 ? (beta_2 <= 1.25 ? color.new(up_color, 60) : (beta_2 <= 1.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (beta_2 >= 0.75 ? color.new(down_color, 60) : (beta_2 >= 0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 3, str.tostring(beta_3, "#.##"), text_color = txt_color, bgcolor = beta_3 >= 1 ? (beta_3 <= 1.25 ? color.new(up_color, 60) : (beta_3 <= 1.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (beta_3 >= 0.75 ? color.new(down_color, 60) : (beta_3 >= 0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 3, str.tostring(beta_avg, "#.##"), text_color = txt_color, bgcolor = beta_avg >= 1 ? (beta_avg <= 1.25 ? color.new(up_color, 60) : (beta_avg <= 1.50 ? color.new(up_color, 40) : color.new(up_color, 10))) : (beta_avg >= 0.75 ? color.new(down_color, 60) : (beta_avg >= 0.50 ? color.new(down_color, 40) : color.new(down_color, 10))))

    table.cell(metrics, 1, 4, str.tostring(correlation_1, "#.##"), text_color = txt_color, bgcolor = correlation_1 >= 0 ? (correlation_1 <= 0.33 ? color.new(up_color, 60) : (correlation_2 <= 0.66 ? color.new(up_color, 40) : color.new(up_color, 10))) : (correlation_1 >= -0.33 ? color.new(down_color, 60) : (correlation_1 >= -0.66 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 4, str.tostring(correlation_2, "#.##"), text_color = txt_color, bgcolor = correlation_2 >= 0 ? (correlation_2 <= 0.33 ? color.new(up_color, 60) : (correlation_2 <= 0.66 ? color.new(up_color, 40) : color.new(up_color, 10))) : (correlation_2 >= -0.33 ? color.new(down_color, 60) : (correlation_2 >= -0.66 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 4, str.tostring(correlation_3, "#.##"), text_color = txt_color, bgcolor = correlation_3 >= 0 ? (correlation_3 <= 0.33 ? color.new(up_color, 60) : (correlation_3 <= 0.66 ? color.new(up_color, 40) : color.new(up_color, 10))) : (correlation_3 >= -0.33 ? color.new(down_color, 60) : (correlation_3 >= -0.66 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 4, str.tostring(correlation_avg, "#.##"), text_color = txt_color, bgcolor = correlation_avg >= 0 ? (correlation_avg <= 0.33 ? color.new(up_color, 60) : (correlation_avg <= 0.66 ? color.new(up_color, 40) : color.new(up_color, 10))) : (correlation_avg >= -0.33 ? color.new(down_color, 60) : (correlation_avg >= -0.66 ? color.new(down_color, 40) : color.new(down_color, 10))))

    table.cell(metrics, 1, 5, str.tostring(sharpe_1, "#.##"), text_color = txt_color, bgcolor = sharpe_1 >= 1 ? (sharpe_1 <= 1.50 ? color.new(up_color, 60) : (sharpe_1 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sharpe_1 >= 0.50 ? color.new(down_color, 60) : (sharpe_1 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 5, str.tostring(sharpe_2, "#.##"), text_color = txt_color, bgcolor = sharpe_2 >= 1 ? (sharpe_2 <= 1.50 ? color.new(up_color, 60) : (sharpe_2 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sharpe_2 >= 0.50 ? color.new(down_color, 60) : (sharpe_2 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 5, str.tostring(sharpe_3, "#.##"), text_color = txt_color, bgcolor = sharpe_3 >= 1 ? (sharpe_3 <= 1.50 ? color.new(up_color, 60) : (sharpe_3 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sharpe_3 >= 0.50 ? color.new(down_color, 60) : (sharpe_3 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 5, str.tostring(sharpe_avg, "#.##"), text_color = txt_color, bgcolor = sharpe_avg >= 1 ? (sharpe_avg <= 1.50 ? color.new(up_color, 60) : (sharpe_avg <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sharpe_avg >= 0.50 ? color.new(down_color, 60) : (sharpe_avg >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))

    table.cell(metrics, 1, 6, str.tostring(sortino_1, "#.##"), text_color = txt_color, bgcolor = sortino_1 >= 1 ? (sortino_1 <= 1.50 ? color.new(up_color, 60) : (sortino_1 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sortino_1 >= 0.50 ? color.new(down_color, 60) : (sortino_1 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 6, str.tostring(sortino_2, "#.##"), text_color = txt_color, bgcolor = sortino_2 >= 1 ? (sortino_2 <= 1.50 ? color.new(up_color, 60) : (sortino_2 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sortino_2 >= 0.50 ? color.new(down_color, 60) : (sortino_2 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 6, str.tostring(sortino_3, "#.##"), text_color = txt_color, bgcolor = sortino_3 >= 1 ? (sortino_3 <= 1.50 ? color.new(up_color, 60) : (sortino_3 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sortino_3 >= 0.50 ? color.new(down_color, 60) : (sortino_3 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 6, str.tostring(sortino_avg, "#.##"), text_color = txt_color, bgcolor = sortino_avg >= 1 ? (sortino_avg <= 1.50 ? color.new(up_color, 60) : (sortino_avg <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (sortino_avg >= 0.50 ? color.new(down_color, 60) : (sortino_avg >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))

    table.cell(metrics, 1, 7, str.tostring(omega_1, "#.##"), text_color = txt_color, bgcolor = omega_1 >= 1 ? (omega_1 <= 1.50 ? color.new(up_color, 60) : (omega_1 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (omega_1 >= 0.50 ? color.new(down_color, 60) : (omega_1 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 2, 7, str.tostring(omega_2, "#.##"), text_color = txt_color, bgcolor = omega_2 >= 1 ? (omega_2 <= 1.50 ? color.new(up_color, 60) : (omega_2 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (omega_2 >= 0.50 ? color.new(down_color, 60) : (omega_2 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 3, 7, str.tostring(omega_3, "#.##"), text_color = txt_color, bgcolor = omega_3 >= 1 ? (omega_3 <= 1.50 ? color.new(up_color, 60) : (omega_3 <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (omega_3 >= 0.50 ? color.new(down_color, 60) : (omega_3 >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))
    table.cell(metrics, 4, 7, str.tostring(omega_avg, "#.##"), text_color = txt_color, bgcolor = omega_avg >= 1 ? (omega_avg <= 1.50 ? color.new(up_color, 60) : (omega_avg <= 2 ? color.new(up_color, 40) : color.new(up_color, 10))) : (omega_avg >= 0.50 ? color.new(down_color, 60) : (omega_avg >= 0 ? color.new(down_color, 40) : color.new(down_color, 10))))



