<jupyter_start><jupyter_text>Q1: HMC's Approach  Q1  a  The reason that HMC focuses on real returns is to fulfill the objectives and "benchmark" of its stakeholders. HMC was established for the purpose of managing the university's endowment, pension assets, and working capital. Specifically, the endowment had a goal of distributing 4-5% of its funds to the university, and wanted to preserve the value of the remaining assets as well.To maintain this distribution rate indefinitely, the endowment would have to achieve a real rate return of 3-4%, given it received 1% in gifts per year. On top of this, the real expenses for the university was growing by an additional 3%, which would require additional returns.Focusing on nominal returns would not suffice, because the university is already "benchmarking" against its real costs. For example, if the university knows it needs to spend 7% (real) of AUM, but the fund returned only 7% (nominal) when inflation was 2%, then that would be a deterioration of 2% in the endowment pool.Therefore, if nominal returns were to be used in the MV optimization, the results may not be optimal for the purposes of the fund. Even though a certain asset class may have relatively high returns for a given amount of risk, if the returns were measured in real terms, then the mean-variance payoff may not be as high versus a different asset class.  b  It is simply too expensive. In 1999, the active management portion of the fund cost 49bps, or 93 million. It had 38 investment professionals managing $19 billion assets, of which they managed 68% directly. Sifting through thousands of securities to find the best portfolio would require additional headcount and more decision-making processes. More importantly, the cost of executing any portfolio rebalancing for a thousand or more securities would be expensive: bid/offer costs, operational maintenance, soft-dollar relationships, liquidity reserves on low trade volume and higher probabiity for problematic stocks, maintaining models per stock/strategy, etc.Mathematically, the MV optimization would find it difficult to find distinct attributes across so many securities, especially if they are of the same asset class and exhibit similar MV characteristics. This may lead to spurious allocations with high offsets, similar to how we see strange betas in multicollinear regressions.  c The securities or funds in each asset class must be distinct. In other words, the securities/funds that make up one asset class must have as many similar characteristics to each other as possible and be as different as possible to the make-up of other classes. Individual securities/funds should not be double-counted.In terms of MV, this means that each asset class should ideally have a distinct mean and variance from the other classes, but most importantly a correlation vs other asset classes that is not close to or equal to 1.  d When first looking at TIPS, one should first consider the asset class it is most related to. This should be domestic bonds, since HMC is based in the US, TIPS is a US gov't issued security, and is categorized as either a note or bond.If one compares the characteristics of TIPS vs other domestic bonds, then one can do the same analysis written in 1c with respect to mean, variance, and correlation to see how the product acts vs Domestic Bonds, and whether it is acts similarly to Domestic Bonds, similarly to another asset class, or is unique.Given the mandate of HMC is to achieve superior real returns, an analysis in real terms would have to be done - which is conveniently presented in Exhibits 3 and 4. Based on the graph in Exhibit 3, it is clear that TIPS has a different mean. For the relatively short period of time that both products exist, it appears the graphs exhibit different shapes or trends.Exhibit 4 presents us with more numbers to compare. We can see that Infl-Indexed Bonds as an asset class has very low s.d., and has only a correlation of 0.50 against Domestic Bonds, the asset class we would have wanted to categorize TIPS by if we had to put it somwehere. Given its unique s.d. and correlation characteristics, it makes sense to give it its own asset classs - in real terms.  Q2  a You would first need to convert the MV optimization formula to use real returns rather than excess returns. You would then need to do a two-sided bound for $w$'$\Sigma$$w$ such that:Exhibit 5$w$'$\tilde{\mu} = \mu^{p}$Risky asset weights must be between 0 and 1Risk-free asset weight must be between -0.5 and 1Exhibit 6$w$'$\tilde{\mu} = \mu^{p}$All assets must be within a 10% bound of the long-term portfolio allocation mandate  b The portfolio is showing the fact that for a given mean-variance payoff, inflation-indexed bonds outperform both domestic equity and domestic bonds (in addition to other asset classes) and therefore the allocation should be concentrated in TIPS.The constraint is binding across all hypothetical expected real returns differing by 25bps because no matter the portfolio return, the variance of the domestic bonds/equity do not offer a comparative advantage in reducing the variance of the portfolio.Visually, with the given constraints, including these asset classes would pull our portfolio away from the efficient frontier.If we were to use an unconstrained portfolio, we would have massive long/short offsets across these asset classes - a portfolio which would be very costly to create and maintain in the market.  c The detioration in the Sharpe ratio across the board is about 2% - which is not a lot considering how different the constraints are. The vol is consistently higher across all hypothetical returns which should be expected given we are further limiting the bounds of the portfolio.From a different perspective, we are able to diversify across every single asset class (whereas Exhibit 5 virtually eliminated 4 asset classes), at very little performance cost (2%). This way, <NAME> can have a convincing argument of adding a new asset class which will enhance returns, without unsettling anyone with a drastic portfolio overhaul. Then, it would be up to the Board on how conservatively they would like to bound the TIPS asset class, given Exhibit 6 shows a 0 to 100% constraint.  Q2: MV Optimization  1. Summary Statistics<jupyter_code>#Import data

