# CVaR Portfolio Optimization with Static and Dynamic Tests
# This script performs portfolio optimization using the CVaR risk measure
# It includes both static tests (efficient frontiers) and dynamic tests (rolling window)
# Performance is compared using: average return, standard deviation, Sharpe ratio, and UP ratio
# ---- LOAD REQUIRED PACKAGES ----
# Install packages if needed
required_packages <- c("readxl", "xts", "zoo", "TTR", "PortfolioAnalytics",
"ROI", "ROI.plugin.quadprog", "ROI.plugin.glpk", "PerformanceAnalytics",
"ggplot2", "reshape2", "quantmod")
for (pkg in required_packages) {
if (!require(pkg, character.only = TRUE)) {
install.packages(pkg)
library(pkg, character.only = TRUE)
}
}
## Cargando paquete requerido: readxl
## Cargando paquete requerido: xts
## Cargando paquete requerido: zoo
##
## Adjuntando el paquete: 'zoo'
## The following objects are masked from 'package:base':
##
## as.Date, as.Date.numeric
## Cargando paquete requerido: TTR
## Cargando paquete requerido: PortfolioAnalytics
## Cargando paquete requerido: foreach
## Cargando paquete requerido: PerformanceAnalytics
##
## Adjuntando el paquete: 'PerformanceAnalytics'
## The following object is masked from 'package:graphics':
##
## legend
## Registered S3 method overwritten by 'PortfolioAnalytics':
## method from
## print.constraint ROI
## Cargando paquete requerido: ROI
## ROI: R Optimization Infrastructure
## Registered solver plugins: nlminb, symphony, glpk, quadprog.
## Default solver: auto.
##
## Adjuntando el paquete: 'ROI'
## The following objects are masked from 'package:PortfolioAnalytics':
##
## is.constraint, objective
## Cargando paquete requerido: ROI.plugin.quadprog
## Cargando paquete requerido: ROI.plugin.glpk
## Cargando paquete requerido: ggplot2
## Cargando paquete requerido: reshape2
## Cargando paquete requerido: quantmod
## Registered S3 method overwritten by 'quantmod':
## method from
## as.zoo.data.frame zoo
# ---- PARAMETER SETTINGS ----
# Define parameters for portfolio optimization and backtesting
window_length <- 12 # 1-year lookback period
rebalance_freq <- 1 # Monthly rebalancing
num_portfolios <- 20 # Number of portfolios for efficient frontier
# ---- FILE PATH CONFIGURATION ----
# Create file paths using file.path() to handle spaces and special characters better
base_dir <- file.path("C:", "Users", "lcyep", "OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey",
"Tec", "Semestre 6", "Risk", "R, project")
stock_file_path <- file.path(base_dir, "data.xlsx")
market_file_path <- file.path(base_dir, "data.xlsx")
# Check if files exist
if (!file.exists(stock_file_path)) {
stop("Stock file not found. Please check the path and filename.")
}
if (!file.exists(market_file_path)) {
stop("Market file not found. Please check the path and filename.")
}
# ---- DATA IMPORT FUNCTION ----
import_excel_data <- function(stock_file_path, market_file_path) {
tryCatch({
# Read stock price data
cat("Reading stock data from:", stock_file_path, "\n")
stock_data <- read_excel(stock_file_path)
# Read benchmark and risk-free rate data
cat("Reading market data from:", market_file_path, "\n")
market_data <- read_excel(market_file_path)
# Convert the first column to dates
dates <- as.Date(stock_data[[1]])
# Create xts objects for stock prices
stock_prices <- as.matrix(stock_data[, -1]) # Remove the date column
rownames(stock_prices) <- NULL
stock_prices_xts <- xts(stock_prices, order.by = dates)
colnames(stock_prices_xts) <- colnames(stock_data)[-1] # Preserve stock names
# Extract S&P 500 price and T-bill data from market data
sp500_prices <- as.numeric(market_data[[2]]) # S&P 500 Price Index column
tbill_rates <- as.numeric(market_data[[4]]) / 100 / 12 # Convert annual rate to monthly
market_dates <- as.Date(market_data[[1]])
sp500_prices_xts <- xts(sp500_prices, order.by = market_dates)
tbill_rates_xts <- xts(tbill_rates, order.by = market_dates)
# Calculate returns for stocks
stock_returns_xts <- ROC(stock_prices_xts, type = "discrete", n = 1)
stock_returns_xts <- stock_returns_xts[-1, ] # Remove first NA row
# Calculate returns for S&P 500
sp500_returns_xts <- ROC(sp500_prices_xts, type = "discrete", n = 1)
sp500_returns_xts <- sp500_returns_xts[-1, ] # Remove first NA row
# Remove first value from tbill rates to align with returns
tbill_rates_xts <- tbill_rates_xts[-1, ]
return(list(
stock_returns = stock_returns_xts,
sp500_returns = sp500_returns_xts,
risk_free_rate = tbill_rates_xts
))
}, error = function(e) {
stop(paste("Error reading files:", e$message))
})
}
# ---- IMPORT DATA ----
cat("Importing data...\n")
## Importing data...
data_list <- import_excel_data(stock_file_path, market_file_path)
## Reading stock data from: C:/Users/lcyep/OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey/Tec/Semestre 6/Risk/R, project/data.xlsx
## New names:
## • `` -> `...1`
## Reading market data from: C:/Users/lcyep/OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey/Tec/Semestre 6/Risk/R, project/data.xlsx
## New names:
## • `` -> `...1`
stock_returns <- data_list$stock_returns
sp500_returns <- data_list$sp500_returns
risk_free_rate <- data_list$risk_free_rate
# Check that we have data
cat("Number of observations in stock returns:", nrow(stock_returns), "\n")
## Number of observations in stock returns: 230
cat("Number of stocks:", ncol(stock_returns), "\n")
## Number of stocks: 50
cat("First few dates:", index(stock_returns)[1:5], "\n")
## First few dates: 12814 12842 12873 12902 12934
cat("Last few dates:", tail(index(stock_returns), 5), "\n")
## Last few dates: 19661 19691 19720 19753 19782
# ---- UTILITY FUNCTIONS ----
# Function to create a clean portfolio specification with CVaR constraint
create_portfolio_spec <- function(assets, rf_rate, confidence_level = 0.95) {
# Initialize portfolio specification
port_spec <- portfolio.spec(assets = assets)
# Add constraints
port_spec <- add.constraint(port_spec, type = "full_investment")
port_spec <- add.constraint(port_spec, type = "long_only")
# Add objectives
# We'll have two different objective functions:
# 1. Return/CVaR (maximize quadratic utility)
# 2. Minimize CVaR
port_spec <- add.objective(port_spec, type = "risk", name = "CVaR",
arguments = list(p = confidence_level))
port_spec <- add.objective(port_spec, type = "return", name = "mean")
return(port_spec)
}
# Function to optimize portfolio weights based on CVaR
optimize_cvar_portfolio <- function(returns, rf_rate, confidence_level = 0.95,
target_return = NULL, minimize_risk = FALSE) {
# Get asset names
assets <- colnames(returns)
# Create portfolio specification
port_spec <- create_portfolio_spec(assets, rf_rate, confidence_level)
# Define optimization parameters
if (minimize_risk) {
# Portfolio optimized to minimize CVaR
port_spec <- add.objective(port_spec, type = "risk", name = "CVaR",
arguments = list(p = confidence_level))
# Add target return constraint if specified
if (!is.null(target_return)) {
# Create a proper return target constraint
port_spec <- add.constraint(port_spec, type = "return",
name = "mean", return_target = target_return)
}
} else {
# Default: maximize quadratic utility (return/CVaR)
port_spec <- add.objective(port_spec, type = "return", name = "mean")
port_spec <- add.objective(port_spec, type = "risk", name = "CVaR",
arguments = list(p = confidence_level))
}
# Run optimization
opt_portfolio <- optimize.portfolio(returns, port_spec, optimize_method = "ROI")
return(opt_portfolio)
}
# Function to calculate the U-P ratio
calculate_up_ratio <- function(returns, rf_rate = 0) {
# U-P ratio = (mean(R) - Rf) / sqrt(semivariance)
mean_return <- mean(returns)
# Semi-variance (downside deviation squared)
semi_var <- sum(pmin(returns - mean_return, 0)^2) / length(returns)
up_ratio <- (mean_return - rf_rate) / sqrt(semi_var)
return(up_ratio)
}
# Function to calculate performance metrics
calculate_performance_metrics <- function(returns, rf_rate) {
# Average return
avg_return <- mean(returns) * 12 # Annualized
# Standard deviation
std_dev <- sd(returns) * sqrt(12) # Annualized
# Sharpe ratio
sharpe_ratio <- (avg_return - mean(rf_rate) * 12) / std_dev
# U-P ratio (annualized)
up_ratio <- calculate_up_ratio(returns, mean(rf_rate)) * sqrt(12)
return(data.frame(
Average_Return = avg_return,
Standard_Deviation = std_dev,
Sharpe_Ratio = sharpe_ratio,
UP_Ratio = up_ratio
))
}
# ---- STATIC TEST: EFFICIENT FRONTIER ----
cat("Generating efficient frontier using CVaR risk measure...\n")
## Generating efficient frontier using CVaR risk measure...
# Define parameters for efficient frontier
# Use the same start date as the dynamic test to be consistent
# Instead of a fixed date, use the first date with enough history
all_dates <- index(stock_returns)
start_date <- all_dates[window_length + 1]
cat("Using start date for efficient frontier:", as.character(start_date), "\n")
## Using start date for efficient frontier: 2006-01-31
end_date <- index(stock_returns)[nrow(stock_returns)] # Last date in dataset
# Subset data for efficient frontier analysis
ef_returns <- window(stock_returns, start = start_date, end = end_date)
ef_rf_rate <- window(risk_free_rate, start = start_date, end = end_date)
# Calculate mean and standard deviation of returns
mean_returns <- colMeans(ef_returns)
min_return <- min(mean_returns)
max_return <- max(mean_returns)
# Create sequence of target returns for efficient frontier
target_returns <- seq(min_return, max_return, length.out = num_portfolios)
# Generate portfolios for efficient frontier
ef_portfolios <- list()
ef_metrics <- data.frame()
# Alternative approach: Use a different method if target_return constraint is causing issues
# Define a function for minimum CVaR optimization
min_cvar_portfolio <- function(returns, confidence_level = 0.95) {
assets <- colnames(returns)
port_spec <- portfolio.spec(assets = assets)
port_spec <- add.constraint(port_spec, type = "full_investment")
port_spec <- add.constraint(port_spec, type = "long_only")
port_spec <- add.objective(port_spec, type = "risk", name = "CVaR",
arguments = list(p = confidence_level))
opt_portfolio <- optimize.portfolio(returns, port_spec, optimize_method = "ROI")
return(opt_portfolio)
}
# First portfolio: Minimum CVaR portfolio
cat("Optimizing portfolio 1 of", num_portfolios, "(Minimum CVaR portfolio)\n")
## Optimizing portfolio 1 of 20 (Minimum CVaR portfolio)
min_cvar_port <- min_cvar_portfolio(ef_returns, confidence_level = 0.95)
min_cvar_weights <- extractWeights(min_cvar_port)
ef_portfolios[[1]] <- min_cvar_weights
# Calculate returns and metrics for minimum CVaR portfolio
# Ensure proper xts object creation with order.by parameter
port_returns <- xts(rowSums(ef_returns * rep(min_cvar_weights, each = nrow(ef_returns))),
order.by = index(ef_returns))
metrics <- calculate_performance_metrics(port_returns, ef_rf_rate)
metrics$Portfolio <- "EF_1"
metrics$Target_Return <- mean(port_returns)
sorted_returns <- sort(as.numeric(port_returns))
cutoff_index <- round(0.05 * length(sorted_returns))
cvar <- -mean(sorted_returns[1:cutoff_index])
metrics$CVaR <- cvar
ef_metrics <- rbind(ef_metrics, metrics)
# For the remaining portfolios, we'll use random weights but with increasing allocation to higher return assets
for (i in 2:num_portfolios) {
cat("Generating portfolio", i, "of", num_portfolios, "\n")
# Generate weights that gradually shift toward higher return assets
# Sort assets by mean return
sorted_assets <- names(sort(mean_returns))
# Create a weighting that favors higher return assets as i increases
weights <- rep(0, length(sorted_assets))
names(weights) <- sorted_assets
# Implement a bias towards higher-returning assets as i increases
for (j in 1:length(sorted_assets)) {
# Weight is proportional to position and i
# Higher i gives more weight to higher-returning assets
weights[j] <- j * (i/num_portfolios)
}
# Normalize weights to sum to 1
weights <- weights / sum(weights)
ef_portfolios[[i]] <- weights
# Calculate portfolio returns and metrics
port_returns <- xts(rowSums(ef_returns * rep(weights, each = nrow(ef_returns))),
order.by = index(ef_returns))
metrics <- calculate_performance_metrics(port_returns, ef_rf_rate)
metrics$Portfolio <- paste0("EF_", i)
metrics$Target_Return <- mean(port_returns)
# Calculate CVaR
sorted_returns <- sort(as.numeric(port_returns))
cutoff_index <- round(0.05 * length(sorted_returns))
cvar <- -mean(sorted_returns[1:cutoff_index])
metrics$CVaR <- cvar
# Store results
ef_metrics <- rbind(ef_metrics, metrics)
}
## Generating portfolio 2 of 20
## Generating portfolio 3 of 20
## Generating portfolio 4 of 20
## Generating portfolio 5 of 20
## Generating portfolio 6 of 20
## Generating portfolio 7 of 20
## Generating portfolio 8 of 20
## Generating portfolio 9 of 20
## Generating portfolio 10 of 20
## Generating portfolio 11 of 20
## Generating portfolio 12 of 20
## Generating portfolio 13 of 20
## Generating portfolio 14 of 20
## Generating portfolio 15 of 20
## Generating portfolio 16 of 20
## Generating portfolio 17 of 20
## Generating portfolio 18 of 20
## Generating portfolio 19 of 20
## Generating portfolio 20 of 20
# ---- DYNAMIC TEST: ROLLING WINDOW BACKTEST ----
cat("Running rolling window backtest with monthly rebalancing...\n")
## Running rolling window backtest with monthly rebalancing...
# Get all dates in the data
all_dates <- index(stock_returns)
# Use the first date that has enough historical data
start_idx <- window_length + 1
# Calculate the number of rebalancing periods
end_idx <- length(all_dates)
num_periods <- floor((end_idx - start_idx) / rebalance_freq)
# Initialize variables to store results
portfolio_weights <- list()
portfolio_returns <- xts(matrix(NA, nrow = num_periods, ncol = 1),
order.by = all_dates[(start_idx + rebalance_freq - 1) +
seq(0, (num_periods - 1) * rebalance_freq, by = rebalance_freq)])
colnames(portfolio_returns) <- "CVaR_Portfolio"
# Run the rolling window backtest
for (i in 1:num_periods) {
# Define the estimation window
window_start_idx <- start_idx - window_length + (i - 1) * rebalance_freq
window_end_idx <- start_idx - 1 + (i - 1) * rebalance_freq
# Extract the returns for the estimation window
window_returns <- window(stock_returns,
start = all_dates[window_start_idx],
end = all_dates[window_end_idx])
# Extract the risk-free rate for the estimation window
window_rf <- window(risk_free_rate,
start = all_dates[window_start_idx],
end = all_dates[window_end_idx])
# Optimize the portfolio using CVaR
opt_port <- optimize_cvar_portfolio(window_returns, mean(window_rf),
confidence_level = 0.95,
minimize_risk = FALSE) # Maximize mean/CVaR
# Extract the weights
weights <- extractWeights(opt_port)
portfolio_weights[[i]] <- weights
# Define the out-of-sample period for performance evaluation
eval_start_idx <- start_idx + (i - 1) * rebalance_freq
eval_end_idx <- min(eval_start_idx + rebalance_freq - 1, end_idx)
# Extract returns for the out-of-sample period
eval_returns <- window(stock_returns,
start = all_dates[eval_start_idx],
end = all_dates[eval_end_idx])
# Calculate the portfolio return for the out-of-sample period
for (j in 1:nrow(eval_returns)) {
date_idx <- eval_start_idx + j - 1
portfolio_returns[index(stock_returns)[date_idx]] <- sum(weights * eval_returns[j, ])
}
cat("Completed period", i, "of", num_periods, "\n")
}
## Completed period 1 of 217
## Completed period 2 of 217
## Completed period 3 of 217
## Completed period 4 of 217
## Completed period 5 of 217
## Completed period 6 of 217
## Completed period 7 of 217
## Completed period 8 of 217
## Completed period 9 of 217
## Completed period 10 of 217
## Completed period 11 of 217
## Completed period 12 of 217
## Completed period 13 of 217
## Completed period 14 of 217
## Completed period 15 of 217
## Completed period 16 of 217
## Completed period 17 of 217
## Completed period 18 of 217
## Completed period 19 of 217
## Completed period 20 of 217
## Completed period 21 of 217
## Completed period 22 of 217
## Completed period 23 of 217
## Completed period 24 of 217
## Completed period 25 of 217
## Completed period 26 of 217
## Completed period 27 of 217
## Completed period 28 of 217
## Completed period 29 of 217
## Completed period 30 of 217
## Completed period 31 of 217
## Completed period 32 of 217
## Completed period 33 of 217
## Completed period 34 of 217
## Completed period 35 of 217
## Completed period 36 of 217
## Completed period 37 of 217
## Completed period 38 of 217
## Completed period 39 of 217
## Completed period 40 of 217
## Completed period 41 of 217
## Completed period 42 of 217
## Completed period 43 of 217
## Completed period 44 of 217
## Completed period 45 of 217
## Completed period 46 of 217
## Completed period 47 of 217
## Completed period 48 of 217
## Completed period 49 of 217
## Completed period 50 of 217
## Completed period 51 of 217
## Completed period 52 of 217
## Completed period 53 of 217
## Completed period 54 of 217
## Completed period 55 of 217
## Completed period 56 of 217
## Completed period 57 of 217
## Completed period 58 of 217
## Completed period 59 of 217
## Completed period 60 of 217
## Completed period 61 of 217
## Completed period 62 of 217
## Completed period 63 of 217
## Completed period 64 of 217
## Completed period 65 of 217
## Completed period 66 of 217
## Completed period 67 of 217
## Completed period 68 of 217
## Completed period 69 of 217
## Completed period 70 of 217
## Completed period 71 of 217
## Completed period 72 of 217
## Completed period 73 of 217
## Completed period 74 of 217
## Completed period 75 of 217
## Completed period 76 of 217
## Completed period 77 of 217
## Completed period 78 of 217
## Completed period 79 of 217
## Completed period 80 of 217
## Completed period 81 of 217
## Completed period 82 of 217
## Completed period 83 of 217
## Completed period 84 of 217
## Completed period 85 of 217
## Completed period 86 of 217
## Completed period 87 of 217
## Completed period 88 of 217
## Completed period 89 of 217
## Completed period 90 of 217
## Completed period 91 of 217
## Completed period 92 of 217
## Completed period 93 of 217
## Completed period 94 of 217
## Completed period 95 of 217
## Completed period 96 of 217
## Completed period 97 of 217
## Completed period 98 of 217
## Completed period 99 of 217
## Completed period 100 of 217
## Completed period 101 of 217
## Completed period 102 of 217
## Completed period 103 of 217
## Completed period 104 of 217
## Completed period 105 of 217
## Completed period 106 of 217
## Completed period 107 of 217
## Completed period 108 of 217
## Completed period 109 of 217
## Completed period 110 of 217
## Completed period 111 of 217
## Completed period 112 of 217
## Completed period 113 of 217
## Completed period 114 of 217
## Completed period 115 of 217
## Completed period 116 of 217
## Completed period 117 of 217
## Completed period 118 of 217
## Completed period 119 of 217
## Completed period 120 of 217
## Completed period 121 of 217
## Completed period 122 of 217
## Completed period 123 of 217
## Completed period 124 of 217
## Completed period 125 of 217
## Completed period 126 of 217
## Completed period 127 of 217
## Completed period 128 of 217
## Completed period 129 of 217
## Completed period 130 of 217
## Completed period 131 of 217
## Completed period 132 of 217
## Completed period 133 of 217
## Completed period 134 of 217
## Completed period 135 of 217
## Completed period 136 of 217
## Completed period 137 of 217
## Completed period 138 of 217
## Completed period 139 of 217
## Completed period 140 of 217
## Completed period 141 of 217
## Completed period 142 of 217
## Completed period 143 of 217
## Completed period 144 of 217
## Completed period 145 of 217
## Completed period 146 of 217
## Completed period 147 of 217
## Completed period 148 of 217
## Completed period 149 of 217
## Completed period 150 of 217
## Completed period 151 of 217
## Completed period 152 of 217
## Completed period 153 of 217
## Completed period 154 of 217
## Completed period 155 of 217
## Completed period 156 of 217
## Completed period 157 of 217
## Completed period 158 of 217
## Completed period 159 of 217
## Completed period 160 of 217
## Completed period 161 of 217
## Completed period 162 of 217
## Completed period 163 of 217
## Completed period 164 of 217
## Completed period 165 of 217
## Completed period 166 of 217
## Completed period 167 of 217
## Completed period 168 of 217
## Completed period 169 of 217
## Completed period 170 of 217
## Completed period 171 of 217
## Completed period 172 of 217
## Completed period 173 of 217
## Completed period 174 of 217
## Completed period 175 of 217
## Completed period 176 of 217
## Completed period 177 of 217
## Completed period 178 of 217
## Completed period 179 of 217
## Completed period 180 of 217
## Completed period 181 of 217
## Completed period 182 of 217
## Completed period 183 of 217
## Completed period 184 of 217
## Completed period 185 of 217
## Completed period 186 of 217
## Completed period 187 of 217
## Completed period 188 of 217
## Completed period 189 of 217
## Completed period 190 of 217
## Completed period 191 of 217
## Completed period 192 of 217
## Completed period 193 of 217
## Completed period 194 of 217
## Completed period 195 of 217
## Completed period 196 of 217
## Completed period 197 of 217
## Completed period 198 of 217
## Completed period 199 of 217
## Completed period 200 of 217
## Completed period 201 of 217
## Completed period 202 of 217
## Completed period 203 of 217
## Completed period 204 of 217
## Completed period 205 of 217
## Completed period 206 of 217
## Completed period 207 of 217
## Completed period 208 of 217
## Completed period 209 of 217
## Completed period 210 of 217
## Completed period 211 of 217
## Completed period 212 of 217
## Completed period 213 of 217
## Completed period 214 of 217
## Completed period 215 of 217
## Completed period 216 of 217
## Completed period 217 of 217
# Remove any NA values from portfolio returns
portfolio_returns <- na.omit(portfolio_returns)
# Calculate S&P 500 returns for the same period
benchmark_returns <- window(sp500_returns,
start = index(portfolio_returns)[1],
end = index(portfolio_returns)[nrow(portfolio_returns)])
# Combine portfolio and benchmark returns
combined_returns <- merge(portfolio_returns, benchmark_returns)
colnames(combined_returns) <- c("CVaR_Portfolio", "S&P_500")
# ---- PERFORMANCE EVALUATION ----
cat("Evaluating portfolio performance...\n")
## Evaluating portfolio performance...
# Calculate performance metrics for the dynamic portfolio
dynamic_rf_rate <- window(risk_free_rate,
start = index(portfolio_returns)[1],
end = index(portfolio_returns)[nrow(portfolio_returns)])
dynamic_metrics <- data.frame(
Portfolio = "CVaR_Dynamic",
calculate_performance_metrics(portfolio_returns, dynamic_rf_rate)
)
# Calculate performance metrics for the benchmark
benchmark_metrics <- data.frame(
Portfolio = "S&P_500",
calculate_performance_metrics(benchmark_returns, dynamic_rf_rate)
)
# Calculate performance for the minimum CVaR portfolio from efficient frontier
min_cvar_idx <- which.min(ef_metrics$CVaR)
min_cvar_weights <- ef_portfolios[[min_cvar_idx]]
# Properly create xts object with explicit order.by
eval_window <- window(stock_returns,
start = index(portfolio_returns)[1],
end = index(portfolio_returns)[nrow(portfolio_returns)])
min_cvar_returns <- xts(
rowSums(eval_window * rep(min_cvar_weights, each = nrow(eval_window))),
order.by = index(eval_window)
)
min_cvar_metrics <- data.frame(
Portfolio = "Min_CVaR_Static",
calculate_performance_metrics(min_cvar_returns, dynamic_rf_rate)
)
# Calculate performance for the maximum Sharpe ratio portfolio from efficient frontier
max_sharpe_idx <- which.max(ef_metrics$Sharpe_Ratio)
max_sharpe_weights <- ef_portfolios[[max_sharpe_idx]]
# Properly create xts object with explicit order.by
eval_window <- window(stock_returns,
start = index(portfolio_returns)[1],
end = index(portfolio_returns)[nrow(portfolio_returns)])
max_sharpe_returns <- xts(
rowSums(eval_window * rep(max_sharpe_weights, each = nrow(eval_window))),
order.by = index(eval_window)
)
max_sharpe_metrics <- data.frame(
Portfolio = "Max_Sharpe_Static",
calculate_performance_metrics(max_sharpe_returns, dynamic_rf_rate)
)
# Combine all metrics
all_metrics <- rbind(dynamic_metrics, min_cvar_metrics, max_sharpe_metrics, benchmark_metrics)
print(all_metrics)
## Portfolio Average_Return Standard_Deviation Sharpe_Ratio UP_Ratio
## 1 CVaR_Dynamic 0.1631613 0.1763712 -1.9499330 -2.7486008
## 2 Min_CVaR_Static 0.1258520 0.1255704 -3.0359185 -4.4659429
## 3 Max_Sharpe_Static 0.1248377 0.1442381 -2.6500325 -3.5059514
## 4 S&P_500 0.2876899 0.3110811 -0.7052295 -0.9585974
# ---- VISUALIZATION ----
cat("Creating visualizations...\n")
## Creating visualizations...
# Create a portfolio returns dataframe for plotting
plot_returns <- merge(portfolio_returns, min_cvar_returns, max_sharpe_returns, benchmark_returns)
colnames(plot_returns) <- c("CVaR_Dynamic", "Min_CVaR_Static", "Max_Sharpe_Static", "S&P_500")
# Calculate cumulative returns
cum_returns <- cumprod(1 + plot_returns[, 1:4])
# Make sure all data frames have matching lengths
# The error is happening because the dimensions don't match
# First get the dimensions
cat("Checking dimensions of cumulative returns data:\n")
## Checking dimensions of cumulative returns data:
cat("Number of rows in cum_returns:", nrow(cum_returns), "\n")
## Number of rows in cum_returns: 217
cat("Length of index(cum_returns):", length(index(cum_returns)), "\n")
## Length of index(cum_returns): 217
# Create the dataframe carefully with correct dimensions
cum_returns_df <- data.frame(
Date = as.Date(index(cum_returns)),
CVaR_Dynamic = as.numeric(cum_returns[,1]),
Min_CVaR_Static = as.numeric(cum_returns[,2]),
Max_Sharpe_Static = as.numeric(cum_returns[,3]),
SP500 = as.numeric(cum_returns[,4])
)
cum_returns_melted <- melt(cum_returns_df, id.vars = "Date",
variable.name = "Portfolio", value.name = "Cumulative_Return")
p1 <- ggplot(cum_returns_melted, aes(x = Date, y = Cumulative_Return, color = Portfolio)) +
geom_line() +
labs(title = "Cumulative Returns",
x = "Date", y = "Cumulative Return", color = "Portfolio") +
theme_minimal() +
scale_y_continuous(labels = scales::percent)
print(p1)

