COSTAR [SS]
//// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
//  ______   ______        _______.___________.    ___      .______      
// /      | /  __  \      /       |           |   /   \     |   _  \     
//|  ,----'|  |  |  |    |   (----`---|  |----`  /  ^  \    |  |_)  |    
//|  |     |  |  |  |     \   \       |  |      /  /_\  \   |      /     
//|  `----.|  `--'  | .----)   |      |  |     /  _____  \  |  |\  \----.
// \______| \______/  |_______/       |__|    /__/     \__\ | _| `._____|
                                                                                                                                                                          
                                                                                                    
                                                                                                    
//                                                                          7^                        
//                                                                         7BG~                       
//                                                                        ?P.7B~                      
//                                                                      ^YJ.^^?B!                     
//                                                                   .~JY~:^~^^!PY^                   
//                                                          ^77!!77?JJ?~:^^^^^^^^7YYJ?7!!~~~~.        
//                                                          :?PPY7!~~^^^~~!!~!7!!!!~~!!7JPB5!.        
//                                                             .!JY7^^!77????77!!~^^^^?5Y!:           
//                                                     .:^~!77:   :J5~^~777!!!!!~^^^!PY~.             
//                                            ?^   :^!!!~^:   :~^   ~B:^!!!!!!~~^^^!#7.               
//                                       ....!7?!77!^.    :~77!^.   JY.^^~~~!!~^^^^~PY.               
//                                       :~?Y?^!?7!:  .^!7~:       ?P :^~7Y5J?JY?~^^~B?.              
//                                      :^~^:7?7   .~!!^.   :~!.  !P.:7Y5J~.   .!YJ~^7B~.             
//                               .^  :~~^.   .J .~!~:   .^!7~^.  ~G^?5Y!.         ~YJ~Y5:             
//                            ...7J!!~.       ^~~:   .^!!^.     !&5Y7:              ~Y5&!.            
//                            ^7Y7!J7^     .~!^!.  :~!^.       !G?^.                  ^J~.            
//                           :^:.JJ.     :~~: ~??^!!:         .~                                      
//                     .. .^^:   ::    :~^^~7?!^!JJ!:                                                 
//                   :^??!~.         :~^     ~J7?:                                                    
//                   ^7Y?~.      :!:^:     :~:^5.                                                     
//                 .:: ~:     :^~77J!:   .^^   .                                                      
//            .  .::.         :~J777^:  ^^.                                                           
//          :~7?~:            :^ ?7   .^.                                                             
//          .^?~.           .^:  .   ::                                                               
//          . .            ::      .:.                                                                
//                     .~!^:      ..                                                                  
//                    ^!??7:     ..                                                                   
//                     .7^                                                                            
//                    :.                                                                              
//                  ..                                                                                
//                 ..                                                                                 
// COSTAR by © Steversteves
//
//  ___             _             _ 
// | _ \___ __ _ __| |  _ __  ___| |
// |   / -_) _` / _` | | '  \/ -_)_|
// |_|_\___\__,_\__,_| |_|_|_\___(_)
//
// COSTAR provides another, more neutral way to determine whether an equity is overbought or oversold. 
// Instead of relying on the traditional oscillator based ways, such as using RSI, Stocahstics and MFI, which can be somewhat biased and narrow sided, COSTAR attempts to take a neutral, unbaised approached
// to determing overbought and oversold conditions. It does this through using a co-integrated partner, or "pair" that is closely linked to the underlying equity and succeeds on both having a high correlation and
// a high t-statistic on the ADF test. It then references this underlying, co-integrated partner as the "benchmark" for the co-integration relationship. 
// How this succeeds as being "unbiased" and "neutral" is because it is responsive to underlying drivers. If there is a market catalyst or just general bullish or bearish momentum in the market, the indicator
// will be referencing the integrated relationship between the two pairs and referencing that as a baseline. If there is a sustained rally on the integrated partner of the underlying ticker that is holding,
// but the other ticker is lagging, it will indicate that the other ticker is likely to be under-valued and thus "oversold" because it is underperforming its benchmark partner. 
// This is in contrast to traditional approaches to determining overbought and oversold conditions, which rely completely on a single ticker, with no external reference to other tickers and no control over
// whether the move could potentially be a fundamental move based on an industry or sector, or whether it is a fluke or a squeeze.
// The control for this giving "false" signals comes from its extent of modelling and assessment of the degree of integration of the relationship. The parameters are set by default to asess over a 1 year period, both  
// the correlation and the integration. Anything that passes this degree of integration is likely to have a solid, co-integrated state and not likely to be a "fluke". Thus, the reliability of the assessment 
// is augmented by the degree of statistical significance found within the relationship. The indicator is not going to prompt you to rely on a relationship that is statistically weak, and will warn you of such.
// The indicator will show you all the information you require regarding the relationship and whether it is reliable or not, so you do not need to worry! 

