Correlation with P-Value & Confidence Interval (alt)
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © balipour
// extended by tartigradia
// 
// Pearson correlation coefficient measures the linear correlation between two variables. It has a value between +1 and −1, where 1 is total positive linear correlation, 0 is no linear correlation and −1 is total negative linear correlation. It’s often denoted by r for sample correlation and ρ for population correlation.
// 
// Note: Pearson Correlation only measures the linear relationship between two variables, such as y = a*x + b. There are other measurements for nonlinear correlations.
// 
// When option “R” for "Correlation variants" is chosen, the value would be the same as TradingView's built in correlation() function. For "Adjusted R ", the calculation is based on the traditional Pearson. The sample r is a biased estimate of population ρ. The adjusted r gets rid of some of the bias but not all. As the sample size or lookback period increases, adjusted r will be closer to r.
// 
// The confidence interval is computed for population ρ estimation based on sample r. Correlation coefficient itself doesn’t follow a normal distribution. Fisher transformation is applied to transform the data into an approximately normal distribution. We compute the standard error based on the transformed data, then use an inverse fisher transform to transform back the standard error in terms of r.
// 
// Note: the confidence interval band is an approximation of population, it proposes a range of plausible r values (instead of a point). The confidence level represents the frequency (i.e. the proportion) of possible confidence intervals that contain the true value of the unknown population parameter. The proportion of those intervals that contain the true value of the parameter will be equal to the confidence level. For example, if the confidence level is 95% then in hypothetical indefinite data collection, in 95% of the samples the interval estimate will contain the population parameter. The default setting is 1.96* standard error which is 95% confidence interval.
// 
// The most important and distinguishable feature of this indicator is the p-value provided along with the correlation.
// 
// The value of Correlation Coefficient alone doesn’t provide any information regarding its statistical significance. For example, two sets of independent samples have 0 correlation in theory. However, your correlation coefficient on these samples will never actually show 0 correlation (small correlation value but not 0). Therefore without a significance test, one would be fooled by the value of r when there’s no linear relationship at all.
// 
// In statistical hypothesis testing, the p-value or probability value is the probability of obtaining test results at least as extreme as the results actually observed during the test, assuming that the null hypothesis is correct. The smaller the p-value, the stronger the evidence that the null hypothesis should be rejected and that the alternate hypothesis might be more credible. Since one could be deceived by r showing values while correlation is actually 0. The null hypothesis here is the “r is 0”. The alternative hypothesis is “ r is not 0”. The default setting for p critical value is 0.05. It means that when p is lower than 0.05, there’s less than 5% chance that correlation is 0, and we consider that to be "significant correlation". To get the p-value, We use a t distribution with n – 2 degrees of freedom to find the probability. P-value will adjust automatically when the sample size or lookback changes.
// 
// Displays :
// When p is lower than 0.05 and r > 0, correlation coefficient shows red, p-value shows yellow, panel shows “Significant Positive Correlation”.
// When p is lower than 0.05 and r < 0, correlation coefficient shows green, p-value shows yellow, panel shows “Significant Negative Correlation”.
// When p is higher than 0.05, correlation, correlation coefficient shows white, p-value shows grey, panel shows “Insignificant Correlation”.
// 
// r² (r squared) also known as the coefficient of determination, is the square of correlation r. r² measures how well the data fit the linear regression model used in correlation. When two assets show significant correlation, r squared can be used to compare which one fits the data better. r² is displayed on the panel and has a different lookback by default than the correlation coefficient .
// 
// Contributors : Pig (ideas, code, math and design), Balipour (ideas), midtownsk8rguy(applying/employing Pine etiquette), tartigradia (optimizing SMA, add EMA, add multi-timeframe resolution, conversion from pinescript v4 to v5).

//@version=5
indicator('Correlation with P-Value & Confidence Interval [pig, tartigradia]', 'BA🐷 CC (TG)', false, format.price, 3, timeframe="", timeframe_gaps=false)

