<jupyter_start><jupyter_text>Portfolio construction* $r_1, r_2$ denotes the returns for both assets - standard deviations are denoted $\sigma_1, \sigma_2$* Covariance is $Cov(r_1,r_2)$ and correlations is defined as$$\rho_{12}=\frac{Cov\left(r_{1},r_{2}\right)}{\sigma_{1}\cdot\sigma_{2}}$$* The fraction of the capital invested in the two assets are denoted $w_1,w_2=(1-w_1)$* Expected portfolio returns are$$E(r_p)=w_{1}E\left(r_{1}\right)+\left(1-w_{1}\right)E\left(r_{2}\right)$$* Portfolio variance is calculated as$$\sigma_{p}^{2}=w_{1}^{2}\sigma_{1}^{2}+\left(1-w_{1}\right)^{2}\sigma_{2}^{2}+2w_{1}\left(1-w_{1}\right)Cov\left(r_{1},r_{2}\right)$$* $A$ denotes relative risk-aversion Minimum variance portfolioThe portfolio which yields the lowest possible portfolio-variance (risk) without taking into account expected returns (global minimum-variance portfolio) is found by solving,$$\min_{w_{1}}\sigma_{p}^{2}=w_{1}^{2}\sigma_{1}^{2}+\left(1-w_{1}\right)^{2}\sigma_{2}^{2}+2w_{1}\left(1-w_{1}\right)\operatorname{Cov}\left(r_{1},r_{2}\right)$$This is solved by:\begin{align*}w_{1}^{\star}&=\frac{\sigma_{2}^{2}-Cov\left(r_{1},r_{2}\right)}{\sigma_{1}^{2}+\sigma_{2}^{2}-2Cov\left(r_{1},r_{2}\right)}\\w_{2}^{\star}&=1-w_{1}^{\star}\end{align*} Optimal portfolioThe minimum variance portfolio will generally not be the optimal portfolio. Without a risk-free asset the combination of assets depend on the level of risk-aversion. We assume the following utility function and thus following optimization problem,\begin{align*}\max_{w_{1}}U&=E\left(r_{p}\right)-\frac{1}{2}A\sigma_{p}^{2}\\\text{ s.t. }\quad E\left(r_{p}\right)&=w_{1}E\left(r_{1}\right)+\left(1-w_{1}\right)E\left(r_{2}\right)\\\sigma_{p}^{2}&=w_{1}^{2}\sigma_{1}^{2}+\left(1-w_{1}\right)^{2}\sigma_{2}^{2}+2w_{1}\left(1-w_{1}\right)Cov\left(r_{1},r_{2}\right)\end{align*}This is solved by:\begin{align*}w_{1}^{*}&=\frac{E\left(r_{1}\right)-E\left(r_{2}\right)+A\left(\sigma_{2}^{2}-\operatorname{Cov}\left(r_{1},r_{2}\right)\right)}{A\left(\sigma_{1}^{2}+\sigma_{2}^{2}-2\operatorname{Cov}\left(r_{1},r_{2}\right)\right)}\\w_{2}^{*}&=1-w_{1}^{*}\end{align*} This notebookWith the quite simple math out of the way, we can solve the problem at hand.<jupyter_code># Settings
ExpectedReturn_AssetA = 0.08
ExpectedReturn_AssetB = 0.13
risk_aversion = 3

std_AssetA = 0.12
std_AssetB = 0.20

correlation_AssetA_AssetB = 0.3
covariance_AssetA_AssetB = std_AssetA * correlation_AssetA_AssetB * std_AssetB
# create different weights for the two assets in increments of .0001
df = pd.DataFrame({'weight_AssetA': np.linspace(0, 1, num=1000)})
df['weight_AssetB'] = 1-df['weight_AssetA']

