Monthly beta (5Y monthly) with multi-timeframe support
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © haribotagada

//@version=5
indicator("Monthly beta", overlay= false)

// Inputs
calculationPeriod = input.string('5Y', 'Calculation period', options = ['1Y', '2Y', '3Y', '4Y', '5Y'], tooltip = 'Need at least 1Y of data to calculate.\nOver longer periods we incorprate new monthly data points available at the time until we reach desired window length.')
indexSymbol = input.symbol('SP:SPX', 'Base index')
numMonthlyBars = switch calculationPeriod
    '1Y' => 12
    '2Y' => 2 * 12
    '3Y' => 3 * 12
    '4Y' => 4 * 12
    '5Y' => 5 * 12

// Price data
[stockReturn, monthlyBarLabel] = request.security(syminfo.tickerid, '1M', [ta.roc(close,1) , str.tostring(year) + str.tostring(month)])
indexReturn = request.security(indexSymbol, '1M', ta.roc(close,1))

// Vars
var lastAddedBarLabel = string(na)
var stockReturns = array.new_float(na)
var indexReturns = array.new_float(na)
beta = float(na)

// Process new monthly return 
if not na(stockReturn) and not na(indexReturn) and monthlyBarLabel != lastAddedBarLabel
    array.push(stockReturns, stockReturn)
    array.push(indexReturns, indexReturn)
    lastAddedBarLabel := monthlyBarLabel
    numStockValues = array.size(stockReturns)
    numIndexValues = array.size(indexReturns)
    if numStockValues > numMonthlyBars
        array.shift(stockReturns)
    if numIndexValues > numMonthlyBars
        array.shift(indexReturns)

    if numStockValues < 12
        log.info('Not enough data to calculate beta.')
        beta := na
    else
        log.info('Calculating beta as of ' + lastAddedBarLabel + ' using ' + str.tostring(array.size(stockReturns)) + ' month(s) of data with the following stock and index values: \n' +  str.tostring(stockReturns) + '\n' +  str.tostring(indexReturns))
        covariance = array.covariance(stockReturns, indexReturns)
        varianceIndex = array.variance(indexReturns)
        beta := covariance / varianceIndex

hline(1)
plot(beta, style= plot.style_circles, linewidth=1, join = true, color = beta > 1 ? color.new(#009688, 0) : color.new(#f44336, 00), trackprice = true)