var invisible = color(na)
bgcolor(#000000c0)

var cPI = 2.0 * math.asin(1.0)  // 3.1415926536 Constant


//===== Functions =====//
custom_sma(src, src_cur, len) =>
    sum = 0.0
    sum := nz(sum[1]) - nz(src[len]) + src  // compute the updated rolling sum
    (nz(sum[1]) - nz(src) + src_cur) / len

cc(x, y, len, ma_type) =>  // Correlation Coefficent function
    lenMinusOne = len - 1
    //meanx = 0.0, meany = 0.0
    //for i=0.0 to lenMinusOne
    //    meanx := meanx + nz(x[i])
    //    meany := meany + nz(y[i])
    //meanx := meanx / len
    //meany := meany / len
    meanx = ma_type == 'ema' ? ta.ema(x, lenMinusOne) : ta.sma(x, lenMinusOne)
    meany = ma_type == 'ema' ? ta.ema(y, lenMinusOne) : ta.sma(y, lenMinusOne)
    sumxy = 0.0
    sumx = 0.0
    sumy = 0.0
    for i = 0 to lenMinusOne by 1
        sumxy += (nz(x[i]) - meanx) * (nz(y[i]) - meany)
        sumx += math.pow(nz(x[i]) - meanx, 2)
        sumy += math.pow(nz(y[i]) - meany, 2)
    sumxy / math.sqrt(sumy * sumx)

adj(r, n) =>  // Unbiased Adjusted R Estimation Approximate Function
    (1 + (1 - math.pow(r, 2)) / (2 * n)) * r

Round(src, digits) =>  // Round Function
    p = math.pow(10, digits)
    math.round(math.abs(src) * p) / p * math.sign(src)

xp(offset) =>  // Offset 
    time + math.round(ta.change(time) * offset)

//label Panel Function
//Note: removed by Tartigradia because it causes side effects which prevents MTF implementation (timeframe argument). Remove timeframe argument in the indicator call and uncomment these lines if you want to use panel again.
//_label(T, color_PnL) =>
//    label PnL_Label = na
//    label.delete(PnL_Label[1])
//    PnL_Label := label.new(time, 0.0, text=T, color=color_PnL, textcolor=color.white, size=size.normal, style=label.style_label_left, xloc=xloc.bar_time, textalign=text.align_left)
//    label.set_x(PnL_Label, label.get_x(PnL_Label) + math.round(ta.change(time) * 3))

//===== Inputs =====//
src = input(close, 'Source')
sec1in = input.symbol('DXY', 'Comparison Symbol', confirm=true)
mode = input.string('Adjusted R', 'Correlation Variants ', options=['R', 'Adjusted R'])
len = input.int(14, 'Correlation Lookback Length', minval=2)
ma_type = input.string('sma', 'Moving Average Type', options=['sma', 'ema'])

//Stats Settings
sc = input(true, 'Show Confidence Interval for Population')
csd = input.float(1.96, 'Confidence Interval SD Multiplier', minval=0.1, step=0.1)  //Default 95% 
sp = input(true, 'Show P-Values')
cp = input.float(0.1, 'P-Value Significant Confidence Level', minval=0.0, step=0.01)  //Default = 1- 0.05 =  95%
//pan = input(true, ' Show Information Panel')
rlen = input.int(50, '  R Squared Length', minval=2)
os = input.int(40, '  Panel Position Offset', minval=0)
lT = input.int(1, '--- Line Thickness ---', options=[1, 2, 3])

sec1 = request.security(sec1in, timeframe.period, close)


//===== Calculations =====//
R = cc(src, sec1, len, ma_type)  // Traditional Pearson
adjr = adj(R, len)  // Adjusted R

float r = na
if mode == 'R'
    r := R
    r
if mode == 'Adjusted R'
    r := adjr
    r

R2 = math.pow(cc(src, sec1, rlen, ma_type), 2)  // R Squared
adjR2 = math.pow(adj(cc(src, sec1, rlen, ma_type), rlen), 2)  // R Sqaured Based on Adjusted R

float r2 = na
if mode == 'R'
    r2 := R2
    r2
if mode == 'Adjusted R'
    r2 := adjR2
    r2

// Fisher Transform
z = 0.5 * math.log((r + 1.0) / (1.0 - r))  // Fisher 
se = 1.0 / math.sqrt(len - 3)  // Standard Error
zl = z - csd * se  // Lower Limit for fisher z
zu = z + csd * se  // Upper Limit for fisher z 95% confidence

// Inverse Fisher Transform to Transform Back to r
rl = (math.exp(2.0 * zl) - 1.0) / (math.exp(2.0 * zl) + 1.0)  // Lower limit for r
ru = (math.exp(2.0 * zu) - 1.0) / (math.exp(2.0 * zu) + 1.0)  // Upper limit for r

//T distribution For P Value
v = len - 2
x = math.abs(r * math.sqrt(v) / math.sqrt(1 - math.pow(r, 2)))

//student t cdf
tcdf(t, df) =>
    Z = t * (1 - 1 / (4 * df)) * math.pow(1 + 1 / (2 * df) * math.pow(t, 2), -0.5)
    a = 0.0
    for n = 0 to 12 by 1
        a := 1 / (n + 0.5) * math.exp(-math.pow(n + 0.5, 2) / 9) * math.sin(math.sqrt(2) / 3 * (n + 0.5) * Z) + a
        a
    p = t > 7 ? 1.0 : t < -7 ? 0.0 : 0.5 + 1 / math.pi * a
    2 * (1 - p)

pro = tcdf(x, v)

//===== Plotting =====//
colorCC = pro < cp and r < 0.0 ? #FF0000ff : pro < cp and r > 0.0 ? #00FF00ff : #FFFFFFff
colorP = pro < cp ? #FFFF00ff : #C0C0C040

plot(sp ? pro : na, color=colorP, title='P Value', style=plot.style_columns)
plotUpper = plot(rl, color=sc ? color.new(#00C0FFff, 100) : invisible, style=plot.style_linebr, title='Confidence Interval Lower')
plotLower = plot(ru, color=sc ? color.new(#00C0FFff, 100) : invisible, style=plot.style_linebr, title='Confidence Interval Higher')
fill(plotUpper, plotLower, color=sc ? color.new(#00C0FFff, 85) : invisible)
plot(r, linewidth=lT, color=colorCC, style=plot.style_linebr, title='🐷 Correlation')

plot(sp ? cp : na, color=color.new(#C0C0C0ff, 30), trackprice=true, show_last=1, title='P value Threshold', style=plot.style_linebr)
plot(sp ? na : 0, color=color.new(#C0C0C0ff, 30), trackprice=true, show_last=1, title='Zero Line')
hline(1.0, color=color.new(#00FFFFff, 30))
hline(-1.0, color=color.new(#FF00FFff, 30))

// Information Panel
//Note: removed by Tartigradia because it causes side effects which prevents MTF implementation (timeframe argument). Remove timeframe argument in the indicator call and uncomment these lines if you want to use panel again.
//sig() =>
//    return_1 = pro < cp and r > 0 ? 'Significant Positive Correlation' : pro < cp and r < 0 ? 'Significant Negative Correlation' : 'Insignificant Correlation'
//    return_1

//if pan
//    txt = 'R : ' + str.tostring(Round(r, 3)) + '\n\n R Squared : ' + str.tostring(Round(r2, 4)) + '\n\n P Value : ' + str.tostring(Round(pro, 4)) + '\n\n' + sig()
//    _label(txt, #000000c0)

