Index Correlation

rm(list = ls())
# install.packages("corrplot")

library(quantmod)
## Loading required package: xts
## Warning: package 'xts' was built under R version 4.0.2
## Loading required package: zoo
## 
## Attaching package: 'zoo'
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## Loading required package: TTR
## Warning: package 'TTR' was built under R version 4.0.2
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
library(corrplot)
## corrplot 0.92 loaded
# Define the symbols

# S&P 500: "^GSPC"
# Dow Jones Industrial Average: "^DJI"
# Nasdaq Composite: "^IXIC"

# E-mini S&P 500 Futures: "ES=F"
# E-mini Dow Jones Futures: "YM=F"
# E-mini Nasdaq Futures: "NQ=F"
# E-mini Russell 2000 Futures: "RTY=F"

# Gold Futures: "GC=F"
# Silver Futures: "SI=F"
# Crude Oil  Futures(WTI): "CL=F"
# Natural Gas Futures: "NG=F"
# Copper Futures: "HG=F"
# Corn Futures: "ZC=F"

symbols <- c("^GSPC", "^DJI", "^IXIC", 
             "ES=F", "YM=F", "NQ=F", "RTY=F",
             "GC=F", "SI=F", "CL=F", "NG=F", "HG=F", "ZC=F")

# Set the start and end dates
start_date <- as.Date("2018-01-01")
end_date <- Sys.Date()

# Fetch the data
getSymbols(symbols, from = start_date, to = end_date)
## Warning: ES=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: YM=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: NQ=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: RTY=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: GC=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: SI=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: CL=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: NG=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: HG=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## Warning: ZC=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
##  [1] "GSPC"  "DJI"   "IXIC"  "ES=F"  "YM=F"  "NQ=F"  "RTY=F" "GC=F"  "SI=F" 
## [10] "CL=F"  "NG=F"  "HG=F"  "ZC=F"
symbols <- c("GSPC", "DJI", "IXIC", 
             "ES=F", "YM=F", "NQ=F", "RTY=F",
             "GC=F", "SI=F", "CL=F", "NG=F", "HG=F", "ZC=F")

# Compute daily returns
daily_returns <- do.call(merge, lapply(symbols, function(sym) dailyReturn(Ad(get(sym)))))
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
colnames(daily_returns) <- symbols 

# Compute weekly returns
weekly_returns <- do.call(merge, lapply(symbols, function(sym) weeklyReturn(Ad(get(sym)))))
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
colnames(weekly_returns) <- symbols 

# Compute monthly returns
monthly_returns <- do.call(merge, lapply(symbols, function(sym) monthlyReturn(Ad(get(sym)))))
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data

## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
colnames(monthly_returns) <- symbols 

# Check The Data Structure
str(daily_returns)
## An 'xts' object on 2018-01-02/2023-07-03 containing:
##   Data: num [1:1387, 1:13] 0 0.0064 0.00403 0.00703 0.00166 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:13] "GSPC" "DJI" "IXIC" "ES=F" ...
##   Indexed by objects of class: [Date] TZ: UTC
##   xts Attributes:  
##  NULL
str(weekly_returns)
## An 'xts' object on 2018-01-05/2023-07-03 containing:
##   Data: num [1:289, 1:13] 0.01756 0.01571 0.00864 0.02226 -0.03855 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:13] "GSPC" "DJI" "IXIC" "ES=F" ...
##   Indexed by objects of class: [Date] TZ: UTC
##   xts Attributes:  
##  NULL
str(monthly_returns)
## An 'xts' object on 2018-01-31/2023-07-03 containing:
##   Data: num [1:68, 1:13] 0.04748 -0.03895 -0.02688 0.00272 0.02161 ...
##  - attr(*, "dimnames")=List of 2
##   ..$ : NULL
##   ..$ : chr [1:13] "GSPC" "DJI" "IXIC" "ES=F" ...
##   Indexed by objects of class: [Date] TZ: UTC
##   xts Attributes:  
##  NULL
# Compute correlations
correlation_daily <- cor(daily_returns, use = "complete.obs")
correlation_weekly <- cor(weekly_returns, use = "complete.obs")
correlation_monthly <- cor(monthly_returns, use = "complete.obs")

