<jupyter_start><jupyter_text><NAME> FINM 36700 Autumn 2021Midterm 1 Solution  1 Short Answer (25 pts) 1. False. Mean-variance optimization is trying to minimize the covariance of different assets, Sharpe ratio does not affect the optimization 2. True, Long term investment might have more value to seek compared with short term  3. We should include an intercept. In short samples, mean returns may be estimated inaccurately, so we may want to include α (eliminate means) to focus on explaining variation. 4. No it is not effective. HDG fails to deliver the same high returns compendated with the high risk of HRFI. The HFRI also shows a very high excess kurtosis, but EDG has a very small excess kurtosis  5. It would be easy to get a large $\alpha$  due to in-sample luck. When testing out of sample data, $\alpha$ might be negative.  2 Allocation (25 pts)<jupyter_code>import pandas as pd
import numpy as np
import statsmodels.api as sm
from statsmodels.regression.rolling import RollingOLS
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings("ignore")
hf_data = pd.read_excel('proshares_analysis_data.xlsx', sheet_name = 'hedge_fund_series', engine='openpyxl')
hf_data = hf_data.set_index('date')

factor_data = pd.read_excel('proshares_analysis_data.xlsx', sheet_name = 'merrill_factors', engine='openpyxl')
factor_data = factor_data.set_index('date')

other_data = pd.read_excel('proshares_analysis_data.xlsx', sheet_name = 'other_data', engine='openpyxl')
other_data = other_data.set_index('date')
other_data['SPY US Equity'] = factor_data['SPY US Equity']
factor_data = factor_data.subtract(factor_data["USGG3M Index"], axis=0) 
factor_data = factor_data.drop(columns = ['USGG3M Index'])
factor_data.head()<jupyter_output><empty_output><jupyter_text>1. (5pts) What are the weights of the tangency portfolio, wtan?<jupyter_code>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)

    # For convenience, I'll wrap the solution back into a pandas.Series object.

    omega_tangency = pd.Series(weights, index=mu_tilde.index)

    return omega_tangency, mu_tilde, Sigma_adj
omega_tangency, mu_tilde, Sigma = compute_tangency(factor_data)

