Exam Economics of Financial Markets

Beniamino Sartini, Mauro Martis, Orazio Terranova

18/6/2021

Question 1

The main purpose of this question is to perform an asset allocation considering a group of assets and to find an optimal portfolio for our target investor. In order to select the best combination of those assets we need to perform some descriptive analysis on our dataset. We are considering a group of 80 securities considering both daily and monthly data, the first thing that we done is to transform the prices computing the log-return.

\[r_{t}= log \biggl( \frac{P_t}{P_{t-1}} \biggl)\]

One of the strongest assumption in finance is the (log)-Normally distribution of stock returns, in practice this assumption strongly depend on the frequency of our data: maybe with monthly or yearly data this assumption is satisfied, but in most cases for daily data is violated. If we deal with models that works under the normality assumption of returns we need to check if it is empirically verified. In order to look at the characteristics of those assets, we perform the computation of some statistics on the returns: mean, variance, standard deviation, skewness and kurtosis. We need to focus our attention on the kurtosis index since it represents the deviation from the normal distribution. In this case we can note that considering daily return most of assets deviate from the normal distribution, while for monthly frequency more assets have a kurtosis index near 3.

Asset Selection

In order to perform an optimal asset allocation, we need to understand the type of investor we are dealing with, we can consider for example two kind of porfolios:

  1. Portfolio A is a portfolio with a low or negative correlation between assets.
  2. Portfolio B is a portfolio with a correlation lower than a certain level to ensure a good diversification but we search assets with mainly a positive mean return.

In this asset allocation exercise we will try to build the first type of portfolio.

Low Risk Portfolio

In order to construct a well-diversified portfolio we need to look at the variance/covariance and at the correlation matrix between the assets. Our criterium of selection is based on the structure of the correlation matrix, ideally, we would like to find uncorrelated assets or negatively correlated in order to diversified of our portfolio. In practice, is very difficult to perform this operation and in order to reach the result we decide to select assets with correlation in an interval (-30% and +30%). The procedure through which we select the assets require a trade-off between the number of assets that we want and their characteristics. In order to reach our goal we filter the variance covariance matrix till we reach a minimum number of assets that in our case is between 10 and 12. Since this kind of selection will produce a different portfolio performing this operation on monthly and daily data, we select the asset considering the correlation of monthly return and then we replicate this selection with daily data, doing this we can easily look at the difference obtained using different frequencies. As we can see, with daily frequency the kurtosis is very far from 3 that represent the normal distribution, while some of those assets considered in a monthly frequency gave a value for the kurtosis near 3, as for example, ALERION CLEAN POWER and RIZZOLI CRER.DLSM.GP. BORGOSESIA RSP present a kurtosis of 144 in daily while only 13 in monthly suggesting that the assumption of normality using daily data is for sure violated. The correlation between the asset selected is low but some of the returns (both daily and monthly) are negative, it implies that an optimal allocation for those assets will be to sell them instead of buy.

# importing stock daily data
stock.daily = read_excel("dataset.xlsx", sheet = "stocks daily", na = "NA")

# trasform in date
stock.daily$Date = as.Date(stock.daily$Date)

# compute log return
stock.day.ret = tibbleReturn(stock.daily, type = "log", order.by = "Date")[-1, ]

# importing stock monthly data
stock.monthly = read_excel("dataset.xlsx", sheet = "stocks monthly", na = "NA")

# trasform in date
stock.monthly$Date = as.Date(stock.monthly$Date)

# compute log return
stock.mon.ret = tibbleReturn(stock.monthly, type = "log", order.by = "Date")[-1, 
    ]

# Descriptive Statistics for all the assets tibbleSummary(stock.day.ret)
# tibbleSummary(stock.day.ret)

# computing covariance matrix
cov.day.matrix = cov(stock.day.ret[, -1])
cov.mon.matrix = cov(stock.mon.ret[, -1])

# computing correlation matrix
cor.day.matrix = cor(stock.day.ret[, -1])
cor.mon.matrix = cor(stock.mon.ret[, -1])

# Portfolio A - low correlation/nagative correlation (daily data) selection of
# low/nagative correlated assets
for (i in 1:nrow(cor.mon.matrix)) {
    # we proceed filtering the matrix till the number of asset is less or equal than
    # 14(to get 12)
    if (i == 1) {
        df = cor.mon.matrix[, cor.mon.matrix[i, ] < 0.3 & cor.mon.matrix[i, ] > -0.3]
    } else {
        df = df[, df[i, ] < 0.3 & df[i, ] > -0.3]
    }
    
    if (ncol(df) <= 10) {
        # use the name of assets in order to filter the datasets
        asset.day.A = stock.day.ret[, c(1, which(colnames(stock.day.ret) %in% colnames(df)))]
        portfolio.day.A = asset.day.A[, -1]
        asset.mon.A = stock.mon.ret[, c(1, which(colnames(stock.mon.ret) %in% colnames(df)))]
        portfolio.mon.A = asset.mon.A[, -1]
        break
    }
    
}

# table 1
tibbleSummary(portfolio.day.A) %>% knitr::kable(caption = "Descriptive Statistics Portfolio (Daily Data)") %>% 
    kableExtra::kable_classic() %>% row_spec(c(1:ncol(portfolio.day.A)), font_size = 8)
Descriptive Statistics Portfolio (Daily Data)
stock mean variance sd skewness kurtosis
ALERION CLEAN POWER 0.0009577 0.0005801 0.0240862 1.3676073 16.281693
VALSOIA 0.0000265 0.0003470 0.0186278 0.4876336 9.200846
CENTRALE DEL LATTE D’ITALIA 0.0001441 0.0003881 0.0197003 2.5431962 26.859505
BORGOSESIA RSP 0.0004440 0.0008861 0.0297674 -1.1657031 144.810565
FIDIA -0.0000799 0.0009936 0.0315218 2.1171264 21.298090
RIZZOLI CRER.DLSM.GP. -0.0000991 0.0007199 0.0268314 0.5863740 10.940342
FULLSIX -0.0002425 0.0010063 0.0317224 1.6411415 20.084952
GABETTI PROPERTY SLTN. 0.0003165 0.0007298 0.0270148 0.8178657 11.159498
BEGHELLI -0.0000359 0.0005081 0.0225411 1.3660149 13.867254
SOL 0.0005541 0.0003118 0.0176579 0.3785158 4.899819
# table 2
tibbleSummary(portfolio.mon.A) %>% knitr::kable(caption = "Descriptive Statistics Portfolio (Monthly Data)") %>% 
    kableExtra::kable_classic() %>% row_spec(c(1:ncol(portfolio.mon.A)), font_size = 8)
Descriptive Statistics Portfolio (Monthly Data)
stock mean variance sd skewness kurtosis
ALERION CLEAN POWER 0.0195417 0.0120186 0.1096292 1.1347794 3.642986
VALSOIA 0.0002040 0.0067129 0.0819321 0.4827787 4.267812
CENTRALE DEL LATTE D’ITALIA 0.0029173 0.0048445 0.0696027 1.5833764 7.544949
BORGOSESIA RSP 0.0089562 0.0252989 0.1590563 1.4364432 13.071978
FIDIA -0.0015926 0.0293511 0.1713216 2.4839397 14.183709
RIZZOLI CRER.DLSM.GP. -0.0030834 0.0166086 0.1288743 0.4276530 4.171498
FULLSIX -0.0047933 0.0254352 0.1594841 2.9396040 17.392574
GABETTI PROPERTY SLTN. 0.0045895 0.0227603 0.1508653 1.6908051 7.969118
BEGHELLI -0.0010611 0.0074131 0.0860995 1.3541656 6.403879
SOL 0.0116814 0.0033669 0.0580253 0.2003585 2.387596
# Table 3
stocks.names.A = map_chr(colnames(portfolio.day.A), ~stringr::str_split(.x, " ")[[1]][1])
stocks.names.A = cbind(stock = stocks.names.A, as_tibble(cor(portfolio.day.A))) %>% 
    mutate_if(is.numeric, round, 2)

colnames(stocks.names.A) = c("stock", as.character(stocks.names.A$stock))

stocks.names.A %>% knitr::kable(caption = "Correlation Matrix Portfolio (Daily Data)") %>% 
    kableExtra::kable_classic() %>% row_spec(c(0), font_size = 5) %>% row_spec(c(1:ncol(portfolio.day.A)), 
    font_size = 5) %>% row_spec(c(1:ncol(portfolio.day.A)), font_size = 8)
Correlation Matrix Portfolio (Daily Data)
stock ALERION VALSOIA CENTRALE BORGOSESIA FIDIA RIZZOLI FULLSIX GABETTI BEGHELLI SOL
ALERION 1.00 0.15 0.11 0.04 0.09 0.10 0.02 0.13 0.18 0.12
VALSOIA 0.15 1.00 0.21 0.02 0.21 0.20 0.09 0.21 0.17 0.21
CENTRALE 0.11 0.21 1.00 0.01 0.16 0.15 0.10 0.20 0.18 0.11
BORGOSESIA 0.04 0.02 0.01 1.00 -0.01 -0.04 0.03 0.01 0.00 0.06
FIDIA 0.09 0.21 0.16 -0.01 1.00 0.17 0.12 0.18 0.14 0.14
RIZZOLI 0.10 0.20 0.15 -0.04 0.17 1.00 0.07 0.22 0.18 0.15
FULLSIX 0.02 0.09 0.10 0.03 0.12 0.07 1.00 0.11 0.08 0.07
GABETTI 0.13 0.21 0.20 0.01 0.18 0.22 0.11 1.00 0.25 0.15
BEGHELLI 0.18 0.17 0.18 0.00 0.14 0.18 0.08 0.25 1.00 0.17
SOL 0.12 0.21 0.11 0.06 0.14 0.15 0.07 0.15 0.17 1.00
# Table 4
stocks.names.A = map_chr(colnames(portfolio.mon.A), ~stringr::str_split(.x, " ")[[1]][1])
stocks.names.A = cbind(stock = stocks.names.A, as_tibble(cor(portfolio.mon.A))) %>% 
    mutate_if(is.numeric, round, 2)

colnames(stocks.names.A) = c("stock", as.character(stocks.names.A$stock))

stocks.names.A %>% knitr::kable(caption = "Correlation Matrix Portfolio (Monthly Data)") %>% 
    kableExtra::kable_classic() %>% row_spec(c(0), font_size = 5) %>% row_spec(c(1:ncol(portfolio.mon.A)), 
    font_size = 5) %>% row_spec(c(1:ncol(portfolio.mon.A)), font_size = 8)
