Two-Stock Portfolio Calculations

Consider a portfolio consisting of 2 stocks, stock 1 and stock 2. The weights on each stock, \(w_1\) and \(w_2\), are the percentages invested in each.

\(w_1\): Portfolio weight on stock 1
\(w_2\): Portfolio weight on stock 2, where
\(w_1 + w_2=1\).

The following variables describe the characteristics of the 2 stocks:

\(r_1\): Expected return on stock 1
\(r_2\): Expected return on stock 2

\(\sigma_1\): Standard deviation of return for stock 1
\(\sigma_2\): Standard deviation of return for stock 2

\(corr_{12}\): Correlation of returns between stock 1 and stock 2

Given these variables, the following 2 rules describe the expected return, variance, and standard deviation of the combined portfolio:

Portfolio Rule 1

\[E(r_p)=w_1r_1+w_2r_2\]

Portfolio Rule 2

\[Variance_p=(w_1\sigma_1)^2+(w_2\sigma_2)^2+2w_1w_2\sigma_1\sigma_2corr_{12}\] \[\sigma_p=\sqrt{Variance_p}\]

In this question, I am asking you to create functions for portfolio expected returns and standard deviation. Please fill in the Python code in the notebook cells below.

Instructions

A. Use the tidyverse library

library(tidyverse)
── Attaching core tidyverse packages ── tidyverse 2.0.0 ──
✔ dplyr     1.1.4     ✔ readr     2.1.5
✔ forcats   1.0.0     ✔ stringr   1.5.1
✔ ggplot2   3.5.2     ✔ tibble    3.2.1
✔ lubridate 1.9.4     ✔ tidyr     1.3.1
✔ purrr     1.0.4     
── Conflicts ──────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(ggplot2)

B. Create a function to calculate the expected return on the portfolio given 3 inputs: w1, r1, and r2. You will need to calculate w2 in the function itself.

portfolio_return <- function(w1, r1, r2) { w2 <- 1 - w1 return(w1 * r1 + w2 * r2) }

C. Create a functions to calculate the standard deviation of a portfolio given 4 inputs: w1, s1, s2, and corr12. You will need to calculate w2 in the function itself.

portfolio_sd <- function(w1, s1, s2, corr12) { w2 <- 1 - w1 variance <- (w1s1)^2 + (w2s2)^2 + 2w1w2s1s2*corr12 return(sqrt(variance)) }

D. For a test case, use the constants defined in the cell below. You will need to make sure you run the cell so the variables will be set in memory.

ret_apple=0.20
ret_walmart=0.10
sd_apple=0.25
sd_walmart=0.20
corr_aw=0.25

<!-- rnb-source-end -->


<!-- rnb-output-end -->

<!-- rnb-output-begin eyJkYXRhIjoiRXJyb3I6IGF0dGVtcHQgdG8gdXNlIHplcm8tbGVuZ3RoIHZhcmlhYmxlIG5hbWVcbiJ9 -->