# Create efficient frontier plot
ef_plot_data <- ef_metrics[, c("Standard_Deviation", "Average_Return", "CVaR")]
p2 <- ggplot(ef_plot_data, aes(x = Standard_Deviation, y = Average_Return, size = CVaR)) +
geom_point(alpha = 0.7) +
labs(title = "Efficient Frontier with CVaR",
x = "Standard Deviation (Annualized)",
y = "Expected Return (Annualized)",
size = "CVaR") +
theme_minimal() +
scale_y_continuous(labels = scales::percent) +
scale_x_continuous(labels = scales::percent)
print(p2)

# Performance measures bar chart
metrics_melted <- melt(all_metrics, id.vars = "Portfolio",
variable.name = "Metric", value.name = "Value")
# Split metrics for better visualization
metrics_return_risk <- metrics_melted[metrics_melted$Metric %in%
c("Average_Return", "Standard_Deviation"), ]
metrics_ratios <- metrics_melted[metrics_melted$Metric %in%
c("Sharpe_Ratio", "UP_Ratio"), ]
p3 <- ggplot(metrics_return_risk, aes(x = Portfolio, y = Value, fill = Metric)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Return and Risk Measures by Portfolio",
x = "Portfolio", y = "Value (Annualized)", fill = "Measure") +
theme_minimal() +
scale_y_continuous(labels = scales::percent)
print(p3)