Correlation Matrix Portfolio (Monthly Data)
stock ALERION VALSOIA CENTRALE BORGOSESIA FIDIA RIZZOLI FULLSIX GABETTI BEGHELLI SOL
ALERION 1.00 0.15 0.23 0.06 0.11 0.06 -0.01 0.10 0.29 0.23
VALSOIA 0.15 1.00 0.30 0.10 0.45 0.12 0.08 0.36 0.23 0.33
CENTRALE 0.23 0.30 1.00 0.03 0.12 0.23 -0.08 0.21 0.27 0.24
BORGOSESIA 0.06 0.10 0.03 1.00 -0.08 -0.02 0.01 -0.01 0.09 0.15
FIDIA 0.11 0.45 0.12 -0.08 1.00 0.17 0.05 0.37 0.14 0.27
RIZZOLI 0.06 0.12 0.23 -0.02 0.17 1.00 0.11 0.22 0.23 0.29
FULLSIX -0.01 0.08 -0.08 0.01 0.05 0.11 1.00 0.16 0.37 0.08
GABETTI 0.10 0.36 0.21 -0.01 0.37 0.22 0.16 1.00 0.21 0.26
BEGHELLI 0.29 0.23 0.27 0.09 0.14 0.23 0.37 0.21 1.00 0.40
SOL 0.23 0.33 0.24 0.15 0.27 0.29 0.08 0.26 0.40 1.00

Plot Returns

figure1 <- asset.mon.A %>% # asset.mon.A[, colnames(stock.monthly) %in% colnames(asset.mon.A)] %>%
gather("Symbol", "Price", -Date) %>% ggplot(aes(Date, Price, group = Symbol, col = Symbol)) + 
    geom_line() + scale_y_continuous(breaks = seq(-2, 2, 0.5)) + facet_wrap(~Symbol, 
    as.table = TRUE, ncol = 3) + labs(title = "Stock Portfolio Returns", subtitle = " Monthly Data", 
    x = "", y = "Monthly Returns (%)", caption = "Figure 1") + ggthemes::theme_solarized() + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 7), axis.text.y = element_text(face = "bold", 
        size = 7), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 15), plot.subtitle = element_text(face = "italic", size = 12), plot.caption = element_text(face = "italic", 
        size = 12), strip.text = element_text(face = "italic", size = 8, color = "red"), 
        strip.background.x = element_rect(colour = "black", fill = "white"), panel.grid.major.x = element_blank(), 
        panel.grid.minor.x = element_blank(), panel.grid.major.y = element_line(colour = "grey60", 
            linetype = "dotted"), legend.position = "none")


figure1

Portfolio Allocation

In order to find the best portfolio allocation, we can deal with some asset allocation procedure: we implement 7 types of asset allocations:

  1. Mean-Variance Portfolio (MV).
  2. Global Minimum Portfolio (GMV).
  3. Efficient Frontier with minimum variance given return.
  4. \(\frac{1}{M}\) allocation used as benchmark, is the simplest possible allocation in which we divide our investment in M-parts all equal.
  5. Bayes Allocation with Informative Prior.
  6. Black-Litterman Allocation (BL).
  7. Mixed Combination of Weights.

Notation

  • \(X_{NxM}\): Matrix of returns.
  • \(M\): number of assets.
  • \(\mu\): Sample Mean estimated on X.
  • \(\mu_p\): Portfolio mean.
  • \(\sigma_p^{2}\): Portfolio variance.
  • \(I = [1, \mu]^T \Sigma^{-1} [1, \mu]\): Information Matrix
  • \(\Delta\): determinant of the information matrix.

To implement these optimization methods we write functions in R and can be found in the file portfolio_functions.R.

Mean Variance Theory

The mean-variance framework was introduced by Markowitz (1952), in particular we consider an investor that choose the portfolio weight in order to maximize the quadratic utility function:

\[U(W) = W^T \mu - \frac{1}{\gamma} W^T \Sigma \, W \]

We know that, when \(\mu\) and \(\Sigma\) are known, the optimal weights are given by:

\[W_{mv} = \frac{1}{\gamma} \Sigma^{-1} \mu \]

From this formula is evident that the result depends on gamma, that represent the coefficient of Risk Aversion. Therefore, we have decided to estimate different set of weights letting gamma varying in a range and in this way we are able to identify different types of allocations reflecting the risk aversion of our potential investor. We colored the portfolios considering the sum of their weights:

  • If this sum is greater than one it means that we are allowing leverage, since the investment require more than our wealth we are accepting an higher risk, so in this case we are less risk adverse.

  • If the sum is less than one, we are not investing all our money but just a little, in this case we are dealing with a risk adverse individual and so the remuneration required is not sufficient to let him to invest all his wealth. In our Mean-Variance allocation we have decided to search the value of gamma for which the sum of the weights is almost one.

# mean variance frontiere with monthly data
figure2 <- plot.frontiere(portfolio.mon.A, method = "mv", min.gamma = 1, max.gamma = 10)

figure2 <- figure2 + labs(subtitle = "Portfolio Stocks (Monthly Data)", caption = "Figure 2") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.text = element_text(face = "italic", size = 8), legend.title = element_text(face = "bold", 
            size = 8), legend.background = element_rect(fill = "white", color = "black"), 
        legend.position = "top")

# mean variance frontiere with daily data
figure3 <- plot.frontiere(portfolio.day.A, method = "mv", min.gamma = 1, max.gamma = 10)

figure3 <- figure3 + labs(subtitle = "Portfolio Stocks (Daily Data)", caption = "Figure 3") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        legend.text = element_text(face = "italic", size = 8), legend.title = element_text(face = "bold", 
            size = 8), legend.background = element_rect(fill = "white", color = "black"), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "top")

figure2 + geom_point(data = optim.mv(portfolio.mon.A, gamma = 3.2)$mv$stat, aes(var * 
    100, mu * 100, label = "gamma 3.2"), size = 4, col = "orange", alpha = 0.5)

figure3 + geom_point(data = optim.mv(portfolio.day.A, gamma = 3.2)$mv$stat, aes(var * 
    100, mu * 100, label = "gamma 3.2"), size = 4, col = "orange", alpha = 0.7)

Comments: choose of Gamma

As we can see from the graph, now we are able to detect the level risk adversion required in order to have that the sum of the weights is near one.

  • We fix the value \(\gamma = 3.2\), and we represent it with the orange point, in this way we can easily compare the behaviour of our potential investor changing the allocation’s methods.

  • Our goal is now to compare the results obtained with this approach with the probabilistic methods such as Bayes and Black-Litterman.

A possible strategy to get the sum of the weights equal to one is to divide each weight by the sum of the weights itself, however in this way the role of gamma become useless, since we obtain the same weights for every level of gamma. In order to avoid this problem our approach is to detect the optimal gamma that guarantee the sum of weights to be one given the portfolio that we are building.

The role of gamma is important since, increasing gamma the individual become more risk adverse and its demand of risky asset will decrease, above the level of gamma 3.75 (MV Monthly) the Mean-Variance’s investor will invest less than its total wealth.

Non-Negative Weights

In order to impose the non-negativity of the weights we use PortfolioAnalytics, an R package ( source link ) build for asset allocation, in order to have a mean-variance allocation with all positive weights we impose 3 contraints:

  1. We invest all our wealth
  2. Each weight must be greater or equal than zero and less than one.
  3. The sum of weights is 1.

We can notice that a certain amount of assets are characterized by a weight equal to zero, this could happen whenever the mean return of the security is negative. That’s an importand difference with respect to the classical approach where short selling is allowed and in which we can accept also negative weights as the optimal ones. As we can see from the graph if we consider different frequencies we will have a huge difference in allocation, some weights that are positive with daily returns will became zero considering monthly return. Moreover the weights associated to SOL and ALERION CLEAN POWERS become greater considering monthly returns. This difference in weights can be explained if we watch at the monthly returns associated with those assets that are very high with respect to the others. If we considere the mean return of the same assets in daily frequencies we can see that they are not very different from the others.

# portfolio specification with PortfolioAnalytics 

stocks.A.mon = asset.mon.A %>%
  select(date = "Date", everything()) %>%
  timetk::tk_xts(silent = TRUE)

stocks.A.day = asset.day.A %>%
  select(date = "Date", everything()) %>%
  timetk::tk_xts(silent = TRUE)

# specification of a portfolio of assets
pspec.A.mon <- portfolio.spec(assets = colnames(stocks.A.mon))
pspec.A.day <- portfolio.spec(assets = colnames(stocks.A.day))

# sum of weights: if full investment weight sum to 1
pspec.A.mon <- add.constraint(portfolio=pspec.A.mon, type="full_investment")
pspec.A.day <- add.constraint(portfolio=pspec.A.day, type="full_investment")

pspec.A.mon <- add.constraint(portfolio=pspec.A.mon,type="weight_sum", min_sum=0.99, max_sum=1.01)
pspec.A.day <- add.constraint(portfolio=pspec.A.day,type="weight_sum", min_sum=0.99, max_sum=1.01)

# constraint to have positive weights: 
pspec.A.mon <- add.constraint(portfolio=pspec.A.mon, type="long_only")
pspec.A.day <- add.constraint(portfolio=pspec.A.day, type="long_only")


# contraint to set a minimum and a maximum for each asset 
pspec.A.mon <- add.constraint(portfolio=pspec.A.mon, type="box", min = 0.0, max = 0.3)
pspec.A.day <- add.constraint(portfolio=pspec.A.day, type="box", min = 0.0, max = 0.3)


# mean objective 
pspec.A.mon <- add.objective(portfolio=pspec.A.mon, 
                       type="return",
                       name="mean") # mean

pspec.A.day <- add.objective(portfolio=pspec.A.day, 
                       type="return",
                       name="mean") # mean

# variance objective 
pspec.A.mon <- add.objective(portfolio=pspec.A.mon, 
                       type="risk",
                       name="var") # uses sd

pspec.A.day <- add.objective(portfolio=pspec.A.day, 
                       type="risk",
                       name="var") # uses sd


# create the efficient frontiere 
# meansd.ef <- create.EfficientFrontier( R = stocks, portfolio = pspec, type = "mean-var",  n.portfolios = 50)
# chart.EfficientFrontier(meansd.ef,  match.col="StdDev", type="l", rf = 0.0005/12, RAR.text="Sharpe Ratio",tangent.line = FALSE, chart.assets=TRUE, labels.assets=TRUE)

