# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

# # Midterm #1-A
#

# +

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")
# -

# ## 1

#

# #### 1. False - The MV optimisation, in practise goes long the highest sharpe ratio and shorts the next highest one, MV optimisation is all about reducing the covariance term so it will actually go long one of the securities that has low correlation with other securities.

# #### 2. False - The LETF has to price the instrument daily so it loses the cross product of returns everyday as compared to a regular levered instrument, e.g a levered etf on S&P will always have a lower return than say a 3x leveraged S&P portfolio. Apart from this even the proces of daily leveraging is difficult and incurs significant trading costs.

# #### 3. Bitcoin and other cryptos are notorious for having very high mean returns and very high volatilites. Even if we had a few years of data an intercept might be required to get match the returns of bitcoin using a traditional portfolio. Hence we will include an intercept.

# #### 4. HDG had a very high correlation with HFRI in sample and a decently high correlation OOS too. HDG was meant to track the MErill indices which tracked the HFRI but since HFRI is an index and HDG is a tradeable ETF, it will never be able to exactly match it, there will always be a tracking error. Moreover the sharpe ratio for HDG was also lower.

# #### 5. A hedge fund alpha is defined as the excess returns that it will get above the market, this can be due to it taking on more diverse assets and risky positions. The Merril Lynch factors have exposure to assets liek ccys, commodites etc. The hedge fund manager might have also invested in these asset classes, leading to the alpha changing from a large positive number to a negative number as the reuturns that alpha initially captured have now been included in the model.
#     

# ## 2 Allocation

# #### 1. weights

df2 = pd.read_excel(r'proshares_analysis_data.xlsx',sheet_name = 'merrill_factors')

df2.set_index(keys = 'date',inplace = True)

# +
df2ex = df2.sub(df2['USGG3M Index'], axis=0)

df2ex.drop(columns = 'USGG3M Index', inplace = True)

# +
## as the data is monthly, peridos = 12
global one_arr 
one_arr = np.ones(len(df2ex.columns),) 
periods = 12
def tangency_portfolio(df,periods):
    """ the function takes the returns and makes them annualized then calculates the correct weights
    for a tangency portfolio according to mean variance optimization.
    After getting the weights, it calculates the mean, volatility and sharpe ratio for a tangency portfolio"""
    df = df*periods
    mu_tilde = df.mean()
    inverse_of_cov = np.linalg.inv(df.cov())  #inverting the covariance matrix
    omega_tangent = inverse_of_cov@mu_tilde/(one_arr.T@inverse_of_cov@mu_tilde) # calculating tangency weights
    
    ## Calculating the weights, mean, vol and sharpe ratio for a MV frontier portfolio
    mean_tan  = omega_tangent@mu_tilde
    vol_tan = (mean_tan/ np.sqrt(mu_tilde.T@inverse_of_cov@mu_tilde))/np.sqrt(periods)
    sharpe_tan = mean_tan/vol_tan
    omega_tangent = pd.Series(omega_tangent, index=mu_tilde.index)
    
    return omega_tangent, mean_tan,vol_tan,sharpe_tan

omega_tangency, mean_tan,vol_tan,sharpe_tan = tangency_portfolio(df2ex,periods)




# -

# #### 1, Tangency weights

## tangency weights 
omega_tangency

# +
target_mean = 0.02*periods

def target_portfolio(df,target_mean,periods):
    """ the function takes the returns and makes them annualized then calculates the correct weights
    for a target portfolio according to mean variance optimization, as per the target mean.
    After getting the weights, it calculates the mean, volatility and sharpe ratio for a target portfolio"""
    df = df*periods
    mu_tilde = df.mean()
    inverse_of_cov = np.linalg.inv(df.cov()) #inverting the covariance matrix
    omega_tangent = inverse_of_cov@mu_tilde/(one_arr.T@inverse_of_cov@mu_tilde)  # calculating tangency weights
    delta = ((one_arr.T @ inverse_of_cov @ mu_tilde)/(mu_tilde.T@inverse_of_cov@mu_tilde))*target_mean #calculating the scaling delta
    omega_target = delta*omega_tangent  # calculating target weights
    
    ## Calculating the weights, mean, vol and sharpe ratio for a MV frontier portfolio
    mean_target  = omega_target@mu_tilde
    vol_target = (mean_target/ np.sqrt(mu_tilde.T@inverse_of_cov@mu_tilde))/np.sqrt(periods)
    sharpe_target = mean_target/vol_target
    omega_target = pd.Series(omega_target, index=mu_tilde.index)
    
    return omega_target, mean_target,vol_target,sharpe_target