import pandas as pd
import numpy as np

#assume excel sheet in same folder as jupyter notebook

path_to_data_file = '/Users/xuzhen/OneDrive - The University of Chicago/FINM 36700 Portfolio Theory and Risk Management/HW/HW1/multi_asset_etf_data.xlsx'

df = pd.read_excel(path_to_data_file, sheet_name="excess returns")

#convert dataset to returns only with date as index
df = df.set_index('Date')
df_tilde = df
# Display raw data with Date as index
df 
#annualize data, starting with returns

df_tilde_annual = 12 * df_tilde

mu_tilde = df_tilde_annual.mean()

#annualize vol, given returns have been annualized alreadys
sigma =df_tilde_annual.std()/np.sqrt(12)

#create a table which includes mean, vol and the sharpe ratio (which is returns achieved per vol)
table1 = pd.DataFrame({'Mean':mu_tilde, 'Vol':sigma, 'Sharpe':mu_tilde/sigma})

#Question 1a and 1b: Mean and vol are presented in the table below.
#As we can see, SPY has the largest Sharpe ratio, while DBC has the lowest.

table1.sort_values(['Sharpe'],ascending=False)<jupyter_output><empty_output><jupyter_text>2. Descriptive Analysis<jupyter_code>import seaborn as sns
corrmap = df_tilde_annual.corr()
corrmap.replace(1,np.nan,inplace = True)
corrmap
#Question 2a. Draw a heatmap of the correlation matrix to get a general idea of how the correlations vary.
#Highest and lowest correlations follow below the heatmap.

sns.heatmap(corrmap)
corr_rank = corrmap.unstack().sort_values().dropna()

max1 = corr_rank.index[-1]
min1 = corr_rank.index[0]

print("Highest correlation is between:"+str(max1)+" with correlation of "+str(corr_rank[-1]))
print("Lowest correlation is between:{} with correlation of {}".format(min1,corr_rank[0]))
dfbonds = df_tilde_annual.loc[:,['BWX','IEF','TIP']]
dfbonds['BWX_Diff'] = df_tilde_annual['TIP'] - df_tilde_annual['BWX']
dfbonds['IEF_Diff'] = df_tilde_annual['TIP'] - df_tilde_annual['IEF']
dfbonds[['BWX_Diff','IEF_Diff']].plot()<jupyter_output><empty_output><jupyter_text>Question 2b: Based on taking the mean of the TIP diff vs BWX and IEF respectively, we can see that TIP outperforms both Domestic and Foreign bond MFs. However, the outperformance is marginal: TIPS vs Foreign is +13bps, and TIPS vs Domestic is +3bps. The question then is whether this outperformance is statistically significant. When evaluating the p-value of the data, it appears that the p value is high for both TIPS vs Foreign and TIPS vs Domestic, so it would be hard to say that this outperformance is in fact 'outperformance' Question 2c: Based on the data, it is difficult to say that TIPS expands the opportunity set, given that its mean returns are similar to that of IEF and BWX. The only point worth mentioning would be the fact that it has a lower standard deviation, which may offer portfolio diversification benefits and achieving optimal mean at lower variance/s.d. However, if we think about TIPS without limiting ourselves to the data, then TIPS as an investment product may offer additional benefits. For example, BWX and IEF are made up of foreign and domestic bonds, while TIPS is specifically US Gov't issued inflation linked bonds. Liquidity/market participants may be different as well in the different spaces.<jupyter_code>dfbonds.describe()
from scipy.stats import ttest_ind