set.seed(1)
nnwgt.optim.A.mon = optimize.portfolio(stocks.A.mon, pspec.A.mon, optimize_method = "random",  search_size = 5000)
nnwgt.optim.A.day = optimize.portfolio(stocks.A.day, pspec.A.day, optimize_method = "random",  search_size = 5000)

nnwgt.portfolio.A.mon = as.matrix(asset.mon.A[,-1]) %*% matrix(c(nnwgt.optim.A.mon$weights), ncol = 1)
nnwgt.portfolio.A.day = as.matrix(asset.day.A[,-1]) %*% matrix(c(nnwgt.optim.A.day$weights), ncol = 1)

# non negative weights portfolio A
nnwgt.A.mon = list(
    stat = tibble(method = "MV NonNeg", 
                  mu = mean(nnwgt.portfolio.A.mon),
                  var = var(nnwgt.portfolio.A.mon)[[1]],
                  sd = sd(nnwgt.portfolio.A.mon),
                  skewness = skewness(nnwgt.portfolio.A.mon),
                  kurtosis = kurtosis(nnwgt.portfolio.A.mon), 
                  gamma = 3.2, 
                  wgt = sum(nnwgt.optim.A.mon$weights)),
    wgt = matrix(c(nnwgt.optim.A.mon$weights), ncol = 1),
    portfolio = nnwgt.portfolio.A.mon 
    )

nnwgt.A.day = list(
    stat = tibble(method = "MV NonNeg", 
                  mu = mean(nnwgt.portfolio.A.day),
                  var = var(nnwgt.portfolio.A.day)[[1]],
                  sd = sd(nnwgt.portfolio.A.day),
                  skewness = skewness(nnwgt.portfolio.A.day),
                  kurtosis = kurtosis(nnwgt.portfolio.A.day), 
                  gamma = 3.2, 
                  wgt = sum(nnwgt.optim.A.day$weights)),
    wgt = matrix(c(nnwgt.optim.A.day$weights), ncol = 1),
    portfolio = nnwgt.portfolio.A.day 
    )

figure4 <- bind_rows(
tibble(
  frequency = "MV-NN Daily",
  stock = colnames(asset.day.A)[-1], 
       wgt = nnwgt.A.day$wgt),
tibble(
  frequency = "MV-NN Monthly",
  stock = colnames(asset.mon.A)[-1], 
       wgt = nnwgt.A.mon$wgt), 
tibble(
  frequency = "MV Monthly",
  stock = colnames(asset.mon.A)[-1], 
       wgt = optim.mv(asset.mon.A[-1], gamma = 3.2, normalize = TRUE)$mv$wgt), 
tibble(
  frequency = "MV Daily",
  stock = colnames(asset.day.A)[-1], 
       wgt = optim.mv(asset.day.A[-1], gamma = 3.2, normalize = TRUE)$mv$wgt) 

) %>%
  ggplot(aes(reorder(stock, wgt), wgt, fill = frequency))+
  geom_bar(stat = "identity", position = "dodge") +
  coord_flip()+ 
  labs(title = "Comparison Portfolio Weights", subtitle = "Stock Portfolio",  x = "", y = "Weight", caption = "Figure 4") +
  theme_solarized()+
   theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), 
        axis.text.y = element_text(face = "bold", size = 6), 
        axis.title  = element_text(face = "bold", size = 8),
        axis.title.y  = element_text(face = "bold", size = 6),
        plot.title  = element_text(face = "bold", size = 12),
        plot.subtitle = element_text(face = "italic", size = 10),
        plot.caption = element_text(face = "italic", size = 10),
        panel.grid.major.x = element_blank(),
        panel.grid.minor.x = element_blank(),
        panel.grid.major.y = element_line(colour="grey60", linetype="dotted"),
        legend.text = element_text(face = "italic", size = 7),
        legend.title = element_text(face = "bold", size = 7),
        legend.background = element_rect(fill = "white", color = "black"),
        legend.position = "top" )

figure4

Efficient Frontiere

Another approach is based on the minimization of the portfolio variance given the mean. This method can be used to get the Efficient Frontiere: if we let the target mean variying in an interval we get a set of optimal portfolios, among them there is one called the Minimum Variance Portfolio that has the minimum variance among all possible portfolios. The optimization procedure consist is the minimization of the portfolio variance fixing a portfolio mean and setting two constraints:

  • Portfolio Variance: \(\sigma_p^{2} = W_{op} \, \Sigma \, W_{op}^T\)
  • Constraint on Portfolio Mean: \(\mu_p = W_{op}^T \, \mu\)
  • Feasibility constraint: \(W_{op}^T \, 1 = 1\)

Set the Lagrangian:

\[L(x,\lambda) = \frac{1}{2} W_{op} \, \Sigma \, W_{op}^T - \lambda_1(W_{op}^T \, \mu - \mu_p) - \lambda_2(1^T W_{op} - 1)\]

Solving for \(W_{op}\) gives the optimal weights that minimize the variance given the mean:

\[W_{op} = \Sigma^{-1} \, [1, \mu] \, I^{-1} \, \begin{bmatrix} \mu_p \\ 1 \end{bmatrix}\]

For semplicity we can explicit the elements in the information matrix and we denote it in this way:

\[I = \begin{bmatrix} \mu^T \Sigma \mu & \mu^T\Sigma^{-1}1 \\ 1^T \Sigma^{-1} \mu & 1^T \Sigma^{-1}1 \end{bmatrix} = \begin{bmatrix} A & B \\ B & C \end{bmatrix}\]

If we explicit the values in the information matrix in the equation for the optimal weights we get an operative formula:

\[W_{op} = \frac{1}{\Delta} \Sigma^{-1} (C \mu - b) \mu_p + \frac{\Sigma^{-1}1}{\Delta} (A - B \mu)\]

The portfolio Variance is given by:

\[\sigma_p^2 = [\mu_p, 1] \, I^{-1} \, \begin{bmatrix} \mu_p \\ 1 \end{bmatrix} =\frac{1}{\Delta} (A - 2B \, \mu_p + C \mu_p^2)\]

It is possible, given a matrix of Data \(X\) to construct a portfolio that has the minimum variance among all possible portfolios with this assets. The minimum variance is given by:

\[\sigma_{gmv}^2 = \frac{1}{C}\]

This variance obtained implied a mean associated with \(\sigma_{gmv}^2\) that is defined as:

\[\mu_{gmv}= \frac{B}{C}\]

The weights of the global minimum variance portfolio are given by:

\[W_{gmv} = \frac{\Sigma^{-1}1}{1^T \Sigma^{-1}1} = \frac{\Sigma^{-1}1}{C}\]

figure5 <- efficient.frontiere(portfolio.mon.A, max.mup = 0.03, plot = TRUE)

figure5 <- figure5 + labs(subtitle = "Stock Porfolio (Monthly Data)", caption = "Figure 5") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "top")

figure6 <- efficient.frontiere(portfolio.day.A, min.mup = 0, max.mup = 0.003, threshold = 1e-05)

figure6 <- figure6 + labs(subtitle = "Stock Porfolio (Daily Data)", caption = "Figure 6") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "top")

figure5

figure6

Security Market Line

We consider the values of the index FTSE Italian and we set this index equal to our market. Considering the values of the returns for daily and monthly frequencies we can compute the betas for all the securities choosen and for our portfolio as well. The portfolio considered is the Mean-Variance portfolio with \(\gamma = 3.2\). The generic beta is computed as the covariace between the asset and the marked divided by the variance of the market itself.

\[\beta_i = \frac{Cov(R_i,\ R_m)}{V(R_m)}\]

Given an annual risk-free rate of 0.05% we recover the monthly and daily risk free rate dividing it respectively by 252 (convention on trading days) and 12. The returns of all the assets on the Security Market Line are computed using the Capital Market Line equation:

\[R_i= R_f +\ \beta_i \ (R_m-\ R_f)\]

The \(\beta\) represent the marginal contribution of maket risk to the i-th risky asset.

In this way we are able to compute the theoretical returns based on CAPM assumption, in order to understand how far the return of those assets are from this theoretical value we can plot also the empirical returns, we define the difference between the theoretical value and the empirical one as \(\alpha\). As \(\alpha\) deviate from zero we get that the SML is not verified. The SML divide the graph in two parts, one above and one below and we can interpret this result as:

  • The assets above the SML are underpriced, since they are assets that in CAPM model gave a return higher than expected, therefore we would BUY those assets.

  • The assets on the SML are correctly priced, since they are assets that in CAPM model gave a return equal than expected.

  • The assets below the SML are overpriced, since they are assets that in CAPM model gave a return lower than expected, therefore we would SELL those assets.

Globally the assets’s Betas are less than one, and it means that we are dealing with stocks less risky than the market. The Beta of our porfolio is very low, moreover the empiriccal return of the portfolio is above the line: it means that it gives a return higher than the theoretical value obtained with CAPM equation. Moreover we can compute the Sharpe Ratio, that is the slope of the CML, which measures for each unit of risk the return of our portfolio p, it is computed as:

\[SR = \frac{R_p - R_f}{\sigma_p}\]

As we can see the return of the FTSE Italia All Market is about 0.04% considering daily data and 0.59% considering monthly data. In the construction of our portfolio our goal would be to get a return higher than the market with a lower variance, if we are not able to reach this result it would be better to invest directly in all the market using an Index Fund.

# importing data
ftse.daily = read_excel("dataset.xlsx", sheet = "ftse italia all share daily", na = "NA")
ftse.daily$Date = as.Date(ftse.daily$Date)

ftse.monthly = read_excel("dataset.xlsx", sheet = "ftse italia all share monthly", 
    na = "NA")
ftse.monthly$Date = as.Date(ftse.monthly$Date)

# computing returns
ftse.day.ret = tibbleReturn(ftse.daily, order.by = "Date")[-1, ]
ftse.mon.ret = tibbleReturn(ftse.monthly, order.by = "Date")[-1, ]

# table 5
bind_rows(mutate(tibbleSummary(ftse.day.ret), freq = "Daily"), mutate(tibbleSummary(ftse.mon.ret), 
    freq = "Monthly")) %>% mutate(mean = mean * 100, variance = variance * 100, sd = sd * 
    100) %>% knitr::kable(caption = "Descriptive Statistics FTSE Italia") %>% kableExtra::kable_classic() %>% 
    kable_styling() %>% row_spec(c(1, 2), font_size = 7)
