CAPM Model with Returns Table
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © Mishari_Algos

//@version=5
indicator("CAPM Model with Returns Table", shorttitle = "CAPM MODEL", overlay=false, precision = 3)

// User inputs
benchmarkSymbol = input.symbol ("CRYPTOCAP:TOTAL")
riskFreeRate = input.float(0.01, "Risk-free Rate (e.g., 0.01 for 1%)",group = "Plot Settings ")

assetSymbol = syminfo.tickerid

Benchmark_Asset_Length = input.int(30, "Benchmark Period", tooltip = "This setting is only used for the plot and not included in the calculations displayed on the table",group = "Plot Settings ")
period1 = input.int(30, "First Period (days)",group = "Periods Settings ",tooltip = "All Periods below are for table calulcations only!")
period2 = input.int(60, "Second Period (days)",group = "Periods Settings ")
period3 = input.int(90, "Third Period (days)",group = "Periods Settings ")
period4 = input.int(120, "Fourth Period (days)",group = "Periods Settings ")

// Retrieve the close prices for the benchmark and the asset
benchmarkClose = request.security(benchmarkSymbol, timeframe.period, close)
assetClose = request.security(assetSymbol, timeframe.period, close)

// Calculate daily returns for benchmark and asset
benchmarkReturn = ta.change(benchmarkClose) / benchmarkClose[1]
assetReturn = ta.change(assetClose) / assetClose[1]


// Helper function to calculate beta for a given period
calcBeta(period) =>
    benchmarkMeanReturnPeriod = ta.sma(benchmarkReturn, period)
    assetMeanReturnPeriod = ta.sma(assetReturn, period)
    covXYPeriod = ta.sma((benchmarkReturn - benchmarkMeanReturnPeriod) * (assetReturn - assetMeanReturnPeriod), period)
    varXPeriod = ta.sma(math.pow(benchmarkReturn - benchmarkMeanReturnPeriod, 2), period)
    covXYPeriod / varXPeriod

// Calculate expected returns for each user-specified period
expectedReturnPeriod1 = riskFreeRate + calcBeta(period1) * (ta.sma(benchmarkReturn, period1) - riskFreeRate)
expectedReturnPeriod2 = riskFreeRate + calcBeta(period2) * (ta.sma(benchmarkReturn, period2) - riskFreeRate)
expectedReturnPeriod3 = riskFreeRate + calcBeta(period3) * (ta.sma(benchmarkReturn, period3) - riskFreeRate)
expectedReturnPeriod4 = riskFreeRate + calcBeta(period4) * (ta.sma(benchmarkReturn, period4) - riskFreeRate)






// Calculate mean returns for benchmark and asset
benchmarkMeanReturn = ta.sma(benchmarkReturn, Benchmark_Asset_Length)
assetMeanReturn = ta.sma(assetReturn, Benchmark_Asset_Length)

// Calculate the asset's mean return for each user-specified period
assetMeanReturnPeriod1 = ta.sma(assetReturn, period1)
assetMeanReturnPeriod2 = ta.sma(assetReturn, period2)
assetMeanReturnPeriod3 = ta.sma(assetReturn, period3)
assetMeanReturnPeriod4 = ta.sma(assetReturn, period4)

// Calculate covariance and variance
covXY = ta.sma((benchmarkReturn - benchmarkMeanReturn) * (assetReturn - assetMeanReturn), Benchmark_Asset_Length)
varX = ta.sma(math.pow(benchmarkReturn - benchmarkMeanReturn, 2), Benchmark_Asset_Length)

// Calculate beta
beta = covXY / varX

// Using CAPM to estimate expected return
expectedReturn = riskFreeRate + beta * (benchmarkMeanReturn - riskFreeRate)



// Average of the expected returns
avgExpectedReturn = (expectedReturnPeriod1 + expectedReturnPeriod2 + expectedReturnPeriod3 + expectedReturnPeriod4) / 4.0

// Average of the asset's mean returns
avgAssetMeanReturn = (assetMeanReturnPeriod1 + assetMeanReturnPeriod2 + assetMeanReturnPeriod3 + assetMeanReturnPeriod4) / 4.0