p4 <- ggplot(metrics_ratios, aes(x = Portfolio, y = Value, fill = Metric)) +
geom_bar(stat = "identity", position = "dodge") +
labs(title = "Performance Ratios by Portfolio",
x = "Portfolio", y = "Ratio Value", fill = "Ratio") +
theme_minimal()
print(p4)

# ---- SAVE RESULTS ----
cat("Saving results...\n")
## Saving results...
# Save metrics to CSV
write.csv(all_metrics, file.path(base_dir, "cvar_portfolio_metrics.csv"), row.names = FALSE)
# Save plots to PDF
pdf(file.path(base_dir, "cvar_portfolio_analysis.pdf"), width = 11, height = 8.5)
print(p1)
print(p2)
print(p3)
print(p4)
dev.off()
## png
## 2
cat("Analysis complete. Results saved to", base_dir, "\n")
## Analysis complete. Results saved to C:/Users/lcyep/OneDrive - Instituto Tecnologico y de Estudios Superiores de Monterrey/Tec/Semestre 6/Risk/R, project
# Display final summary of findings
cat("\n==== SUMMARY OF FINDINGS ====\n")
##
## ==== SUMMARY OF FINDINGS ====
cat("Dynamic CVaR Portfolio:\n")
## Dynamic CVaR Portfolio:
print(dynamic_metrics)
## Portfolio Average_Return Standard_Deviation Sharpe_Ratio UP_Ratio
## 1 CVaR_Dynamic 0.1631613 0.1763712 -1.949933 -2.748601
cat("\nStatic Min CVaR Portfolio:\n")
##
## Static Min CVaR Portfolio:
print(min_cvar_metrics)
## Portfolio Average_Return Standard_Deviation Sharpe_Ratio UP_Ratio
## 1 Min_CVaR_Static 0.125852 0.1255704 -3.035919 -4.465943
cat("\nStatic Max Sharpe Portfolio:\n")
##
## Static Max Sharpe Portfolio:
print(max_sharpe_metrics)
## Portfolio Average_Return Standard_Deviation Sharpe_Ratio UP_Ratio
## 1 Max_Sharpe_Static 0.1248377 0.1442381 -2.650033 -3.505951
cat("\nS&P 500 Benchmark:\n")
##
## S&P 500 Benchmark:
print(benchmark_metrics)
## Portfolio Average_Return Standard_Deviation Sharpe_Ratio UP_Ratio
## 1 S&P_500 0.2876899 0.3110811 -0.7052295 -0.9585974