Descriptive Statistics FTSE Italia
stock mean variance sd skewness kurtosis freq
FTSE ITALIA ALL SHARE - TOT RETURN IND 0.0423509 0.0197541 1.405494 -1.5408198 20.750639 Daily
FTSE ITALIA ALL SHARE - PRICE INDEX 0.5991672 0.3564045 5.969962 -0.5443885 6.221363 Monthly
# Betas portfolio A
day.market = ftse.day.ret$`FTSE ITALIA ALL SHARE - TOT RETURN IND`
mon.market = ftse.mon.ret$`FTSE ITALIA ALL SHARE - PRICE INDEX`

beta.mon.A = MarketBeta(portfolio.mon.A, market = mon.market) %>% gather("Symbol", 
    "MonthlyBeta")
beta.day.A = MarketBeta(portfolio.day.A, market = day.market) %>% gather("Symbol", 
    "DailyBeta")

beta.A = inner_join(beta.mon.A, beta.day.A, by = "Symbol")


# annual risk-free rate
Rf = 0.05

# market return
Rm.day = mean(day.market) * 100
Rm.mon = mean(mon.market) * 100

# market risk premium
MRP.day = Rm.day - Rf/252
MRP.mon = Rm.mon - Rf/12

# security market line
SML.day = tibble(beta = seq(0, 2, 0.01), mu = Rf/360 + MRP.day * beta)
SML.mon = tibble(beta = seq(0, 2, 0.01), mu = Rf/12 + MRP.mon * beta)

# Portfolio A and SML
beta.day.A = tibble(stock = beta.A$Symbol, beta = beta.A$DailyBeta, capm = Rf/252 + 
    MRP.day * beta, mu = unlist(apply(portfolio.day.A, 2, mean)) * 100)

# adding portfolio Beta
beta.day.A = bind_rows(beta.day.A, tibble(stock = "Stock Portfolio", beta = (cov(optim.mv(portfolio.day.A, 
    gamma = 3.2)$mv$portfolio, day.market)/var(day.market))[[1]], capm = Rf/252 + 
    MRP.day * beta, mu = optim.mv(portfolio.day.A, gamma = 3.2)$mv$stat$mu * 100))
# computing alphas
beta.day.A$alpha = beta.day.A$mu - beta.day.A$capm

# daily data
figure7 <- ggplot() + geom_line(data = SML.day, aes(beta, mu, group = 1)) + geom_point(data = beta.day.A, 
    aes(beta, capm, col = stock)) + geom_point(data = beta.day.A, aes(beta, mu, col = stock), 
    alpha = 0.6) + geom_segment(data = beta.day.A, aes(x = beta, y = capm, yend = mu, 
    xend = beta), colour = "grey50") + geom_label(data = beta.day.A, aes(beta, mu, 
    label = stock, col = stock), size = 2) + labs(title = "Betas and Security Market Line", 
    x = "Beta", y = "Mean Return (%)", subtitle = "Stock Portfolio (Daily Data)", 
    caption = "Figure 7") + ggthemes::theme_solarized() + theme(axis.text.x = element_text(angle = 0, 
    face = "bold", size = 6), axis.text.y = element_text(face = "bold", size = 6), 
    axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
    panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), legend.position = "none")


# monthly data
beta.mon.A = tibble(stock = beta.A$Symbol, beta = beta.A$MonthlyBeta, capm = Rf/12 + 
    MRP.mon * beta, mu = unlist(apply(portfolio.mon.A, 2, mean)) * 100)

# adding portfolio Beta
beta.mon.A = bind_rows(beta.mon.A, tibble(stock = "Stock Portfolio", beta = (cov(optim.mv(portfolio.mon.A, 
    gamma = 3.2)$mv$portfolio, mon.market)/var(mon.market))[[1]], capm = Rf/12 + 
    MRP.mon * beta, mu = optim.mv(portfolio.mon.A, gamma = 3.2)$mv$stat$mu * 100))
# computing alphas
beta.mon.A$alpha = beta.mon.A$mu - beta.mon.A$capm

figure8 <- ggplot() + geom_line(data = SML.mon, aes(beta, mu, group = 1)) + geom_point(data = beta.mon.A, 
    aes(beta, capm, col = stock)) + geom_point(data = beta.mon.A, aes(beta, mu, col = stock), 
    alpha = 0.6) + geom_segment(data = beta.mon.A, aes(x = beta, y = capm, yend = mu, 
    xend = beta), colour = "grey50") + geom_label(data = beta.mon.A, aes(beta, mu, 
    label = stock, col = stock), size = 2) + labs(title = "Betas and Security Market Line", 
    x = "Beta", y = "Mean Return (%)", subtitle = "Stock Portfolio (Monthly Data)", 
    caption = "Figure 8") + theme_solarized() + theme(axis.text.x = element_text(angle = 0, 
    face = "bold", size = 6), axis.text.y = element_text(face = "bold", size = 6), 
    axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
    panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), legend.position = "none")

colnames(beta.day.A) = c("stock", paste0("Daily.", colnames(beta.day.A)[-1]))
colnames(beta.mon.A) = c("stock", paste0("Monthly.", colnames(beta.mon.A)[-1]))

# table 6
inner_join(beta.day.A, beta.mon.A, by = "stock") %>% mutate_if(is.numeric, round, 
    3) %>% select(stock, Daily.beta, Daily.alpha, Monthly.beta, Monthly.alpha) %>% 
    knitr::kable(caption = "Betas for Portfolio") %>% kableExtra::kable_classic() %>% 
    kable_styling() %>% row_spec(c(0), font_size = 9) %>% row_spec(c(1:(ncol(portfolio.day.A))), 
    font_size = 7.5)
Betas for Portfolio
stock Daily.beta Daily.alpha Monthly.beta Monthly.alpha
ALERION CLEAN POWER 0.375 0.080 0.496 1.655
VALSOIA 0.478 -0.018 0.553 -0.313
CENTRALE DEL LATTE D’ITALIA 0.427 -0.004 0.505 -0.013
BORGOSESIA RSP 0.115 0.039 0.217 0.762
FIDIA 0.685 -0.037 1.006 -0.762
RIZZOLI CRER.DLSM.GP. 0.797 -0.044 0.913 -0.856
FULLSIX 0.370 -0.040 0.282 -0.651
GABETTI PROPERTY SLTN. 0.773 -0.001 1.211 -0.266
BEGHELLI 0.547 -0.027 0.684 -0.517
SOL 0.364 0.040 0.282 0.996
Stock Portfolio 0.200 0.084 0.014 2.783
figure7

figure8

Black-Litterman Approach

The Black-Letterman Model is based on the Bayes Theory, and the main innovation is the possibility to specify some views on the mean return of the security considered. Views are a way to take into account opinions and to insert into the model other informations. In order to specify this kind of information, we can construct a design matrix \(P\), that is a \(qxM\) matrix, where M is the number of assets and q the number of views. In BL approach we can specify views just for a group of assets, therefore the design matrix P can be not a square matrix.

Equilibrium CAPM and BL

The main idea of Black Letterman is to start from the definition of market equilibrium, then we would like to detect whatever we have a view that brings the behavior of the security outside market equilibrium. Suppose that the CAPM is valid, so the market portfolio gives us the equilibrium, given this portfolio the weights associated with each asset are:

\[W_{eq}^i = \frac{StockCap}{MarketCap} = \frac{P_i Q_i}{\sum_{i=1}^{n} P_i Q_i} \] Given the weights associated with equilibrium we can recover the implied mean under equilibrium:

\[\mu_{eq} = \gamma \, \hat{\Sigma} \,W_{eq}\] Therefore the prior distribution for \(\mu\) is:

\[f_{pr}(\mu) \sim N(\mu_{eq}, \tau \Sigma)\] - \(\tau\): is a parameter of uncertainty usually set \(\frac{1}{M}\)

Specificating views in Black Litterman

As before we specify \(P\), \(\mu\), \(v\), but \(P\) can be not squared but with the same number of column as the rows of the vector \(\mu\). Denote with \(q\) the number of views the design matrix is \(qxn\).

\[P_{qxm} = \begin{bmatrix} 1 & ... & 0 & 1 \\ ... & ... & ... & ... \\ 0 & ... & 1 & 1 \end{bmatrix}\]

The vector \(v\) is the vector of view and it is \(qx1\):

\[v = \begin{bmatrix} v_1 \\ ... \\ v_{q} \end{bmatrix}\] As before: \(P\mu = v\), moreover \(v \sim N(P\mu, \Omega)\), and \(v = P\mu + \Omega\)

The formula for \(\Omega\), that is the variance/covariance matrix for the views is:

\[\Omega = diag \bigl[ P (\tau \hat{\Sigma}) P^T \bigl]\] The posterior for Black Litterman

\[f_{po}(\mu |v, \Sigma) \propto exp \biggl\{ \frac{1}{2} \bigl( v - P\mu \bigl)^T \Omega \bigl( v - P\mu \bigl) + \frac{1}{2} \bigl( \mu - \mu_{eq} \bigl)^T \bigl( \tau \, \Sigma \bigl)^{-1} \bigl( \mu - \mu_{eq} \bigl) \biggl\} \sim N(\mu_{bl}, \, \Sigma_{bl} )\] The predictive density for Black Litterman is given by:

\[f(R_{t+1} |v,\Sigma) \sim N(\mu_{bl}, \, \Sigma + \Sigma_{bl})\] The mean in black litterman approach is given by:

\[\mu_{bl} = \biggl[ (\tau\Sigma)^{-1} + P^T\Omega^{-1} P \biggl]^{-1} \biggl[ (\tau\Sigma)^{-1} \mu_{eq} + P^T\Omega^{-1} v \biggl]\] Alternatively we can consider an equivalent expression in which we divide the equilibrium parameter from black litterman deviation from equilibrium:

\[\mu_{bl} = \mu_{eq} + \tau \Sigma P^T \biggl[ \Omega + P(\tau \Sigma) P^T \biggl]^{-1} \bigl( v - P \mu_{eq} \bigl)\] The variance covariance matrix in black litterman approach is given by:

\[\Sigma_{BL} = \biggl[ (\tau\Sigma)^{-1} + P^T\Omega^{-1} P \biggl]^{-1}\] Alternatively we can consider the equivalent expression:

\[\Sigma_{BL} = \tau \Sigma + \tau \Sigma P^T \biggl[ \Omega + P(\tau \Sigma) P^T \biggl]^{-1} P^T \tau \Sigma \] Finally the weights are given by: \[W_{bl} = \frac{1}{\gamma} \Sigma_{bl}^{-1} \, \mu_{bl}\]