// Display results in a table
var table returnsTable = table.new(position.middle_left, 6, 3,frame_width = 1,frame_color = color.black,border_width = 1)
table.cell(returnsTable, 0, 0, "Period", bgcolor=color.gray, text_color=color.white)
table.cell(returnsTable, 0, 1, "Expected Return", bgcolor=color.gray, text_color=color.rgb(14, 145, 42))
table.cell(returnsTable, 1, 0, str.tostring(period1) + " Days", bgcolor=color.silver)
table.cell(returnsTable, 1, 1, str.tostring(math.round(expectedReturnPeriod1 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 2, 0, str.tostring(period2) + " Days", bgcolor=color.silver)
table.cell(returnsTable, 2, 1, str.tostring(math.round(expectedReturnPeriod2 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 3, 0, str.tostring(period3) + " Days", bgcolor=color.silver)
table.cell(returnsTable, 3, 1, str.tostring(math.round(expectedReturnPeriod3 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 4, 0, str.tostring(period4) + " Days", bgcolor=color.silver)
table.cell(returnsTable, 4, 1, str.tostring(math.round(expectedReturnPeriod4 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 5, 0, "Average", bgcolor=color.gray, text_color=color.white)
table.cell(returnsTable, 5, 1, str.tostring(math.round(avgExpectedReturn * 100, 2)) + "%", bgcolor=color.gray, text_color=color.white)

// Add asset's mean returns to the table
table.cell(returnsTable, 0, 2, "Asset's Mean Return", bgcolor=color.gray, text_color=color.rgb(25, 69, 158))

table.cell(returnsTable, 1, 2, str.tostring(math.round(assetMeanReturnPeriod1 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 2, 2, str.tostring(math.round(assetMeanReturnPeriod2 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 3, 2, str.tostring(math.round(assetMeanReturnPeriod3 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 4, 2, str.tostring(math.round(assetMeanReturnPeriod4 * 100, 2)) + "%", bgcolor=color.silver)
table.cell(returnsTable, 5, 2, str.tostring(math.round(avgAssetMeanReturn * 100, 2)) + "%", bgcolor=color.gray, text_color=color.white)



// ========== Start of Implication Code ==========
// Determine implication based on comparison
implication() =>
    er = avgExpectedReturn
    mr = avgAssetMeanReturn
    
    if er > 0 and mr > er
        "Historical performance of the asset has been better than what the model currently predicts.\n The asset might be experiencing a slowdown or the model might be conservative."
    else if er > 0 and er > mr
        "The model is more optimistic about the asset's future performance than its recent history suggests.\n Possible signs of a turnaround or an upcoming positive trend."
    else if er > 0 and mr == 0
        "The model suggests positive returns, but the asset's historical performance has been neutral.\n This might indicate changing conditions or new factors influencing the asset."
    else if er > 0 and mr < 0
        "The model suggests positive returns, but historically the asset has performed poorly. \nThis could indicate potential turnaround, or it might suggest that the model's assumptions or inputs need review."
    else if er == 0 and mr > 0
        "The model suggests neutral returns while the asset has had positive historical returns.\n This might indicate a potential slowdown or stagnation."
    else if er == 0 and mr == 0
        "Both the model and historical data suggest neutral returns. \nThe asset may be seen as stable or stagnant."
    else if er == 0 and mr < 0
        "The model expects no change, but the asset has historically lost value. \nThis might indicate potential stabilization after a downturn."
    else if er < 0 and mr < er
        "Both the model and historical data suggest negative returns, but the model is more pessimistic than the asset's history. \nThe situation might be worsening, or the model might be overly pessimistic."
    else if er < 0 and er < mr
        "Both suggest negative returns, but historical data is more pessimistic than the model's prediction.\n This could indicate potential stabilization or a slight recovery."
    else if er < 0 and mr == 0
        "The model suggests negative returns, while the asset's historical performance has been neutral.\n This might indicate potential challenges ahead."
    else
        "No specific implication detected." // Default message in case no conditions are met

// Create a mutable label variable
var label impLabel = na

// Update the label
// Offset for label placement
labelOffset = 300

// Update the label
if (na(impLabel))
    impLabel := label.new(x=bar_index - labelOffset, y=0, text=implication(), color=color.gray )
else
    label.set_xy(impLabel, bar_index - labelOffset, 0)
    label.set_text(impLabel, implication())


// ========== End of Implication Code ==========
// Determine implication based on comparison





// Plot expected return and asset's mean return
plot(expectedReturn * 100, title="Expected Return (%)", color=color.green)
plot(assetMeanReturn * 100, title="Asset's Mean Return (%)", color=color.blue)

hline(0, "Zero Return", color=color.gray)