# Plot the correlations
corrplot(correlation_daily, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Daily Returns Correlation")

corrplot(correlation_weekly, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Weekly Returns Correlation")

corrplot(correlation_monthly, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Monthly Returns Correlation")

Upside vs Downside Index Correlation

daily_returns_up <- daily_returns[daily_returns$GSPC >= 0]
daily_returns_down <- daily_returns[daily_returns$GSPC < 0]

weekly_returns_up <- weekly_returns[weekly_returns$GSPC >= 0]
weekly_returns_down <- weekly_returns[weekly_returns$GSPC < 0]

monthly_returns_up <- monthly_returns[monthly_returns$GSPC >= 0]
monthly_returns_down <- monthly_returns[monthly_returns$GSPC < 0]

# Compute market upside correlations
correlation_daily_up <- cor(daily_returns_up, use = "complete.obs")
correlation_weekly_up <- cor(weekly_returns_up, use = "complete.obs")
correlation_monthly_up <- cor(monthly_returns_up, use = "complete.obs")

# Plot the upside correlations
corrplot(correlation_daily_up, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Daily Returns Correlation")

corrplot(correlation_weekly_up, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Weekly Returns Correlation")

corrplot(correlation_monthly_up, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Monthly Returns Correlation")

# Compute market downside correlations
correlation_daily_down <- cor(daily_returns_down, use = "complete.obs")
correlation_weekly_down <- cor(weekly_returns_down, use = "complete.obs")
correlation_monthly_down <- cor(monthly_returns_down, use = "complete.obs")

# Plot the downside correlations
corrplot(correlation_daily_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Daily Returns Correlation")

corrplot(correlation_weekly_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Weekly Returns Correlation")

corrplot(correlation_monthly_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Monthly Returns Correlation")

Extreme Downside Index Correlation

# Extreme Downside Market Data
daily_returns_down <- daily_returns[daily_returns$GSPC < 0]
weekly_returns_down <- weekly_returns[weekly_returns$GSPC < -0.01]
monthly_returns_down <- monthly_returns[monthly_returns$GSPC < -0.05]

# Compute market extreme downside correlations
correlation_daily_down <- cor(daily_returns_down, use = "complete.obs")
correlation_weekly_down <- cor(weekly_returns_down, use = "complete.obs")
correlation_monthly_down <- cor(monthly_returns_down, use = "complete.obs")

# Plot the downside correlations
corrplot(correlation_daily_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Daily Returns Correlation")

corrplot(correlation_weekly_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Weekly Returns Correlation")

corrplot(correlation_monthly_down, method = "color", type = "lower", tl.cex = 0.8, tl.col = "black", addCoef.col = "black", diag = FALSE, title = "Monthly Returns Correlation")

S&P500 with Gold Futures Portfolio

library(quantmod)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:xts':
## 
##     first, last
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union
library(ggplot2)

# Set the start and end dates
start_date <- as.Date("2018-01-01")
end_date <- Sys.Date()

# Fetch the S&P 500 index data
getSymbols("^GSPC", from = start_date, to = end_date)
## [1] "GSPC"
# Fetch the Gold Futures data
getSymbols("GC=F", from = start_date, to = end_date)
## Warning: GC=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## [1] "GC=F"
# Compute monthly returns for S&P 500
sp500_returns <- monthlyReturn(Ad(GSPC))

# Compute monthly returns for Gold Futures
gold_returns <- monthlyReturn(Ad(`GC=F`))
## Warning in to_period(xx, period = on.opts[[period]], ...): missing values
## removed from data
# Create a data frame to store the weight combinations and results
weight_table <- expand.grid(SnP500_Weight = seq(0, 1, by = 0.05))

# Compute Gold weights based on SnP500 weights
weight_table$Gold_Weight <- 1 - weight_table$SnP500_Weight

# Function to compute portfolio returns and standard deviations
compute_portfolio_stats <- function(SnP500_Weight, Gold_Weight) {
  portfolio_returns <- SnP500_Weight * sp500_returns + Gold_Weight * gold_returns
  expected_return <- mean(portfolio_returns)
  standard_deviation <- sd(portfolio_returns)
  return(c(Expected_Return = expected_return, Standard_Deviation = standard_deviation))
}

# Apply the function to each row of the weight table
portfolio_stats <- t(apply(weight_table, 1, function(x) compute_portfolio_stats(x[1], x[2])))

# Combine the weight table and portfolio statistics into a final table
final_table <- cbind(weight_table, portfolio_stats)

# Print the final table
print(final_table)
##    SnP500_Weight Gold_Weight Expected_Return Standard_Deviation
## 1           0.00        1.00     0.006908562         0.03805550
## 2           0.05        0.95     0.006969765         0.03676455
## 3           0.10        0.90     0.007030968         0.03566559
## 4           0.15        0.85     0.007092171         0.03477683
## 5           0.20        0.80     0.007153374         0.03411471
## 6           0.25        0.75     0.007214576         0.03369259
## 7           0.30        0.70     0.007275779         0.03351954
## 8           0.35        0.65     0.007336982         0.03359941
## 9           0.40        0.60     0.007398185         0.03393040
## 10          0.45        0.55     0.007459388         0.03450530
## 11          0.50        0.50     0.007520591         0.03531219
## 12          0.55        0.45     0.007581793         0.03633563
## 13          0.60        0.40     0.007642996         0.03755791
## 14          0.65        0.35     0.007704199         0.03896032
## 15          0.70        0.30     0.007765402         0.04052417
## 16          0.75        0.25     0.007826605         0.04223152
## 17          0.80        0.20     0.007887808         0.04406570
## 18          0.85        0.15     0.007949011         0.04601155
## 19          0.90        0.10     0.008010213         0.04805550
## 20          0.95        0.05     0.008071416         0.05018556
## 21          1.00        0.00     0.008132619         0.05239124
plot(final_table[,4:3], 
     xlab = "Portfolio SD", 
     ylab = "Portfolio Expected Returns",
     main = "S&P500 + Gold Futures Portfolio")

# Combine the returns into a data frame
port100 = cumprod(1 + sp500_returns) * 10000
port9010 = cumprod(1 + 0.9 * sp500_returns + 0.1 * gold_returns) * 10000
port8020 = cumprod(1 + 0.8 * sp500_returns + 0.2 * gold_returns) * 10000
port7030 = cumprod(1 + 0.7 * sp500_returns + 0.3 * gold_returns) * 10000
port6040 = cumprod(1 + 0.6 * sp500_returns + 0.4 * gold_returns) * 10000
port5050 = cumprod(1 + 0.5 * sp500_returns + 0.5 * gold_returns) * 10000
ret <- merge(port100, port9010, port8020, port7030, port6040, port5050)
returns <- data.frame(Date = index(sp500_returns), ret)
returns <- returns[1:65,]
colnames(returns) <- c("Date","port100","port9010", "port8020",
                       "port7030", "port6040", "port5050")

# Plot cumulative returns
ggplot(returns, aes(x = Date)) +
  geom_line(aes(y = port100, color = "port100")) +
  geom_line(aes(y = port9010, color = "port9010")) +
  geom_line(aes(y = port8020, color = "port8020")) +
  geom_line(aes(y = port7030, color = "port7030")) +
  geom_line(aes(y = port6040, color = "port6040")) +
  geom_line(aes(y = port5050, color = "port5050")) +
  labs(title = "Cumulative Returns of Portfolios") +
  scale_color_manual(values = c("port100" = "blue", 
                                "port9010" = "red", 
                                "port8020" = "green", 
                                "port7030" = "purple", 
                                "port6040" = "orange",
                                "port5050" = "black")) +
  theme_minimal()

Stock Portfolio Hedge

# Set the start and end dates for the cumulative return calculation
start_date <- as.Date("2020-01-01")
end_date <- Sys.Date()

# Fetch the price data for AAPL, TSLA, WMT, and SBUX from 2020 to present
symbols <- c("AAPL", "TSLA", "WMT", "SBUX")

getSymbols(symbols, from = start_date, to = end_date)
## [1] "AAPL" "TSLA" "WMT"  "SBUX"
# Calculate the monthly returns for the selected period
returns_2020 <- data.frame(AAPL = monthlyReturn(Ad(AAPL)),
                           TSLA = monthlyReturn(Ad(TSLA)),
                           WMT = monthlyReturn(Ad(WMT)),
                           SBUX = monthlyReturn(Ad(SBUX)))

returns_2020 <- as.matrix(returns_2020, 
                          nrow(returns_2020), 
                          ncol(returns_2020))

optimal_weights <- as.matrix(c(0.25,0.25,0.25,0.25),4,1)

# Compute the portfolio returns using the optimal weights
portfolio_returns <- returns_2020 %*% optimal_weights

# Calculate the cumulative returns
cumulative_returns <- cumprod(1 + portfolio_returns) * 10000

# Create a data frame with the cumulative returns
portfolio_cumulative <- data.frame(Date = index(portfolio_returns), Cumulative_Return = cumulative_returns)

# Plot the cumulative returns
ggplot(portfolio_cumulative, aes(x = Date, y = Cumulative_Return)) +
  geom_line(color = "blue") +
  ylab("Portfolio Value (USD)") +
  xlab("Date") +
  ggtitle("Cumulative Portfolio Returns") +
  theme_minimal()

Optimal Hedge Ratio

### First we will calculate the optimal hedge ratio for AAPL stock using the S&P500 Futures Index 

# Load required libraries
library(quantmod)

# Set the start and end dates for historical data
start_date <- as.Date("2020-01-01")  # Replace with the desired start date
end_date <- as.Date("2021-12-31")  # Replace with the desired end date

# Define the symbol for the asset to be hedged and the hedging instrument
asset_symbol <- "AAPL"  # Replace with the symbol of the asset to be hedged
hedging_symbol <- "ES=F"  # Replace with the symbol of the hedging instrument

# Fetch the historical price data for the asset and hedging instrument
getSymbols(c(asset_symbol, hedging_symbol), from = start_date, to = end_date)
## [1] "AAPL" "ES=F"
# Extract the adjusted close prices for the asset and hedging instrument
asset_prices <- Ad(get(asset_symbol))
hedging_prices <- Ad(get(hedging_symbol))

# Combine the prices into a data frame
data <- merge(Asset = asset_prices, Hedging = hedging_prices)
data <- na.omit(data)
data <- diff(data)
data <- na.omit(data)
colnames(data) <- c("AAPL", "SPF")
cor(data$AAPL, data$SPF)
##            SPF
## AAPL 0.6746872
# Perform linear regression (OLS) to determine the hedge ratio
model <- lm(AAPL ~ SPF, data = data)
optimal_hedge_ratio <- coef(model)["SPF"] ### THIS IS THE OPTIMAL HEDGE RATIO!

# Print the optimal hedge ratio
cat("Optimal Hedge Ratio:", optimal_hedge_ratio, "\n")
## Optimal Hedge Ratio: 0.03285104
### Then we will evaluate the hedging performance from January 1, 2022 - June 30, 2023

# Set the start and end dates for historical data
start_date <- as.Date("2022-01-01")  # Replace with the desired start date
end_date <- as.Date("2023-06-30")  # Replace with the desired end date

# Define the symbol for the asset to be hedged and the hedging instrument
asset_symbol <- "AAPL"  # Replace with the symbol of the asset to be hedged
hedging_symbol <- "ES=F"  # Replace with the symbol of the hedging instrument

# Fetch the historical price data for the asset and hedging instrument
getSymbols(c(asset_symbol, hedging_symbol), from = start_date, to = end_date)
## Warning: ES=F contains missing values. Some functions will not work if objects
## contain missing values in the middle of the series. Consider using na.omit(),
## na.approx(), na.fill(), etc to remove or replace them.
## [1] "AAPL" "ES=F"
# Extract the adjusted close prices for the asset and hedging instrument
asset_prices <- Ad(get(asset_symbol))
hedging_prices <- Ad(get(hedging_symbol))

# Combine the prices into a data frame
data <- merge(Asset = asset_prices, Hedging = hedging_prices)
data <- na.omit(data)
data <- diff(data)
data <- na.omit(data)
colnames(data) <- c("AAPL", "SPF")

# Calculate the hedged values
unhedged_values <- data$AAPL
hedged_values <- data$AAPL - optimal_hedge_ratio * data$SPF ## TO HEDGE THE APPL STOCK, YOU **SHORT** THE S&P 500 INDEX FUTURES = 0.03994316 PER ONE APPL STOCK

# Unhedged vs Hedge Stats
psych::describe(unhedged_values)
##    vars   n mean   sd median trimmed mad   min   max range skew kurtosis   se
## X1    1 373 0.02 3.03   0.02    0.03 2.8 -9.55 11.97 21.51 0.14     1.15 0.16
psych::describe(hedged_values)
##    vars   n mean   sd median trimmed  mad   min  max range skew kurtosis   se
## X1    1 373 0.06 1.72   0.01    0.04 1.46 -5.78 7.88 13.65 0.34     2.35 0.09
# Calculate the post-hedged performance
pre_hedged_performance <- cumsum(unhedged_values)
post_hedged_performance <- cumsum(hedged_values)

# Plot the pre vs post-hedged performance
par(mfrow = c(2,1))
plot(pre_hedged_performance)
plot(post_hedged_performance)