Since we do not have the informations about the capitalization of the secuirities, we implement the BL approach specifying as mean equilibrium the actual vector of mean and then we specify a matrix of 4 views on the secuirities. Looking at the empirical values, we are able to formulate 3 views considering a scenario which is a little worst than the reality, in contrast the fourth view is optimistic. The specification considering monthly returns is:

  • View 1: ALERION CLEAN POWER overperform 0.1 better than VALSOIA CENTRALE DEL LATTE D’ITALIA (1%).
  • View 2: SOL overperform 0.01 better than BEGHELLI (1%).
  • View 3: FULLSIX return is zero.
  • View 4: BORGOSESIA RSP will have a perform (better than reality) of 0.01 (1%(.

While the specification considering daily returns is:

  • View 1: ALERION CLEAN POWER overperform 0.0005 (0.05%) better than VALSOIA CENTRALE DEL LATTE D’ITALIA
  • View 2: SOL overperform 0.0005 (0.05%) better than BEGHELLI
  • View 3: FIDIA return is zero
  • View 4: FULLSIX will have a perform (better than reality) of 0.00001 (0.001%)

As we can see from the graph our ideal investor will risk less than all his wealth if we consider daily data, instead with monthly data it risk approximativly 1. A possible explanetion of this differences could be the violation of the normality distribution of the prices.

# Assume as Market Equilibrium the mean
mu.eq.A = matrix(apply(portfolio.mon.A, 2, mean), ncol = 1)

# view 1: ALERION CLEAN POWER overperform 0.1 better than VALSOIA CENTRALE DEL
# LATTE D'ITALIA view 2: SOL overperform 0.01 better than BEGHELLI view 3:
# FULLSIX return is zero view 4: BORGOSESIA RSP will have a perform (better than
# reality) of 0.01

P.A = matrix(0, nrow = 4, ncol = ncol(portfolio.mon.A))
colnames(P.A) = colnames(portfolio.mon.A)

# view 1: ALERION CLEAN POWER overperform 0.1 better than VALSOIA CENTRALE DEL
# LATTE D'ITALIA
P.A[1, 1] = 1
P.A[1, 2] = -1

# view 2: SOL overperform 0.01 better than BEGHELLI
P.A[2, 9] = -1
P.A[2, 10] = 1

# view 3: FULLSIX return is zero
P.A[3, 7] = 1

# view 4: BORGOSESIA RSP will have a perform (better than reality) of 0.01
P.A[4, 4] = 1

views.A = c(0.1, 0.01, 0, 0.01)

figure9 <- plot.frontiere(portfolio.mon.A, method = "bl", min.gamma = 1, views = views.A, 
    P = P.A, mu.eq = mu.eq.A, max.gamma = 20)

figure9 <- figure9 + labs(subtitle = "Stock Portfolio (Monthly Data)", caption = "Figure 9") + 
    theme_solarized() + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    panel.grid.minor.x = element_blank(), legend.text = element_text(face = "italic", 
        size = 7), legend.title = element_text(face = "bold", size = 7), legend.background = element_rect(fill = "white", 
        color = "black"), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "top")
# Assume as Market Equilibrium the mean
mu.eq.A.day = matrix(apply(portfolio.day.A, 2, mean), ncol = 1)

# view 1: ALERION CLEAN POWER overperform 0.0005 (0.05%) better than VALSOIA
# CENTRALE DEL LATTE D'ITALIA view 2: SOL overperform 0.0005 (0.05%) better than
# BEGHELLI view 3: FIDIA return is zero view 4: FULLSIX will have a perform
# (better than reality) of 0.00001

P.A.day = matrix(0, nrow = 4, ncol = ncol(portfolio.day.A))
colnames(P.A.day) = colnames(portfolio.day.A)

# view 1: ALERION CLEAN POWER overperform 0.0005 (0.05%) better than VALSOIA
# CENTRALE DEL LATTE D'ITALIA
P.A.day[1, 1] = 1
P.A.day[1, 2] = -1

# view 2: SOL overperform 0.0005 (0.05%) better than BEGHELLI
P.A.day[2, 9] = -1
P.A.day[2, 10] = 1

# view 3: FIDIA return is zero
P.A.day[3, 5] = 1

# view 4: FULLSIX will have a perform (better than reality) of 0.00001
P.A.day[4, 7] = 1

views.A.day = c(5e-04, 5e-04, 0, 1e-05)

figure10 <- plot.frontiere(portfolio.day.A, method = "bl", min.gamma = 1, views = views.A.day, 
    P = P.A.day, mu.eq = mu.eq.A.day, max.gamma = 50)

figure10 <- figure10 + labs(subtitle = "Stock Portfolio (Daily Data)", caption = "Figure 10") + 
    theme_solarized() + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    legend.text = element_text(face = "italic", size = 7), legend.title = element_text(face = "bold", 
        size = 7), legend.background = element_rect(fill = "white", color = "black"), 
    panel.grid.minor.x = element_blank(), panel.grid.major.y = element_line(colour = "grey60", 
        linetype = "dotted"), legend.position = "top")
figure9 + geom_point(data = optim.bl(portfolio.mon.A, views = views.A, P = P.A, mu.eq = mu.eq.A, 
    gamma = 3.2)$stat, aes(var * 100, mu * 100, label = "gamma 3.2"), alpha = 0.7, 
    size = 4, col = "orange")

figure10 + geom_point(data = optim.bl(portfolio.day.A, views = views.A.day, P = P.A.day, 
    mu.eq = mu.eq.A.day, gamma = 3.2)$stat, aes(var * 100, mu * 100, label = "gamma 3.2"), 
    size = 4, alpha = 0.7, col = "orange")

Classical Bayes Approach

In the Classical Bayes Approach we usually specify a prior density for the mean and then we insert this into the model in order to take care of this information. In this section we implement the Bayes optimization with an informative prior assuming a normal density for the mean. Assume that the sample density for the returns is normal:

\[f(r_t | \mu, \Sigma) \sim N(\mu, \Sigma)\]

The prior for the mean is:

\[f_{pr} (\mu) \sim N(\mu_0, \Lambda_0)\]

Where \(\Lambda_0) = cI_{mxm}\) is a diagonal matrix, as c goes to plus infinity the prior collapse into an non-informative prior. Applying Bayes Theorem we get the posterior density for the mean:

\[f_{po} (\mu | X, \Sigma) \sim N(\mu_1, \Sigma_1)\]

Where the parameters are:

\[\mu_{1} = \bigl[ M \Sigma^{-1} + \Lambda_0^{-1} \bigl]^{-1} \bigl[ M \Sigma^{-1} \hat{\mu} + \Lambda_0^{-1} \mu_0 \bigl]\]

\[\Sigma_{1} = \bigl[ M \Sigma^{-1} + \Lambda_0^{-1} \bigl]^{-1}\]

The Predictive density used to make prediction and to compute the expected value of \(\mu\) is:

\[f ( r_{t+1} | X, \Sigma) \sim N(\mu_1, \Sigma + \Sigma_1)\]

Note that the variance/covariance matrix of the predictive density is given by the sum of the sample variance plus the posterior.

Rewriting \(\mu_1\) we get:

\[\mu_{1} = \frac{\Lambda_0^{-1}}{M \Sigma^{-1} + \Lambda_0^{-1} } \, \mu_0 + \frac{M\Sigma^{-1}}{M \Sigma^{-1} + \Lambda_0^{-1} } \mu = \delta \mu_0 + (1-\delta) \mu\]

Where \(\delta\) is the shrinkage factor, and it is a matrix that represent a weighted avererage of the prior and posterior variance/covariance matrix:

\[\delta = \frac{\Lambda_0^{-1}}{M \Sigma^{-1} + \Lambda_0^{-1} }\]

  • \(\delta\) higher: means stronger information from the prior.
  • \(\delta\) lower: means less precise information from the prior.
figure11 <- plot.frontiere(portfolio.mon.A, method = "bayes", min.gamma = 1, max.gamma = 30)

figure12 <- plot.frontiere(portfolio.day.A, method = "bayes", min.gamma = 1, max.gamma = 30)

figure11 <- figure11 + labs(subtitle = "Stock Portfolio (Monthly Data)", caption = "Figure 11") + 
    theme_solarized() + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    panel.grid.minor.x = element_blank(), legend.text = element_text(face = "italic", 
        size = 7), legend.title = element_text(face = "bold", size = 7), legend.background = element_rect(fill = "white", 
        color = "black"), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "top")

figure12 <- figure12 + labs(subtitle = "Stock Portfolio (Daily Data)", caption = "Figure 12") + 
    theme_solarized() + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    panel.grid.minor.x = element_blank(), legend.text = element_text(face = "italic", 
        size = 7), legend.title = element_text(face = "bold", size = 7), legend.background = element_rect(fill = "white", 
        color = "black"), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "top")

figure11 + geom_point(data = optim.bayes(portfolio.mon.A, gamma = 3.2)$stat, aes(var * 
    100, mu * 100, label = "gamma 3.2"), size = 4, alpha = 0.7, col = "orange")

figure12 + geom_point(data = optim.bayes(portfolio.day.A, gamma = 3.2)$stat, aes(var * 
    100, mu * 100, label = "gamma 3.2"), size = 4, alpha = 0.7, col = "orange")

Comment: Difference Bayes-BL

If we take as benchmark asset allocation the Mean-Variance allocation done initially and we can compare the position of our ideal investor (with \(\gamma = 3.2\)) we can see the difference in these two types of optimization:

  • Mean Variance Investor: we use only the information coming from the data, assuming that the variance/covariance matrix and the mean of our assets are known we get the optimal weights.

  • Bayes Investor: in this optimization we assume for the prior parameter of the mean, a vector given by the empiric mean plus a one standard deviation. This assumption mean that we are expecting a better situation then the reality, comparing the Bayes Frontiere with the the Mean Variance situatione we see that the same investor (gamma 3.2) is located in a different part of the frontiere and will risk more since it has an optimistic view.

  • Black-Litterman Investor: since with Bayes method we have insert an optimistic view, in the specification of our views we hypothize that the mean return are a little bit lower than the reality. The investor in this situation will risk less with respect to the Bayes situation, he will invest at least all his wealth only in the range of gamma between 3 and 3,6. Another difference between the two methods, is that, we assume as \(\mu_{eq}\) the empiric vector of mean, while in Bayes approach we add one standard deviation.

It is clearly evident as two different assumptions on the prior, will produce a huge differents in the allocation, in this case the Bayes result is not realible since it is based on a strong prior assumption not confermed by data. A common approach used for the specification of a prior is to compute the mean and the variance on a sub-sample, for example in the interval between 2015 to 2018 and then use those values as prior parameters. This approach is equivalent to the case in which we are in 2018 and we want to construct a portfolio for the next years taking into account the informations availables till that moment.

  • The last thing to notice is that using these model we implicitly assume the normality of the returns and this assumption is not confermed by data with daily frequencies. As we can see from Table 1 and Table 2 the kurtosis in Daily frequencies is very high and suggest a strong deviation from the normal distribution.