//@version=5
indicator("Cointegration, Overbought, and Oversoldness ST [SS]", shorttitle = "COSTAR [SS]", max_labels_count = 500)
import Steversteves/SPTS_StatsPakLib/3 as spts 


// _   _                 _____                  _       
//| | | |               |_   _|                | |      
//| | | |___  ___ _ __    | | _ __  _ __  _   _| |_ ___ 
//| | | / __|/ _ \ '__|   | || '_ \| '_ \| | | | __/ __|
//| |_| \__ \  __/ |     _| || | | | |_) | |_| | |_\__ \
// \___/|___/\___|_|     \___/_| |_| .__/ \__,_|\__|___/
//                                 | |                  
//                                 |_|                 

cip = input.symbol("AMEX:SPY", "Benchmark Ticker"),         plt_type =      input.string("Z-Score Price Ratio", "Plot Type", ["Z-Score Price Ratio", "Raw Price Ratio", "Co-Integration Price"]) 
reg_len = input.int(252, "Regression Length"),              info_table =    input.bool(true, "Show Statistics Table") 
cor_len = input.int(252, "Length of Correlation"),          show_entry_exit =   input.bool(false, "Show Entries and Exits")
z_len = input.int(75, "Z-Score Length") 


//  ___             _   _              
// | __|  _ _ _  __| |_(_)___ _ _  ___ 
// | _| || | ' \/ _|  _| / _ \ ' \(_-< 
// |_| \_,_|_||_\__|\__|_\___/_||_/__/ 

// This section provides the fundamental functions for the indicator. The remaning functions are pulled from the SPTS library. 

f_arr() =>                          // Create a new float array 
    array.new_float() 

cor(variable1, variable2) =>        // Perform a correlation assessment on two variables referencing the cor_len input 
    ta.correlation(variable1, variable2, cor_len) 

z_info(variable) =>                 // Creates all parameters for Z-score assessment, the z-score itself and the SD and Avg value. 
    float z = 0.0 
    float sd = 0.0 
    float avg = 0.0 
    avg := ta.sma(variable, z_len) 
    sd := ta.stdev(variable, z_len) 
    z := (variable - avg) / sd 
    [z, avg, sd]

sc(ticker, variable) =>             // Pulls a security request for the desired ticker and expression. 
    request.security(ticker, "", variable, gaps = barmerge.gaps_on, lookahead = barmerge.lookahead_on)

f_cointegration(dependent_ticker, independent_ticker, len) =>
    float result = 0.0 
    dep_array = array.new<float>() 
    indep_array = array.new<float>() 
    for i = 0 to len 
        array.push(dep_array, dependent_ticker[i])
        array.push(indep_array, independent_ticker[i])
    // Average 
    dep_avg = array.avg(dep_array) 
    indep_avg = array.avg(indep_array) 
    // Covariance
    integration_covariance = array.covariance(dep_array, indep_array) 
    variance = array.variance(indep_array) 
    slope = (integration_covariance / variance) 
    intercept = dep_avg - (indep_avg * slope) 
    result := (independent_ticker * slope) + intercept 