#show that the p-values suggest that there is no significant difference between the foreign/domestic bonds and TIPS
print(ttest_ind(dfbonds['BWX'], dfbonds['TIP']))
print(ttest_ind(df_tilde_annual['EFA'], dfbonds['TIP']))<jupyter_output>Ttest_indResult(statistic=-0.6655860356292569, pvalue=0.5061940502903506)
Ttest_indResult(statistic=1.3041039284832523, pvalue=0.19321133819271927)<jupyter_text>3. The MV frontier<jupyter_code># (a) Compute and display the weights of the tangency portfolio:

def compute_tangency(df_tilde, diagonalize_Sigma=False):
    Sigma = df_tilde.cov()
    # N is the number of assets
    N =Sigma.shape[0]
    Sigma_adj = Sigma.copy()
    if diagonalize_Sigma:
        Sigma_adj.loc[:,:] = np.diag(np.diag(Sigma_adj))
    
    mu_tilde = df_tilde.mean()
    Sigma_inv = np.linalg.inv(Sigma_adj)
    weights = Sigma_inv @ mu_tilde / (np.ones(N) @ Sigma_inv @ mu_tilde)
    omega_tangency = pd.Series(weights, index=mu_tilde.index)
    return omega_tangency, mu_tilde, Sigma

omega_tangency, mu_tilde, Sigma = compute_tangency(df_tilde_annual)

omega_tangency
# (b) Compute the mean, volatility and Sharpe ratio for the tangency
# Mean

mean = mu_tilde @ omega_tangency

# Volatility

vol = np.sqrt(omega_tangency @ Sigma @ omega_tangency)/np.sqrt(12)

# Sharpe ratio

sharpe_ratio = mean/vol

print("Mean: ",mean, ", vol: ",vol,", sharpe_ratio: ",sharpe_ratio)<jupyter_output>Mean:  0.23776716339549708 , vol:  0.10480197847812801 , sharpe_ratio:  2.268727812663562<jupyter_text>4. The allocation<jupyter_code># (a) Compute and display the weights of MV portfolios with target returns =0.01

def target_mv_portfolio(df_tilde, target_return=0.01*12, diagonalize_Sigma=False):
    
    omega_tangency, mu_tilde, Sigma = compute_tangency(df_tilde, diagonalize_Sigma=diagonalize_Sigma)
    
    Sigma_adj = Sigma.copy()
    
    if diagonalize_Sigma:
        Sigma_adj.loc[:,:] = np.diag(np.diag(Sigma_adj))
    Sigma_inv = np.linalg.inv(Sigma_adj)
    N = Sigma_adj.shape[0]
    delta_tilde = ((np.ones(N) @ Sigma_inv @ mu_tilde)/(mu_tilde @ Sigma_inv @ mu_tilde)) * target_return
    omega_star = delta_tilde * omega_tangency
    
    return omega_star

omega_star = target_mv_portfolio(df_tilde_annual, target_return=0.01*12)

omega_star
omega_star.sum()
# (b) What is the mean, volatility, and Sharpe ratio for $w^p$?
# Mean

mean = mu_tilde @ omega_star

# Volatility

vol = np.sqrt(omega_star @ Sigma @ omega_star)/np.sqrt(12)

# Sharpe Ratio

sharpe_ratio = mean/vol

print("Mean: ",mean,", vol: ",vol,", sharpe_ratio: ",sharpe_ratio)
# (c) Discuss the allocation. In which asset is the portfolio most long? and short?

omega_star.sort_values(ascending=False)

#The longest position is in Domestic Bonds, which does not have the highest Sharpe Ratio. 
#This is a reminder that MV optimization will put strong weights on some securities due to their covariances, 
#not due to their means, vols or Sharpe Ratios
# (d) Does this line up with wich assets have the strongest Sharpe ratios?
# See tables. It does not.<jupyter_output><empty_output><jupyter_text>5. Simple Portfolios<jupyter_code># (a) Calculate the performance of the equally-weighted portfolio over the sample. 
# Rescale the entire weighting vector to have target mean w ̃ = .01. 
# Report its mean, volatility, and Sharpe ratio.