Portfolio Selection and Comparisons

In this work we have implemented different methods to get the portfolios weights, now we estimate directly one porfolio for each method. As we have seen the value of the weights strongly depends on the choice of the coefficient of risk adversion \(\gamma\), in order to make comparisons we set the value of gamma at 3.2. In choosing of the optimal portfolio we need to consider the payoff between Risk (Porfolio Variance) and Return (Portfolio Mean):

  • For a high-adverse individual the best choice could be the Mean Variance Porfolio with non negaive weights that ensure a profit double with respect to the Global Minimum Variance portfolio with only 0.5% of standard deviation. Morover the MV-Portfolio with non negative weights has the Sharpe Ratio higher. Moreover this portfolio is on the frontiere (Monthly) and so it is also efficient.

  • For a low-adverse individual the best choice could be the Mean Variance Porfolio that ensure a good profit (2,79%) and also a low risk, we do not take into account the bayes portfolio since it prior assumption on the mean return strongly deviate from the reality and bring the investor to invest more than one even for high level of gamma. In order to have that the Bayes investor allocate only all his wealth we need that its coefficient of risk adversion is between 16 and 20 (with monthly data). Moreover the normality of the returns with daily frequency (for monthly not for all) is not supported by data, therefore in the consideration of the results obtained with this model (and also BL), we need to consider this problem.

Morover with a equal-weighted linear combination of the 4 methods (MV, GMV, BL, Bayes) we have recovered another portfolio called eqmix, it is situated below the frontere for both Daily and Monthly frequencies, therefore it is not efficient.

gamma = 3.2

# Effient frontiere
efficient.mon <- efficient.frontiere(portfolio.mon.A, max.mup = 0.07)

# comparison porfolio A day
comparison.mon = bind_rows(optim.mv(portfolio.mon.A, gamma = gamma)$mv$stat, optim.mv(portfolio.mon.A, 
    gamma = gamma)$gmv$stat, optim.mv(portfolio.mon.A, gamma = gamma)$M$stat, optim.bayes(portfolio.mon.A, 
    gamma = gamma)$stat, optim.bl(portfolio.mon.A, P = P.A, views = views.A, mu.eq = mu.eq.A, 
    gamma = gamma)$stat, create.combination(portfolio.mon.A, gamma = gamma)$stat, 
    nnwgt.A.mon$stat)

comparison.mon = comparison.mon %>% mutate(mu = mu * 100, var = var * 100, sd = sd * 
    100, skewness = skewness * 100, kurtosis = kurtosis * 100) %>% mutate(SR = (mu - 
    0.05/12)/var)

figure13 <- efficient.mon + geom_label(data = comparison.mon, aes(var, mu, label = method, 
    col = method), alpha = 0.7, size = 4) + labs(title = "Comparison on the Efficient Frontiere", 
    subtitle = "Stock Portfolio with Gamma = 3.2 (Monthly Data)", caption = "Figure 13") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "none")

# Effient frontiere
efficient.day <- efficient.frontiere(portfolio.day.A, max.mup = 0.002, threshold = 1e-05)

# comparison porfolio A
comparison.day = bind_rows(optim.bayes(portfolio.day.A, gamma = 3.2)$stat, optim.mv(portfolio.day.A, 
    gamma = 3.2)$mv$stat, optim.mv(portfolio.day.A, gamma = 3.2)$gmv$stat, optim.mv(portfolio.day.A, 
    gamma = 3.2)$M$stat, optim.bl(portfolio.day.A, P = P.A.day, views = views.A.day, 
    mu.eq = mu.eq.A.day, gamma = 3.2)$stat, create.combination(portfolio.day.A, gamma = 3.2, 
    P = P.A.day, views = views.A.day, mu.eq = mu.eq.A.day)$stat, nnwgt.A.day$stat)

comparison.day = comparison.day %>% mutate(mu = mu * 100, var = var * 100, sd = sd * 
    100, skewness = skewness * 100, kurtosis = kurtosis * 100) %>% mutate(SR = (mu - 
    0.05/360)/var)

figure14 <- efficient.day + geom_label(data = comparison.day[-1, ], aes(var, mu, 
    label = method, col = method), alpha = 0.7, size = 4) + labs(title = "Comparison on the Efficient Frontiere", 
    subtitle = "Stock Portfolio with Gamma = 3.2 (Daily Data)", caption = "Figure 14") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "none")

comparison.mon %>% select(method, mu, sd, skewness, kurtosis, SR, wgt, gamma) %>% 
    arrange(desc(mu)) %>% knitr::kable(caption = "Portfolios Comparison with Monthly Returns") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(1:nrow(comparison.mon)), 
    font_size = 7)
Portfolios Comparison with Monthly Returns
method mu sd skewness kurtosis SR wgt gamma
BL 4.0890178 15.938207 60.047761 312.60341 1.6080417 1.024369 3.2
Bayes 3.8549383 25.533944 51.636649 328.53104 0.5906252 5.621385 3.2
MV 2.7956891 9.346940 3.043588 244.00322 3.1952308 1.059813 3.2
eqmix 2.5047145 10.658701 15.423438 269.66034 2.2010333 2.193849 3.2
GMV 0.6965026 4.531811 55.785556 336.11696 3.3711124 1.000000 3.2
MV NonNeg 0.5725431 4.713581 43.747737 34.31292 2.5581979 1.002000 3.2
1/M 0.3735962 5.728453 40.412748 386.67772 1.1257885 1.000000 3.2
comparison.day %>% select(method, mu, sd, skewness, kurtosis, SR, wgt, gamma) %>% 
    arrange(desc(mu)) %>% knitr::kable(caption = "Portfolios Comparison with Daily Returns") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(1:nrow(comparison.day)), 
    font_size = 7)
Portfolios Comparison with Daily Returns
method mu sd skewness kurtosis SR wgt gamma
Bayes 0.2076295 8.186676 -40.92487 1115.7203 0.3095873 7.7648884 3.2
eqmix 0.1010791 2.780063 -23.05916 1090.2977 1.3060358 2.5757966 3.2
MV 0.0924656 1.699868 55.71090 724.8702 3.1951934 0.7850650 3.2
BL 0.0766792 1.419674 47.29311 709.1394 3.7976321 0.7532328 3.2
MV NonNeg 0.0297829 1.052321 -23.95887 768.7405 2.6769516 0.9900000 3.2
GMV 0.0275423 1.047061 -29.99651 1011.1166 2.4995396 1.0000000 3.2
1/M 0.0198542 1.128686 -68.81234 1210.2893 1.5475971 1.0000000 3.2
figure13

figure14

Question 3: Funds

Now we consider investment funds, this situation is different from the previous since now we are dealing with portfolios of assets that can be very different. We can have funds that constains stocks and bonds with different positions such as long or short. Moreover the assets contained in a single fund can come from different markets. In order to construct a well diversified portfolio of found we did some research on internet using as sources:

One of the main problems in the selection of a diversified portfolio is the presence of assets from the United States market that in many case have a huge proportion in the index. The funds considered are:

  1. INVESCO EURO BOND A ACC EUR ( link source ): this fund invest mainly in Bonds (96%) with a long position, with mainly EU bonds, Spain Government Bonds, and Italian Bonds. The standard deviation in 3 years is 5,08%, the mean return 3% and the Sharpe Ratio is 0,67. Finally the \(\beta = 1,27\). We choose a fund with a large components of bonds in order to diversificate our porfolio.

  2. PICTET FUND.EU.S A LUX CLEAN ENERGY I ( link source ): it invest mainly in United States (52%) Europe (32%) and in emergent countries in Asia (5,4%). The main sectors of investments are Tecnology (48%%), Comunication Services (15%), Utilities (25%), Ciclic Goods (2,74%%) and Industrial (17%). The standard deviation in 3 years is 18,8%, the mean return 15% and the Sharpe Ratio is 1.75. Finally the \(\beta = 0,42\).

  3. INVESCO EURO EQUITY E EUR ACC ( link source ): it invest mainly in shres of Europe (94%). The main sectors of investments are Tecnology (48%%), Comunication Services (10%), Finance (18%), Ciclic Goods (9,74%%), Energy (8,32%), and Industrial (18%). The standard deviation in 3 years is 24,61%, the mean return 6,24% and the Sharpe Ratio is 0.26. Finally the \(\beta = 1,23\). Despite the high standard deviation and beta, we have decided to insert this fund in order to differenciate the markets considered.

  4. PICTET GESTION FUNDS BIOTECH R EUR ( link source ): is a fund that invest all in health sector (100%), mainly in United States (93%), Canada(2,5%) and Asia(2,3%). The standard deviation in 3 years is 22,18%, the mean return 10,60% and the Sharpe Ratio is 0,48. Finally the \(\beta = 0,94\).

  5. CANDRIAM EQUITIES L GLBL DEMOGRAPHY I EUR CAP ( link source: it invest mainly in United States (60%), United Kingdom (10,16%), Eurozone (9,59%) and Japan (8,12%). The main sectors of investments are Tecnology (21%), Industrial (11,37%), Finance (15%), Ciclic Goods (10%) and Health (11%). The standard deviation in 3 years is 17,57%, the mean return 15,23% and the Sharpe Ratio is 0,84. Finally the \(\beta = 0,98\).

  6. EURIZON AZIONI AMERICA ( link source ): it invest mainly in United States (95%) and in emergent countries in Asia (1,72%). The main sectors of investments are Tecnology (20%), Comunication Services (15%), Finance (15%), Ciclic Goods (14%) and Health (12%). The standard deviation in 3 years is 17,57%, the mean return 15,23% and the Sharpe Ratio is 0,84. Finally the \(\beta = 0,98\).

  7. ROBECO GLOBAL CONSUMER TRENDS F EUR ( link source): it invest mainly in United States (63%), West Europe Euro (13%), emrgent countries in Asia (11%). The main sectors of investments are Tecnology (17%), Comunication Services (22%), Finance (7%), Ciclic Goods (29%) and Health (12%). The standard deviation in 3 years was 15,30% while the mean return 21,13% with a Sharpe Ratio of 1,29. The \(\beta = 0,93\).

# daily data funds
funds.day = read_excel("dataset.xlsx", sheet = "funds daily", na = "NA")
funds.day$Date = as.Date(funds.day$Date)

# compute log return
funds.day.ret = tibbleReturn(funds.day, type = "log", order.by = "Date")[-1, ]

# monthly data funds
funds.mon = read_excel("dataset.xlsx", sheet = "funds monthly", na = "NA")
funds.mon$Date = as.Date(funds.mon$Date)

# compute log return
funds.mon.ret = tibbleReturn(funds.mon, type = "log", order.by = "Date")[-1, ]

# import files with information on selected funds
selected.funds = read_excel("dataset.xlsx", sheet = "selected funds", na = "") %>% 
    na.omit()

# selected funds names
funds.name = select(selected.funds, stock, area, sd_3y, beta)$stock

# variance/covariance and correlation funds
cov.day.funds = cov(funds.day.ret[, -1])
cov.mon.funds = cov(funds.mon.ret[, -1])

# correlation matrix
cor.day.funds = cor(funds.day.ret[, -1])
cor.mon.funds = cor(funds.mon.ret[, -1])



funds.name <- c("INVESCO EURO BOND A ACC EUR", "PICTET FUND.EU.S A LUX CLEAN ENERGY I", 
    "INVESCO EURO EQUITY E EUR ACC", "PICTET GESTION FUNDS BIOTECH R EUR", "CANDRIAM EQUITIES L GLBL DEMOGRAPHY I EUR CAP", 
    "EURIZON AZIONI AMERICA", "ROBECO GLOBAL CONSUMER TRENDS F EUR")

# creating dataframe with selected funds
funds.day.A = funds.day.ret[, c(1, which(colnames(funds.day.ret) %in% funds.name))]
funds.mon.A = funds.mon.ret[, c(1, which(colnames(funds.mon.ret) %in% funds.name))]

# summary statistics daily funds
funds.stat.day = tibbleSummary(funds.day.A[, -1])

funds.stat.day %>% mutate_if(is.numeric, round, 5) %>% knitr::kable(caption = "Funds Characteristics (Daily Data)") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(0), font_size = 8) %>% 
    row_spec(c(1:nrow(funds.stat.day)), font_size = 7)