Error: attempt to use zero-length variable name ```

E. Using the functions you created and the constants above, calculate and print the expected return and standard deviation of a portfolio with a 0.5 weight in apple and a 0.5 weight in walmart.

w_test <- 0.5 test_return <- portfolio_return(w_test, ret_apple, ret_walmart) test_sd <- portfolio_sd(w_test, sd_apple, sd_walmart, corr_aw)

cat(“50/50 Portfolio:”) cat(“Expected Return:”, test_return, “”) cat(“Standard Deviation:”, test_sd, “”)

F.Using seq(), create a range of weights on apple (stock 1) ranging from 0 to 1 in increments of 0.01.

weights <- seq(0, 1, by = 0.01)

G. Create two arrays, one with the portfolio standard deviations corresponding to the different weights, the other giving the portfolio expected returns based on the different weights.

returns <- sapply(weights, portfolio_return, r1 = ret_apple, r2 = ret_walmart) sds <- sapply(weights, portfolio_sd, s1 = sd_apple, s2 = sd_walmart, corr12 = corr_aw)

H. Create a plot with portfolio standard deviation on the x-axis and portfolio expected return on the y-axis.

plot_data <- data.frame(SD = sds, Return = returns) ggplot(plot_data, aes(x = SD, y = Return)) + geom_line(color = “blue”) + geom_point(data = plot_data[which.min(sds), ], color = “red”, size = 3) + labs(title = “Efficient Frontier”, x = “Portfolio Standard Deviation”, y = “Portfolio Expected Return”) + theme_minimal()

I. What is the minimum standard deviation of all the points you plotted on the curve?

min_sd <- min(sds) cat(“Minimum Standard Deviation:”, min_sd, “”)

#Minimum Standard Deviation = 0.1739

J. What are the weights corresponding to the portfolio with the minimum standard deviation?

min_sd_index <- which.min(sds) min_sd_weights <- c(weights[min_sd_index], 1 - weights[min_sd_index]) cat(“Optimal Weights (Apple, Walmart):”, min_sd_weights)

#Weights corresponding to the portfolio with the minimum standard deviation? # Apple = 0.35, Walmart = 0.65

LS0tCnRpdGxlOiAiRnVuY3Rpb25zIGFuZCBQbG90dGluZyBQcmFjdGljZSBRdWVzdGlvbiIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgVHdvLVN0b2NrIFBvcnRmb2xpbyBDYWxjdWxhdGlvbnMgIApDb25zaWRlciBhIHBvcnRmb2xpbyBjb25zaXN0aW5nIG9mIDIgc3RvY2tzLCBzdG9jayAxIGFuZCBzdG9jayAyLiAgVGhlIHdlaWdodHMgb24gZWFjaCBzdG9jaywgJHdfMSQgYW5kICR3XzIkLCBhcmUgdGhlIHBlcmNlbnRhZ2VzIGludmVzdGVkIGluIGVhY2guCgokd18xJDogUG9ydGZvbGlvIHdlaWdodCBvbiBzdG9jayAxICAKJHdfMiQ6IFBvcnRmb2xpbyB3ZWlnaHQgb24gc3RvY2sgMiwgd2hlcmUgIAokd18xICsgd18yPTEkLiAgCgpUaGUgZm9sbG93aW5nIHZhcmlhYmxlcyBkZXNjcmliZSB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSAyIHN0b2NrczogIAoKJHJfMSQ6IEV4cGVjdGVkIHJldHVybiBvbiBzdG9jayAxICAKJHJfMiQ6IEV4cGVjdGVkIHJldHVybiBvbiBzdG9jayAyICAKCiRcc2lnbWFfMSQ6IFN0YW5kYXJkIGRldmlhdGlvbiBvZiByZXR1cm4gZm9yIHN0b2NrIDEgIAokXHNpZ21hXzIkOiBTdGFuZGFyZCBkZXZpYXRpb24gb2YgcmV0dXJuIGZvciBzdG9jayAyICAKCiRjb3JyX3sxMn0kOiBDb3JyZWxhdGlvbiBvZiByZXR1cm5zIGJldHdlZW4gc3RvY2sgMSBhbmQgc3RvY2sgMiAgCgpHaXZlbiB0aGVzZSB2YXJpYWJsZXMsIHRoZSBmb2xsb3dpbmcgMiBydWxlcyBkZXNjcmliZSB0aGUgZXhwZWN0ZWQgcmV0dXJuLCB2YXJpYW5jZSwgYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgY29tYmluZWQgcG9ydGZvbGlvOiAgCgojIyMjIFBvcnRmb2xpbyBSdWxlIDEgIAoKJCRFKHJfcCk9d18xcl8xK3dfMnJfMiQkCgojIyMjIFBvcnRmb2xpbyBSdWxlIDIgIAoKJCRWYXJpYW5jZV9wPSh3XzFcc2lnbWFfMSleMisod18yXHNpZ21hXzIpXjIrMndfMXdfMlxzaWdtYV8xXHNpZ21hXzJjb3JyX3sxMn0kJAokJFxzaWdtYV9wPVxzcXJ0e1ZhcmlhbmNlX3B9JCQKCkluIHRoaXMgcXVlc3Rpb24sIEkgYW0gYXNraW5nIHlvdSB0byBjcmVhdGUgZnVuY3Rpb25zIGZvciBwb3J0Zm9saW8gZXhwZWN0ZWQgcmV0dXJucyBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uLiAgUGxlYXNlIGZpbGwgaW4gdGhlIFB5dGhvbiBjb2RlIGluIHRoZSBub3RlYm9vayBjZWxscyBiZWxvdy4KCgojIyMgSW5zdHJ1Y3Rpb25zCkEuIFVzZSB0aGUgdGlkeXZlcnNlIGxpYnJhcnkgIApgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgpCLiBDcmVhdGUgYSBmdW5jdGlvbiB0byBjYWxjdWxhdGUgdGhlIGV4cGVjdGVkIHJldHVybiBvbiB0aGUgcG9ydGZvbGlvIGdpdmVuIDMgaW5wdXRzOiB3MSwgcjEsIGFuZCByMi4gIFlvdSB3aWxsIG5lZWQgdG8gY2FsY3VsYXRlIHcyIGluIHRoZSBmdW5jdGlvbiBpdHNlbGYuICAKCnBvcnRmb2xpb19yZXR1cm4gPC0gZnVuY3Rpb24odzEsIHIxLCByMikgewogIHcyIDwtIDEgLSB3MQogIHJldHVybih3MSAqIHIxICsgdzIgKiByMikKfQoKQy4gQ3JlYXRlIGEgZnVuY3Rpb25zIHRvIGNhbGN1bGF0ZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIGEgcG9ydGZvbGlvIGdpdmVuIDQgaW5wdXRzOiB3MSwgczEsIHMyLCBhbmQgY29ycjEyLiAgWW91IHdpbGwgbmVlZCB0byBjYWxjdWxhdGUgdzIgaW4gdGhlIGZ1bmN0aW9uIGl0c2VsZi4KCnBvcnRmb2xpb19zZCA8LSBmdW5jdGlvbih3MSwgczEsIHMyLCBjb3JyMTIpIHsKICB3MiA8LSAxIC0gdzEKICB2YXJpYW5jZSA8LSAodzEqczEpXjIgKyAodzIqczIpXjIgKyAyKncxKncyKnMxKnMyKmNvcnIxMgogIHJldHVybihzcXJ0KHZhcmlhbmNlKSkKfQoKRC4gRm9yIGEgdGVzdCBjYXNlLCB1c2UgdGhlIGNvbnN0YW50cyBkZWZpbmVkIGluIHRoZSBjZWxsIGJlbG93LiBZb3Ugd2lsbCBuZWVkIHRvIG1ha2Ugc3VyZSB5b3UgcnVuIHRoZSBjZWxsIHNvIHRoZSB2YXJpYWJsZXMgd2lsbCBiZSBzZXQgaW4gbWVtb3J5LiAgCmBgYHtyfQojIERlZmluaXRpb24gb2YgQ29uc3RhbnRzCnJldF9hcHBsZT0wLjIwCnJldF93YWxtYXJ0PTAuMTAKc2RfYXBwbGU9MC4yNQpzZF93YWxtYXJ0PTAuMjAKY29ycl9hdz0wLjI1CmBgYApFLiBVc2luZyB0aGUgZnVuY3Rpb25zIHlvdSBjcmVhdGVkIGFuZCB0aGUgY29uc3RhbnRzIGFib3ZlLCBjYWxjdWxhdGUgYW5kIHByaW50IHRoZSBleHBlY3RlZCByZXR1cm4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBhIHBvcnRmb2xpbyB3aXRoIGEgMC41IHdlaWdodCBpbiBhcHBsZSBhbmQgYSAwLjUgd2VpZ2h0IGluIHdhbG1hcnQuICAKCndfdGVzdCA8LSAwLjUKdGVzdF9yZXR1cm4gPC0gcG9ydGZvbGlvX3JldHVybih3X3Rlc3QsIHJldF9hcHBsZSwgcmV0X3dhbG1hcnQpCnRlc3Rfc2QgPC0gcG9ydGZvbGlvX3NkKHdfdGVzdCwgc2RfYXBwbGUsIHNkX3dhbG1hcnQsIGNvcnJfYXcpCgpjYXQoIjUwLzUwIFBvcnRmb2xpbzpcbiIpCmNhdCgiRXhwZWN0ZWQgUmV0dXJuOiIsIHRlc3RfcmV0dXJuLCAiXG4iKQpjYXQoIlN0YW5kYXJkIERldmlhdGlvbjoiLCB0ZXN0X3NkLCAiXG5cbiIpCgoKRi5Vc2luZyBzZXEoKSwgY3JlYXRlIGEgcmFuZ2Ugb2Ygd2VpZ2h0cyBvbiBhcHBsZSAoc3RvY2sgMSkgcmFuZ2luZyBmcm9tIDAgdG8gMSBpbiBpbmNyZW1lbnRzIG9mIDAuMDEuICAKCndlaWdodHMgPC0gc2VxKDAsIDEsIGJ5ID0gMC4wMSkKCkcuIENyZWF0ZSB0d28gYXJyYXlzLCBvbmUgd2l0aCB0aGUgcG9ydGZvbGlvIHN0YW5kYXJkIGRldmlhdGlvbnMgY29ycmVzcG9uZGluZyB0byB0aGUgZGlmZmVyZW50IHdlaWdodHMsICAgdGhlIG90aGVyIGdpdmluZyB0aGUgcG9ydGZvbGlvIGV4cGVjdGVkIHJldHVybnMgYmFzZWQgb24gdGhlIGRpZmZlcmVudCB3ZWlnaHRzLiAgCgoKcmV0dXJucyA8LSBzYXBwbHkod2VpZ2h0cywgcG9ydGZvbGlvX3JldHVybiwgcjEgPSByZXRfYXBwbGUsIHIyID0gcmV0X3dhbG1hcnQpCnNkcyA8LSBzYXBwbHkod2VpZ2h0cywgcG9ydGZvbGlvX3NkLCBzMSA9IHNkX2FwcGxlLCBzMiA9IHNkX3dhbG1hcnQsIGNvcnIxMiA9IGNvcnJfYXcpCgpILiBDcmVhdGUgYSBwbG90IHdpdGggcG9ydGZvbGlvIHN0YW5kYXJkIGRldmlhdGlvbiBvbiB0aGUgeC1heGlzIGFuZCBwb3J0Zm9saW8gZXhwZWN0ZWQgcmV0dXJuIG9uIHRoZSB5LWF4aXMuICAKCgpwbG90X2RhdGEgPC0gZGF0YS5mcmFtZShTRCA9IHNkcywgUmV0dXJuID0gcmV0dXJucykKZ2dwbG90KHBsb3RfZGF0YSwgYWVzKHggPSBTRCwgeSA9IFJldHVybikpICsKICBnZW9tX2xpbmUoY29sb3IgPSAiYmx1ZSIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBwbG90X2RhdGFbd2hpY2gubWluKHNkcyksIF0sIGNvbG9yID0gInJlZCIsIHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJFZmZpY2llbnQgRnJvbnRpZXIiLAogICAgICAgeCA9ICJQb3J0Zm9saW8gU3RhbmRhcmQgRGV2aWF0aW9uIiwKICAgICAgIHkgPSAiUG9ydGZvbGlvIEV4cGVjdGVkIFJldHVybiIpICsKICB0aGVtZV9taW5pbWFsKCkKCgpJLiBXaGF0IGlzIHRoZSBtaW5pbXVtIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBhbGwgdGhlIHBvaW50cyB5b3UgcGxvdHRlZCBvbiB0aGUgY3VydmU/ICAKCgptaW5fc2QgPC0gbWluKHNkcykKY2F0KCJNaW5pbXVtIFN0YW5kYXJkIERldmlhdGlvbjoiLCBtaW5fc2QsICJcbiIpCgojTWluaW11bSBTdGFuZGFyZCBEZXZpYXRpb24gPSAwLjE3MzkKCgpKLiBXaGF0IGFyZSB0aGUgd2VpZ2h0cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBwb3J0Zm9saW8gd2l0aCB0aGUgbWluaW11bSBzdGFuZGFyZCBkZXZpYXRpb24/ICAKCgptaW5fc2RfaW5kZXggPC0gd2hpY2gubWluKHNkcykKbWluX3NkX3dlaWdodHMgPC0gYyh3ZWlnaHRzW21pbl9zZF9pbmRleF0sIDEgLSB3ZWlnaHRzW21pbl9zZF9pbmRleF0pCmNhdCgiT3B0aW1hbCBXZWlnaHRzIChBcHBsZSwgV2FsbWFydCk6IiwgbWluX3NkX3dlaWdodHMpCgojV2VpZ2h0cyBjb3JyZXNwb25kaW5nIHRvIHRoZSBwb3J0Zm9saW8gd2l0aCB0aGUgbWluaW11bSBzdGFuZGFyZCBkZXZpYXRpb24/CiMgQXBwbGUgPSAwLjM1LCBXYWxtYXJ0ID0gMC42NQoKCgoKCg==