def compute_equal_weight(df_tilde, target_mean):
    Sigma = df_tilde.cov()
    # N is the number of assets
    N =Sigma.shape[0]
    mu_tilde = df_tilde.mean()
    original_equal_weight = np.ones(N)
    original_mean = mu_tilde @ original_equal_weight
    scaler = target_mean / original_mean
    weights = scaler * original_equal_weight
    omega_equal = pd.Series(weights, index=mu_tilde.index)
    return omega_equal, mu_tilde, Sigma

# annual_target_mean = 12 * mu_p_tilde = 0.12
omega_equal, mu_tilde, Sigma = compute_equal_weight(df_tilde_annual, 0.12)
print(omega_equal)
print(omega_equal.sum())
# Mean

mean = mu_tilde @ omega_equal

# Volatility

vol = np.sqrt(omega_equal @ Sigma @ omega_equal)/np.sqrt(12)

# Sharpe Ratio

sharpe_ratio = mean/vol

print("Mean: ",mean,", vol: ",vol,", sharpe_ratio: ",sharpe_ratio)
# (b) Calculate the performance of the “risk-parity” portfolio over the sample.

def compute_risk_parity(df_tilde, target_mean):
    Sigma = df_tilde.cov()
    # N is the number of assets
    N =Sigma.shape[0]
    mu_tilde = df_tilde.mean()
    vol =df_tilde.std()/np.sqrt(12)
    original_weight = vol.copy()
    original_weight = original_weight.apply(lambda x: 1/x)
    original_mean = mu_tilde @ original_weight
    scaler = target_mean / original_mean
    weights = scaler * original_weight
    omega_equal = pd.Series(weights, index=mu_tilde.index)
    return omega_equal, mu_tilde, Sigma

# annual_target_mean = 12 * mu_p_tilde = 0.12
omega_risk_parity, mu_tilde, Sigma = compute_risk_parity(df_tilde_annual, 0.12)
print(omega_risk_parity)
print(omega_risk_parity.sum())
# Mean

mean = mu_tilde @ omega_risk_parity

# Volatility

vol = np.sqrt(omega_risk_parity @ Sigma @ omega_risk_parity)/np.sqrt(12)

# Sharpe Ratio

sharpe_ratio = mean/vol

print("Mean: ",mean,", vol: ",vol,", sharpe_ratio: ",sharpe_ratio)<jupyter_output>Mean:  0.12 , vol:  0.12835250527091432 , sharpe_ratio:  0.9349252649702111<jupyter_text>(c) How does these compare to the MV portfolio from problem 2.4?\With the same target mean, the equally weighted portfolio and the risk parity portfolio from problem 2.5 both have much higher volatility than the MV portffolio from 2.4. The Sharpe ratios of the two portfolios are also much lower than the MV portfolio. This implies that these two portfolios are less desired than the MV portfolio.   6. Out-of-Sample Performance<jupyter_code># (a) Using only data through the end of 2020, compute w^p for $/mu^p = 0.01$, ...
df_temp = df_tilde_annual.loc[:'2020', :]
omega_tangency, mu_tilde, Sigma = compute_tangency(df_temp)
omega_star = target_mv_portfolio(df_temp, target_return=0.01*12)

omega_star
# (b) Calculate the portfolio's Sharpe ratio within that sample

mean = omega_star @ mu_tilde
vol = np.sqrt(omega_star @ Sigma @ omega_star)/np.sqrt(12)

sharpe_ratio_in_sample = mean/vol

sharpe_ratio_in_sample
# (c) Calculate the portfolio’s Sharpe ratio based on performance in 2021.

df_temp = df_tilde_annual.loc["2021", :]

omega_tangency, mu_tilde, Sigma = compute_tangency(df_temp)
mean = omega_star @ mu_tilde

vol = np.sqrt(omega_star @ Sigma @ omega_star)/np.sqrt(12)

sharpe_ratio_out_of_sample = mean/vol

sharpe_ratio_out_of_sample<jupyter_output><empty_output>