Funds Characteristics (Daily Data)
stock mean variance sd skewness kurtosis
PICTET FUND.EU.S A LUX CLEAN ENERGY I 0.00041 0.00015 0.01223 -0.60167 7.70067
PICTET GESTION FUNDS BIOTECH R EUR 0.00021 0.00026 0.01622 -0.22424 5.04767
CANDRIAM EQUITIES L GLBL DEMOGRAPHY I EUR CAP 0.00042 0.00011 0.01039 -1.07544 17.05121
EURIZON AZIONI AMERICA 0.00039 0.00015 0.01244 -1.00734 19.07350
INVESCO EURO EQUITY E EUR ACC 0.00022 0.00016 0.01253 -1.55401 21.70162
INVESCO EURO BOND A ACC EUR 0.00011 0.00001 0.00229 -1.22880 32.91278
ROBECO GLOBAL CONSUMER TRENDS F EUR 0.00062 0.00012 0.01115 -0.76609 12.29477
funds.stat.mon = tibbleSummary(funds.mon.A[, -1])

funds.stat.mon %>% mutate_if(is.numeric, round, 3) %>% knitr::kable(caption = "Funds Characteristics (Monthly Data)") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(0), font_size = 8) %>% 
    row_spec(c(1:nrow(funds.stat.mon)), font_size = 7)
Funds Characteristics (Monthly Data)
stock mean variance sd skewness kurtosis
PICTET FUND.EU.S A LUX CLEAN ENERGY I 0.009 0.003 0.056 -0.656 4.567
PICTET GESTION FUNDS BIOTECH R EUR 0.003 0.005 0.071 -0.426 4.348
CANDRIAM EQUITIES L GLBL DEMOGRAPHY I EUR CAP 0.009 0.002 0.046 -1.104 7.054
EURIZON AZIONI AMERICA 0.008 0.003 0.050 -1.049 7.193
INVESCO EURO EQUITY E EUR ACC 0.005 0.003 0.057 -0.780 6.942
INVESCO EURO BOND A ACC EUR 0.002 0.000 0.014 -1.542 10.930
ROBECO GLOBAL CONSUMER TRENDS F EUR 0.013 0.002 0.044 -0.638 4.880

Funds Returns

We have decided to plot the returns instead of the prices to compare all the assets since the price of the CANDRIAM EQUITIES L GLBL DEMOGRAPHY I EUR CAP is very different from the others and it dominate the graph. From the figure 15 it is easy to notice tha difference in the level of volatility which charcterize them. In particular there is one (INVESCO EURO BOND A ACC EUR) that shows really constant behaviour with monthly return near to zero while the others such as PICTED GESTION FUNDS BIOTECH R EUR seems to be the more variable.

# plot prices
figure15 <- funds.mon.ret[, c(1, which(colnames(funds.mon) %in% funds.name))] %>% 
    gather("Symbol", "Price", -Date) %>% ggplot(aes(Date, Price, group = Symbol, 
    col = Symbol)) + geom_line() + # scale_y_continuous(breaks = seq(0, 50, 10)) +
facet_wrap(~Symbol, as.table = T, ncol = 2) + labs(title = "Funds's Portfolio: Returns", 
    subtitle = "Monthly Returns", x = "", y = "Price", caption = "Figure 15") + ggthemes::theme_solarized() + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), strip.text = element_text(face = "italic", size = 8, color = "red"), 
        strip.background.x = element_rect(colour = "black", fill = "white"), panel.grid.major.x = element_blank(), 
        panel.grid.minor.x = element_blank(), panel.grid.major.y = element_line(colour = "grey60", 
            linetype = "dotted"), legend.position = "none")

figure15

Mean Variance Optimization with Funds

figure16 <- plot.frontiere(funds.day.A[, -1], method = "mv", min.gamma = 1, max.gamma = 30)

figure17 <- plot.frontiere(funds.mon.A[, -1], method = "mv", min.gamma = 1, max.gamma = 20)

figure16 + labs(subtitle = "Funds's Portfolio (Daily Data)", x = "", y = "Return", 
    caption = "Figure 16") + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    panel.grid.minor.x = element_blank(), panel.grid.major.y = element_line(colour = "grey60", 
        linetype = "dotted"), legend.text = element_text(face = "italic", size = 7), 
    legend.title = element_text(face = "bold", size = 7), legend.background = element_rect(fill = "white", 
        color = "black"), legend.position = "top") + geom_point(data = optim.mv(funds.day.A[, 
    -1], gamma = 3.2)$mv$stat, aes(var * 100, mu * 100, label = "gamma 3.2"), size = 4, 
    col = "orange")

figure17 + labs(subtitle = "Funds's Portfolio (Monthly Data)", x = "", y = "Return", 
    caption = "Figure 17") + theme(axis.text.x = element_text(angle = 0, face = "bold", 
    size = 6), axis.text.y = element_text(face = "bold", size = 6), axis.title = element_text(face = "bold"), 
    plot.title = element_text(face = "bold", size = 12), plot.subtitle = element_text(face = "italic", 
        size = 10), plot.caption = element_text(face = "italic", size = 10), panel.grid.major.x = element_blank(), 
    panel.grid.minor.x = element_blank(), legend.text = element_text(face = "italic", 
        size = 7), legend.title = element_text(face = "bold", size = 7), legend.background = element_rect(fill = "white", 
        color = "black"), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "top") + geom_point(data = optim.mv(funds.mon.A[, -1], gamma = 3.2)$mv$stat, 
    aes(var * 100, mu * 100, label = "gamma 3.2"), size = 4, col = "orange", alpha = 0.7)

Comment on Funds’s Mean Variance Frontiere

Considering the Mean Variance optimization with funds we see that the values of the risk adversion coefficients required to invest 100% of the wealth in the Funds’s Portfolio are higher compared to the ones identified in the question 1 in which we considere the single assets. The orange point represent as always our potential investor with \(\gamma = 3.2\), as we can see the in this case the same investor will invest more than its wealth in the Funds’s Portfolio and it means that funds seems to be less riskier, since the funds already make the diversification job inside their own portfolios of securities and so the risk is mitigted by the fund itself.

  • In the Funds’s Portfolio we change the type of target investor increasing \(\gamma = 6.5\) we are dealing more risk adverse individual with respect to Question 1.

Usually a portfolio of asset can be classificated as an active investment strategy since by adjusting the weights of the assets one tries to get higher excess returns. On the countrary a portfolio of funds can be considered as a passive investment strategy since it does not require as much effort as investing into the single assets. It is much more easy to invest in funds which try to replicate the behaviour of different markets and sectors by pooling togheter huge financial resurces coming from a large audience of small investors. This can also explain why the levels of risk aversion are higher

MV Optimization with Non negative weights

In the implementation of a portfolio with non negative weights we set a bound for the minumum and maximum value for the weights respectivaly 0 and 0.3, while the other implementation (MV) is done follwing the classical mean variance optimization without imposing any constraint on the coefficient and setting \(\gamma = 6.5\).

# portfolio specification with PortfolioAnalytics
stocks.A.mon = funds.mon.A %>% select(date = "Date", everything()) %>% timetk::tk_xts(silent = TRUE)

stocks.A.day = funds.day.A %>% select(date = "Date", everything()) %>% timetk::tk_xts(silent = TRUE)

# specification of a portfolio of assets
pspec.A.mon <- portfolio.spec(assets = colnames(stocks.A.mon))
pspec.A.day <- portfolio.spec(assets = colnames(stocks.A.day))

# sum of weights: if full investment weight sum to 1
pspec.A.mon <- add.constraint(portfolio = pspec.A.mon, type = "full_investment")
pspec.A.day <- add.constraint(portfolio = pspec.A.day, type = "full_investment")

pspec.A.mon <- add.constraint(portfolio = pspec.A.mon, type = "weight_sum", min_sum = 0.99, 
    max_sum = 1.01)
pspec.A.day <- add.constraint(portfolio = pspec.A.day, type = "weight_sum", min_sum = 0.99, 
    max_sum = 1.01)

# constraint to have positive weights:
pspec.A.mon <- add.constraint(portfolio = pspec.A.mon, type = "long_only")
pspec.A.day <- add.constraint(portfolio = pspec.A.day, type = "long_only")

# contraint to set a minimum and a maximum for each asset
pspec.A.day <- add.constraint(portfolio = pspec.A.day, type = "box", min = 0, max = 0.3)
pspec.A.day <- add.constraint(portfolio = pspec.A.day, type = "box", min = 0, max = 0.3)

# mean objective
pspec.A.mon <- add.objective(portfolio = pspec.A.mon, type = "return", name = "mean")  # mean