omega_tangency.to_frame('Tangency Weights')<jupyter_output><empty_output><jupyter_text>2. (5pts) What are the weights of the optimal portfolio, w∗, with a targeted excess mean return of.02 per month?<jupyter_code>def target_mv_portfolio(df_tilde, target_return=0.02, 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, mu_tilde, Sigma_adj
omega_star, mu_tilde, Sigma = target_mv_portfolio(factor_data)

omega_star_df = omega_star.to_frame('MV Portfolio Weights')

omega_star_df<jupyter_output><empty_output><jupyter_text>- Is the optimal portfolio, w∗, invested in the risk-free rate? Yes! 3. (5pts) Report the mean, volatility, and Sharpe ratio of the optimized portfolio. Annualize all three statistics.<jupyter_code>def portfolio_stats(omega, mu_tilde, Sigma, annualize_fac):
    # Mean
    mean = (mu_tilde @ omega) * annualize_fac

    # Volatility
    vol = np.sqrt(omega @ Sigma @ omega) * np.sqrt(annualize_fac)


    # Sharpe ratio
    sharpe_ratio = mean / vol

    return round(pd.DataFrame(data = [mean, vol, sharpe_ratio], 
                              index = ['Mean', 'Volatility', 'Sharpe'], 
                              columns = ['Portfolio Stats']), 4)

portfolio_stats(omega_star, mu_tilde, Sigma, 12)<jupyter_output><empty_output><jupyter_text>4. (5pts) Re-calculate the optimal portfolio, w∗ with target excess mean of .02 per month. But this time only use data through 2018 in doing the calculation. Calculate the return in 2019-2021 based on those optimal weights.<jupyter_code>df_ex_2018 = factor_data.loc[:'2018']

omega_p, mu_tilde_p, Sigma_p = target_mv_portfolio(df_ex_2018, target_return=0.02, diagonalize_Sigma=False)

omega_p.to_frame('MV Weights')
df_ex_2021 = factor_data.loc['2019':'2021']

omega_t2021, mu_tilde2021, Sigma2021 = compute_tangency(df_ex_2021)

portfolio_stats(omega_p, mu_tilde2021, Sigma2021, 12)<jupyter_output><empty_output><jupyter_text>5. (5pts) Suppose that instead of optimizing these 5 risky assets, we optimized 5 commodity futures: oil, coffee, cocoa, lumber, cattle, and gold. - It would be better (?)  3 Hedging & Replication (20 pts)<jupyter_code>factor_data.head()<jupyter_output><empty_output><jupyter_text>1. (5pts) What is the optimal hedge ratio over the full sample of data? That is, for every dollar invested in EEM, what would you invest in SPY?<jupyter_code>X = factor_data['SPY US Equity']
y = factor_data['EEM US Equity']
static_model_noint1 = sm.OLS(y,X).fit()
static_model_noint1.summary()
1 / static_model_noint1.params[0]<jupyter_output><empty_output><jupyter_text>2. (5pts) What is the mean, volatility, and Sharpe ratio of the hedged position, had we applied that hedge throughout the full sample? Annualize the statistics.<jupyter_code>df = static_model_noint1.fittedvalues
mean = df.mean() * 12
volatility = df.std() * np.sqrt(12)
Sharpe_ratio = mean / volatility
print("Mean: ", mean, "\n Vol ", volatility, "\n Sharpe ratio: ", Sharpe_ratio)<jupyter_output>Mean:  0.13129322304184685 
 Vol  0.12544418860893303 
 Sharpe ratio:  1.0466265874710858<jupyter_text>3. (5pts) Does it have the same mean as EEM? Why or why not?<jupyter_code>factor_data['EEM US Equity'].mean() * 12<jupyter_output><empty_output><jupyter_text>- No, becasue there will be tracking error in the replicating model  4. (5pts) Suppose we estimated a multifactor regression where in addition to SPY, we had IWM as a regressor. Why might this regression be difficult to use for attribution or even hedging?<jupyter_code>X = factor_data[['SPY US Equity', 'IWM US Equity']]
y = factor_data['EEM US Equity']
static_model_noint = sm.OLS(y,X).fit()
static_model_noint.summary()
factor_data.corr()<jupyter_output><empty_output><jupyter_text>- Becasue the corelation between the two regressor are high, which is not good for the model  4 Modeling Risk (20pts)<jupyter_code>factor_data = pd.read_excel('proshares_analysis_data.xlsx', sheet_name = 'merrill_factors', engine='openpyxl')
factor_data = factor_data.set_index('date')
factor_data.head()<jupyter_output><empty_output><jupyter_text>1. (10pts) SPY and EFA are highly correlated, yet SPY has had a much higher return. How confident are we that SPY will overperform EFA over the next 10 years?<jupyter_code>def calculate_volatality_expanding(return_series, rolling = 60):
    vol_list = list()
    for i in range(0,len(return_series)):
        if i < 61:
            vol_list.append(np.nan)
        else:
            vol_list.append((((return_series.iloc[0:i] ** 2).sum())/(i-1)) ** 0.5)
    return vol_list
    
def calculate_volatality_rolling(return_series, rolling = 60):
    vol_list = list()
    for i in range(0,len(return_series)):
        if i < 61:
            vol_list.append(np.nan)
        else:
            vol_list.append((((return_series.iloc[i-61:i] ** 2).sum())/60) ** 0.5)
    return vol_list  

def calculate_volatality_EWMA(return_series, theta = 0.97, initial_vol = 0.15):
    vol_list = list()
    for i in range(len(return_series)):
        if i == 0:
            vol_list.append(initial_vol)
        else:
            vol_list.append(((theta  *  (vol_list[i-1] ** 2)) + (1 - theta) * (return_series[i-1] ** 2)) ** 0.5)
    return vol_list

def calculate_volatality_GARCH(return_series, initial_vol = 0.15):
    vol_list = list()
    am = arch_model(return_series, vol = 'GARCH', p = 1, q = 1, rescale = False)
    GARCH_model = am.fit()
    for i in range(len(return_series)):
        if i == 0:
            vol_list.append(initial_vol)
        else:
            vol_list.append(np.sqrt(GARCH_model.params['omega'] + GARCH_model.params['beta[1]'] * np.square(vol_list[i-1]) +
                           GARCH_model.params['alpha[1]'] * np.square(return_series[i-1])))
    
    return vol_list
# Logs
df_subset = np.log(factor_data + 1)
data = pd.DataFrame()
data['Expanding_Vol'] = calculate_volatality_expanding(df_subset['SPY US Equity'], rolling = 60)
data['Rolling_Vol'] = calculate_volatality_rolling(df_subset['SPY US Equity'], rolling = 60)
data['EWMA_Vol'] = calculate_volatality_EWMA(df_subset['SPY US Equity'])
#data['GARCH_Vol'] = calculate_volatality_GARCH(data.Excess_SPX)

data[['Expanding_Vol', 'Rolling_Vol', 'EWMA_Vol']].plot(figsize = (8,8))
import scipy.stats
table5 = pd.DataFrame(columns=['h', 'tilde_mu_hat']) 
table5['h'] = [5, 10, 15, 20, 25, 30]
table5 = table5.set_index('h')

def p(h, tilde_mu=0.525, tilde_sigma=0.150):
    x = - np.sqrt(h) * tilde_mu / tilde_sigma
    val = scipy.stats.norm.cdf(x)
    return val

tilde_mu = df_subset['SPY US Equity'].mean()
tilde_sigma = df_subset['SPY US Equity'].std()

table5['tilde_mu_hat'] = p(table5.index, tilde_mu=tilde_mu, tilde_sigma=tilde_sigma)
table5.T.style.set_caption('Solution Table 5: Shortfall probability estimates using 1965-2020 data.')
print('p(30) = {:.4f}'.format(table5.loc[10, 'tilde_mu_hat']))<jupyter_output>p(30) = 0.1761<jupyter_text>2. (10pts) Calculate the 60-month rolling volatility of EFA.<jupyter_code>a = df_subset['EFA US Equity'].rolling(60).std()
a
def p(h, tilde_mu, tilde_sigma):
    x = - np.sqrt(h) * tilde_mu / tilde_sigma
    val = scipy.stats.norm.cdf(x)
    return val
std = a[-1]
VaR = (mean - 2.33*std)*np.sqrt(1/12)
VaR<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11470760805789165, 'fraction_numerical': 0.030510463524349697, 'mean_word_length': 4.729411764705882, 'pattern_counts': {'":': 0, '<': 54, '<?xml version=': 0, '>': 52, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '13', 'dir': 'jupyter-structured-clean-dedup', 'id': '163558', 'license': 'MIT', 'n_tokens_mistral': 3851, 'n_tokens_neox': 3532, 'n_words': 908, 'path': 'solutions/mid1/submissions/zouharry_170454_6241713_Midterm_Haoxiang_Zou.ipynb', 'repo_name': 'tulyu96/finm-portfolio-2021', 'size': '10234'}