omega_target, mean_target,vol_target,sharpe_target = target_portfolio(df2ex,target_mean,periods)
# -

# #### 2. target portfolio, weights

omega_target

sum(omega_target)

wt_rfr = 1- sum(omega_target)
wt_rfr

# ####  As the sum of weights is >1 the portfolio indeed is borrowing the risk free rate with the weight of -0.158

# #### 3. I am assuming the optimized portfolio here means the one with target mean of 0.2 per month. Even in such a case only mean and sd will be different, sharpe still remains the same across all MV optimized portfolios
#

mean_target

vol_target

sharpe_target

# #### 4. OOS performance

df2ex.head()

df2ex_is = df2ex['2011':'2018']

omega_target_is, mean_target_is,vol_target_is,sharpe_target_is = target_portfolio(df2ex_is,target_mean,periods)

# +
years = [2019,2020,2021]
def oos_performance(df_excess,years,periods,weights_target_in_sample):
    oos_data = df_excess[df_excess.index.year.isin(years)]*periods  # isolating the oos data
    cov_mat = oos_data.cov()         #covariance matrix
    mu_tilde_oos = oos_data.mean()
    
    # calculating the mean, vol and sharpe for oos data using in sample weights
    mean_oos = weights_target_in_sample@mu_tilde_oos
    vol_oos = np.sqrt(weights_target_in_sample.T@cov_mat@weights_target_in_sample)/np.sqrt(periods)
    sharpe_oos = mean_oos/vol_oos
    return mean_oos,vol_oos,sharpe_oos

mean_oos,vol_oos,sharpe_oos = oos_performance(df2ex,years,periods,omega_target_is)
# -

mean_oos

vol_oos

sharpe_oos

# #### 5. It probably would have been better if the commodity futures arent as risky or correlated as the equities as, since the correlation would have been low, the oos sample performance might have been more stable. It depends on the correlation matrix of the chosen assets

# ## 3 Hedging and Replication

df3 = df2ex

sns.heatmap(df3.corr())

df3.columns

Y = df3['EEM US Equity']
X = df3['SPY US Equity']

# +

static_model = sm.OLS(Y,X).fit()
# -

static_model.params

static_model.summary()

# #### 1. The optimal hedge ratio is given by B in a one factor regression, therefore the hedge ratio is 0.92566, i.e. we will invest 0.92566 dollars in S&P for every dollar in EEM

# #### 2. Our hedged position would simply be returns of EEM - B* returns of SPY

hp = pd.DataFrame(index = df3.index)

hp.head()

df3.columns

for i in range(len(df3.index)):
    hp.iloc[i,0] = df3.iloc[i,1] - static_model.params*df3.iloc[i,0]

hp.head()

mean = hp.mean()
std = hp.std()/np.sqrt(periods)
sharpe = mean/std

mean

std

sharpe

df3.mean()*periods

# #### 3. The value of the two means is not equal (-0.007792 vs 0.037784) as SPY and EEM have very high correlation so if we take out the B due to SPY there is very less excess return, infact the very low value of returns shows that the hedge is working very well. The high multicollinearity probably won't affect the hedging as beta is simply the ratio of vol x correlation of the two

# #### 4. IWM also has a very high correlation with SPY and EEM as shown in the correlation heatmap above. This means that there will be a lot of multicollinearity, making it almost impossible to trust the betas outputted by the model, so attribution is out of the picture. For hedging again it might not be very useful out of sample due to the high multicollinearity, it might be difficult to get the beta for both the factors, making it difficult to judge how much money needs to be put into each to accurately hedge the portfolio

# ## 4 Modeling Risk

df4 = df2

df4.columns

# +
# we need a distribution of returns that captures returns of SPY - returns of EFA
# and check if it is greater than zero

df4 = df2.sub(df2['EFA US Equity'], axis=0)

series = df4['SPY US Equity']
# -

mu = series.mean()*periods

mu

std = series.std()*np.sqrt(12)

std

h = 10 

np.sqrt(10)*mu/std

import scipy.stats
def p(h, tilde_mu, tilde_sigma):
    x = - np.sqrt(h) * tilde_mu / tilde_sigma
    val = scipy.stats.norm.cdf(x)
    return val
probability = p(h,mu,std)

probability

# #### The probability of SPY outperforming EFA is very high, there is just a 0.04% chance of it not happening over the next 10 years

# +
#std of september 2021
# -

std = ((df2['EFA US Equity']**2).shift(1).rolling(60).mean()[-1])**0.5

# +
## Z-score for 99% is 2.576
# -

Var = -2.576*std

Var

# #### The 99% one month VaR estimate is -0.108



StarcoderdataJupyterScriptsDedupFiltered