# calculate optimal portfolio
def optimal_portfolio(e_1, e_2, a, std_1, std_2, cov):
    w_1 = (e_1-e_2 + a*(std_2**2-cov)) / (a*(std_1**2+std_2**2-2*cov))
    w_2 = 1-w_1
    return {'weight_AssetA': w_1, 'weight_AssetB': w_2, }
    

df = df.append(pd.DataFrame(optimal_portfolio(ExpectedReturn_AssetA,
                                              ExpectedReturn_AssetB,
                                              risk_aversion,
                                              std_AssetA,
                                              std_AssetB,
                                              covariance_AssetA_AssetB), index=['optimal']))

# calculate min. variance portfolio
def minvar_portfolio(e_1, e_2, a, std_1, std_2, cov):
    w_1 = (std_2**2 - cov) / (std_1**2 + std_2**2 - 2*cov)
    w_2 = 1-w_1
    return {'weight_AssetA': w_1, 'weight_AssetB': w_2, }
    

df = df.append(pd.DataFrame(minvar_portfolio(ExpectedReturn_AssetA,
                                              ExpectedReturn_AssetB,
                                              risk_aversion,
                                              std_AssetA,
                                              std_AssetB,
                                              covariance_AssetA_AssetB), index=['minvar']))

# Calculate expected returns
df['ExpectedReturn'] = df['weight_AssetA']*ExpectedReturn_AssetA + df['weight_AssetB']*ExpectedReturn_AssetB

def calc_variance(w_1, w_2, e_1, e_2, std_1, std_2, cov):
    return (w_1**2)*(std_1**2)+(w_2**2)*(std_2**2)+2*w_1*w_2*cov

df['Variance'] = calc_variance(w_1=df['weight_AssetB'],
                               w_2=df['weight_AssetA'],
                               e_1=ExpectedReturn_AssetB,
                               e_2=ExpectedReturn_AssetA,
                               std_1=std_AssetB,
                               std_2=std_AssetA,
                               cov=covariance_AssetA_AssetB)

df['Standard_Deviation'] = np.sqrt(df['Variance'])
df = df.sort_values(by='ExpectedReturn')
# Plots
def utility_function(E, A, V):
    return E-A*(1/2)*V

def inverse_utility_function(U, A, V):
    return U+A*(1/2)*V

def plot_indifference():
    fixed_util = utility_function(df.loc['optimal']['ExpectedReturn'], 
                                  risk_aversion, 
                                  df.loc['optimal']['Variance'])
    
    return inverse_utility_function(fixed_util, risk_aversion, df['Variance'])

df['Indifference'] = plot_indifference()


plt.xlabel(r'$\sigma_c$')
plt.ylabel(r'$\mathbb{E}(r_C)$')

plt.plot(df['Standard_Deviation'], df['ExpectedReturn'], label='Portfolio Possibilty Frontier')
plt.plot(df['Standard_Deviation'], df['Indifference'], label='Indifference Curve')

# plot min. variance and optimal portfolio
plt.scatter(df.loc['optimal']['Standard_Deviation'], df.loc['optimal']['ExpectedReturn'], 
            zorder=5, marker='s', label='Optimal Portfolio', c='black')
plt.scatter(df.loc['minvar']['Standard_Deviation'], df.loc['minvar']['ExpectedReturn'], 
            zorder=5, marker='o', label='Minimum Variance Portfolio', c='black')

plt.legend()

plt.show()
df.loc[['minvar', 'optimal']]<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.17371213417764847, 'fraction_numerical': 0.03457128187574876, 'mean_word_length': 3.3065585851142227, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '1', 'dir': 'jupyter-structured-clean-dedup', 'id': '188157', 'license': 'MIT', 'n_tokens_mistral': 2010, 'n_tokens_neox': 1935, 'n_words': 322, 'path': 'Notebooks/Financial Economics/01 - Portfolios - Two Assets (no risk-free rate).ipynb', 'repo_name': 'ismand95/PythonForEconomists', 'size': '5843'}