//  ___     _                     _   _          
// |_ _|_ _| |_ ___ __ _ _ _ __ _| |_(_)___ _ _  
//  | || ' \  _/ -_) _` | '_/ _` |  _| / _ \ ' \ 
// |___|_||_\__\___\__, |_| \__,_|\__|_\___/_||_|
//                 |___/                        
// This section conducts the integration of the two variables. It does this through first testing for statistical significance via the DF test. If the DF tests returns "True", it will create a linear regression model,
// that will serve as the initial basis for determining the fair market value or expected value of the co-integrated pair as a reference point. 
ip_coef =   f_arr() 
ip_int =    f_arr()

i_c =   sc(cip, close) 
dp_c =  sc(syminfo.tickerid, close) 

[reg_r, pear_cor, r2, se, co_ef, i_r] = spts.f_linear_regression(i_c, dp_c, reg_len, i_c)

// Test for Significance of integration 
dif_i_d =   reg_r - dp_c 
delta_x =   dif_i_d - dif_i_d[1]
lagged_x =  dif_i_d[1]

[t_stat, t_stat_s] = spts.f_dickey_fuller_test(delta_x, lagged_x, reg_len) 


 
for i = 0 to 50
    array.push(ip_coef, co_ef[i])
    array.push(ip_int, i_r[i])

//   ___        ___     _                     _   _            ___             _ _   
//  / __|___ __|_ _|_ _| |_ ___ __ _ _ _ __ _| |_(_)___ _ _   | _ \___ ____  _| | |_ 
// | (__/ _ \___| || ' \  _/ -_) _` | '_/ _` |  _| / _ \ ' \  |   / -_|_-< || | |  _|
//  \___\___/  |___|_||_\__\___\__, |_| \__,_|\__|_\___/_||_| |_|_\___/__/\_,_|_|\__|
//                             |___/                                                

cor_r = cor(i_c, dp_c)
coint_r = (i_c * co_ef) + i_r  

coint_coef_avg = array.avg(ip_coef) 
coint_int_avg = array.avg(ip_int) 

//coint_r_plot = (i_c * coint_coef_avg) + coint_int_avg 

coint_r_plot = f_cointegration(dp_c, i_c, reg_len) 
// |_  /__/ __| __ ___ _ _ ___ 
//  / /___\__ \/ _/ _ \ '_/ -_)
// /___|  |___/\__\___/_| \___|

pr = i_c / dp_c 

[z, avg, sd] = z_info(pr) 

max_z = ta.highest(z, z_len) 
min_z = ta.lowest(z, z_len) 

bool overbought = z >= max_z and dp_c > coint_r 
bool oversold = z <= min_z and dp_c < coint_r 

color green = color.lime,                           color black = color.rgb(0, 0, 0) 
color red = color.red,                            color redfill = color.new(color.red, 85) 
color greenfill = color.new(color.lime, 85),      color white = color.white 
color transp = color.new(color.white, 100),       color fadedteal = color.new(color.aqua, 85)
color whitefill = color.new(color.white, 85),     color purplefill = color.new(color.purple, 55) 

//  ___ _     _      
// | _ \ |___| |_ ___
// |  _/ / _ \  _(_-<
// |_| |_\___/\__/__/
                   
zsma = ta.sma(z, 14) 

color assess = overbought ? red : oversold ? green : zsma > 2 ? fadedteal : zsma < -2 ? fadedteal : color.aqua
color coint_col = coint_r_plot > dp_c ? greenfill : coint_r_plot > dp_c and oversold ? purplefill : coint_r_plot < dp_c ? redfill : coint_r_plot < dp_c and overbought ? purplefill : transp 