pspec.A.day <- add.objective(portfolio = pspec.A.day, type = "return", name = "mean")  # mean

# variance objective
pspec.A.mon <- add.objective(portfolio = pspec.A.mon, type = "risk", name = "var")  # uses sd

pspec.A.day <- add.objective(portfolio = pspec.A.day, type = "risk", name = "var")  # uses sd

set.seed(1)
funds.nnwgt.optim.A.mon = optimize.portfolio(stocks.A.mon, pspec.A.mon, optimize_method = "random", 
    search_size = 1000)
funds.nnwgt.optim.A.day = optimize.portfolio(stocks.A.day, pspec.A.day, optimize_method = "random", 
    search_size = 1000)

nnwgt.funds.A.mon = as.matrix(funds.mon.A[, -1]) %*% matrix(c(funds.nnwgt.optim.A.mon$weights), 
    ncol = 1)
nnwgt.funds.A.day = as.matrix(funds.day.A[, -1]) %*% matrix(c(funds.nnwgt.optim.A.day$weights), 
    ncol = 1)


# non negative weights portfolio A
funds.nnwgt.A.mon = list(stat = tibble(method = "MV NonNeg", mu = mean(nnwgt.funds.A.mon), 
    var = var(nnwgt.funds.A.mon)[[1]], sd = sd(nnwgt.funds.A.mon), skewness = skewness(nnwgt.funds.A.mon), 
    kurtosis = kurtosis(nnwgt.funds.A.mon), gamma = 6.5, wgt = sum(funds.nnwgt.optim.A.mon$weights)), 
    wgt = matrix(c(funds.nnwgt.optim.A.mon$weights), ncol = 1), portfolio = nnwgt.funds.A.mon)

funds.nnwgt.A.day = list(stat = tibble(method = "MV NonNeg", mu = mean(nnwgt.funds.A.day), 
    var = var(nnwgt.funds.A.day)[[1]], sd = sd(nnwgt.funds.A.day), skewness = skewness(nnwgt.funds.A.day), 
    kurtosis = kurtosis(nnwgt.funds.A.day), gamma = 6.5, wgt = sum(funds.nnwgt.optim.A.day$weights)), 
    wgt = matrix(c(funds.nnwgt.optim.A.day$weights), ncol = 1), portfolio = nnwgt.funds.A.day)

figure18 <- bind_rows(tibble(frequency = "MV-NN Daily", stock = colnames(funds.day.A)[-1], 
    wgt = funds.nnwgt.A.day$wgt), tibble(frequency = "MV-NN Monthly", stock = colnames(funds.mon.A)[-1], 
    wgt = funds.nnwgt.A.mon$wgt), tibble(frequency = "MV Monthly", stock = colnames(funds.mon.A)[-1], 
    wgt = optim.mv(funds.mon.A[-1], gamma = 6.5, normalize = TRUE)$mv$wgt), tibble(frequency = "MV Daily", 
    stock = colnames(funds.day.A)[-1], wgt = optim.mv(funds.day.A[-1], gamma = 6.5, 
        normalize = TRUE)$mv$wgt)) %>% ggplot(aes(reorder(stock, wgt), wgt, fill = frequency)) + 
    geom_bar(stat = "identity", position = "dodge") + labs(title = "Comparison on Porfolios Weights", 
    x = "", y = "Weight", caption = "Figure 18") + theme_solarized() + coord_flip() + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        legend.text = element_text(face = "italic", size = 6), legend.title = element_text(face = "bold", 
            size = 6), legend.background = element_rect(fill = "white", color = "black"), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "top")

figure18

figure20 <- efficient.frontiere(funds.mon.A[, -1], max.mup = 0.03)

figure21 <- efficient.frontiere(funds.day.A[, -1], max.mup = 0.002, threshold = 1e-05)

figure20 + labs(subtitle = "Funds's Portfolio (Monthly Data)", x = "Portfolio Variance (%)", 
    y = "Portfolio Return(%)", caption = "Figure 20") + theme(axis.text.x = element_text(angle = 0, 
    face = "bold", size = 6), axis.text.y = element_text(face = "bold", size = 6), 
    axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
    legend.text = element_text(face = "italic", size = 8), legend.title = element_text(face = "bold", 
        size = 8), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "none")

figure21 + labs(subtitle = "Funds's Portfolio (Daily Data)", x = "Portfolio Variance (%)", 
    y = "Portfolio Return(%)", caption = "Figure 21") + theme(axis.text.x = element_text(angle = 0, 
    face = "bold", size = 6), axis.text.y = element_text(face = "bold", size = 6), 
    axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
    legend.text = element_text(face = "italic", size = 8), legend.title = element_text(face = "bold", 
        size = 8), panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
    legend.position = "none")

Comparison of Results

The MV portfolio gaves the highest return in both daily and monthly frequencies, moreover in both case it is situated on the efficient frontiere (in the daily seems outside, but it is a graphic effect, the point is on the frontiere). Despite this the MV portfolio has a return high becouse in the implementation there are not contraints on the coefficient and as we can see from Figure 18 some weights are near to 2 and other to -2. The standard deviation of GMV portfolio, in the funds case, is very lower than the Stock Portfolio, due to the diversification already done by the funds itself, the mean return is also very lower.

gamma = 6.5

# Effient frontiere
efficient.mon.funds <- efficient.frontiere(funds.mon.A[, -1], max.mup = 0.08)

efficient.day.funds <- efficient.frontiere(funds.day.A[, -1], max.mup = 0.002, threshold = 1e-05)

# comparison porfolio month
comparison.mon.funds = bind_rows(optim.mv(funds.mon.A[, -1], gamma = gamma)$mv$stat, 
    optim.mv(funds.mon.A[, -1], gamma = gamma)$gmv$stat, optim.mv(funds.mon.A[, -1], 
        gamma = gamma)$M$stat, funds.nnwgt.A.mon$stat)

comparison.mon.funds = comparison.mon.funds %>% mutate(mu = mu * 100, var = var * 
    100, sd = sd * 100, skewness = skewness * 100, kurtosis = kurtosis * 100)

figure22 <- efficient.mon.funds + geom_label(data = comparison.mon.funds, aes(var, 
    mu, label = method), alpha = 0.7, size = 4) + labs(title = "Comparison on the Efficient Frontiere", 
    subtitle = "Funds's Portfolio, Gamma 3.2 (Monthly Data)", caption = "Figure") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "none")


# comparison porfolio day
comparison.day.funds = bind_rows(optim.mv(funds.day.A[, -1], gamma = gamma)$mv$stat, 
    optim.mv(funds.day.A[, -1], gamma = gamma)$gmv$stat, optim.mv(funds.day.A[, -1], 
        gamma = gamma)$M$stat, funds.nnwgt.A.day$stat)

comparison.day.funds = comparison.day.funds %>% mutate(mu = mu * 100, var = var * 
    100, sd = sd * 100, skewness = skewness * 100, kurtosis = kurtosis * 100)

figure23 <- efficient.day.funds + geom_label(data = comparison.day.funds, aes(var, 
    mu, label = method), alpha = 0.7, size = 4) + labs(title = "Comparison on the Efficient Frontiere", 
    subtitle = "Funds's Portfolio, Gamma 3.2 (Daily Data)", caption = "Figure") + 
    theme(axis.text.x = element_text(angle = 0, face = "bold", size = 6), axis.text.y = element_text(face = "bold", 
        size = 6), axis.title = element_text(face = "bold"), plot.title = element_text(face = "bold", 
        size = 12), plot.subtitle = element_text(face = "italic", size = 10), plot.caption = element_text(face = "italic", 
        size = 10), panel.grid.major.x = element_blank(), panel.grid.minor.x = element_blank(), 
        panel.grid.major.y = element_line(colour = "grey60", linetype = "dotted"), 
        legend.position = "none")

comparison.day.funds %>% arrange(desc(mu)) %>% knitr::kable(caption = "Funds's Portfolio with Daily Returns") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(0), font_size = 9) %>% 
    row_spec(c(1:nrow(comparison.day.funds)), font_size = 7)
Funds’s Portfolio with Daily Returns
method mu var sd skewness kurtosis gamma wgt
MV 0.1057118 0.0162633 1.2752784 -46.91169 857.0682 6.5 3.541568
1/M 0.0338087 0.0075543 0.8691538 -118.15111 1294.7680 6.5 1.000000
MV NonNeg 0.0301488 0.0047687 0.6905577 -154.99257 1516.6597 6.5 0.990000
GMV 0.0115038 0.0004997 0.2235456 -93.62694 2824.5961 6.5 1.000000
comparison.mon.funds %>% arrange(desc(mu)) %>% knitr::kable(caption = "Funds's Portfolio with Monthly Returns") %>% 
    kableExtra::kable_classic() %>% kable_styling() %>% row_spec(c(0), font_size = 9) %>% 
    row_spec(c(1:nrow(comparison.mon.funds)), font_size = 7)
Funds’s Portfolio with Monthly Returns
method mu var sd skewness kurtosis gamma wgt
MV 3.5089911 0.5398448 7.347413 -5.701214 287.4993 6.5 1.038059
1/M 0.7053433 0.1809242 4.253518 -85.562080 593.3051 6.5 1.000000
MV NonNeg 0.2271374 0.0195017 1.396484 -155.211127 813.6398 6.5 0.994000
GMV 0.1098800 0.0162848 1.276121 -56.554389 752.0138 6.5 1.000000
figure22

Bibliography

[1] Lectures and Slides, Massimiliano Marzo, Economics of Financial Markets, MsC Quantitative Finance, University of Bologna, 2021.

[2] John Y. Cambell, Financial Decision and Markets, Princeton University Press.

[3] H. Markowitz, Porfolio Selection, Journal of Finance, 1952.

[4] H. Markowitz, As I Still See It, Annual Reviw of Financial Economics 2010, Vol.2 pp. 1-23.

[5] Micheal J. Best and Robert R Graur, Positively Weighted Minimum-Variance Portfolios and the Structure of Asset Expected Returns, Journal of FInancial and Quantitative Analysis Dec. 1992, Vol. 27, No 4, pp. 513-537.

[6] T. Bodnar, S. Mazur, Y. Okhirin, Bayesian estimation of the global minimum portfolio, European Journal of Operational Research, 2017.

[7] Richard C. Green, Positively Weighted Portfolios on the Minimum-Variance Frontiere, The Journal of Finance Dec. 1986, pp. 1051-1068.

[8] D. Avramov, Bayesian Portfolio Analysis, Annual Reviw of Financial Economics 2010, Vol.2 pp. 25-47.