a = plot(plt_type == "Z-Score Price Ratio" ? zsma : na, "Z-Score of Price Ratio", color = assess, style = plot.style_histogram, linewidth=3)
plot(plt_type == "Raw Price Ratio" ? pr : na, "Price Ratio (Raw)", color = color.aqua, linewidth = 3) 
d = plot(plt_type == "Co-Integration Price" ? ta.sma(coint_r_plot, 14) : na, "Co-Integration Price", color = color.aqua, linewidth=2) 
e = plot(plt_type == "Co-Integration Price" ? ta.sma(dp_c, 14) : na, "Dependent Closing Price", color = color.yellow, linewidth = 2)
fill(d, e, color = plt_type == "Co-Integration Price" ? coint_col : na) 
var int overbought_c = 0 
var int oversold_c = 0 
if overbought and plt_type == "Z-Score Price Ratio" 
    overbought_c += 1 
    if overbought_c >= 10
        label.new(bar_index, y = zsma + 2, text = "Overbought, \n Expected Price = \n " + str.tostring(math.round(coint_r,2)), color = redfill, textcolor = white)
        overbought_c := 0 
if oversold and plt_type == "Z-Score Price Ratio" 
    oversold_c += 1 
    if oversold_c >= 10 
        label.new(bar_index, y = zsma - 2, text = "Oversold, \n Expected Price = \n " + str.tostring(math.round(coint_r,2)), color = greenfill, textcolor = white, style=label.style_label_up)
        oversold_c := 0 

//  ___       __       _____     _    _     
// |_ _|_ _  / _|___  |_   _|_ _| |__| |___ 
//  | || ' \|  _/ _ \   | |/ _` | '_ \ / -_)
// |___|_||_|_| \___/   |_|\__,_|_.__/_\___|

if info_table 
    var table stats_table = table.new(position.middle_right, 5, 6, bgcolor = black, frame_color = white, frame_width = 3)
    table.cell(stats_table, 1, 1, text = "Statistics Table", bgcolor = black, text_color = white)
    table.cell(stats_table, 1, 2, text = "Dependent Ticker: " + str.tostring(syminfo.ticker) + "\n Independent Ticker: " + str.tostring(cip),  bgcolor = black, text_color = white)
    table.cell(stats_table, 1, 3, text = "T-Statistic: " + str.tostring(math.round(t_stat, 2)), bgcolor = black, text_color = white)
    table.cell(stats_table, 1, 4, text = "Stationary Data? " + str.tostring(t_stat_s) , bgcolor = black, text_color = white)
    table.cell(stats_table, 1, 5, text = "Correlation: " + str.tostring(math.round(cor_r,2)), bgcolor = black, text_color = white)


//  ___     _       _                       _   ___     _ _      
// | __|_ _| |_ _ _(_)___ ___  __ _ _ _  __| | | __|_ _(_) |_ ___
// | _|| ' \  _| '_| / -_|_-< / _` | ' \/ _` | | _|\ \ / |  _(_-<
// |___|_||_\__|_| |_\___/__/ \__,_|_||_\__,_| |___/_\_\_|\__/__/
                                                              

// Entries and exits 
var int entry_wait = 0 
var int long_entry_wait = 0 
var label long_entry_lbl = na 
var label exit_long_lbl = na 
var label exit_short_lbl = na 

if overbought and show_entry_exit 
    entry_wait += 1
    if entry_wait >= 10 
        label.new(bar_index, y = close, text = "Short Entry", color = redfill, style = label.style_circle)
        entry_wait := 0 
if ta.crossunder(close, coint_r_plot) and show_entry_exit 
    label.delete(exit_short_lbl) 
    exit_short_lbl := label.new(bar_index, y = close, text = "Exit Short", color = whitefill, style = label.style_circle)
if oversold and show_entry_exit
    long_entry_wait += 1
    if long_entry_wait >= 10 
        long_entry_lbl := label.new(bar_index, y = close, text = "Long Entry", color = greenfill, style = label.style_circle)
        long_entry_wait := 0 
if ta.crossover(close, coint_r_plot) and show_entry_exit 
    label.delete(exit_long_lbl) 
    exit_long_lbl := label.new(bar_index, y = close, text = "Exit Long", color = whitefill, style = label.style_circle)
 