# Libraries
libs <- c("tidyverse", "dplyr", "knitr", "ggplot2", "tinytex", "nlme", "quantmod", "lubridate", "rugarch", "forecast")
for (i in libs) {
 library(package=i, character.only=TRUE)
}
## Warning: package 'ggplot2' was built under R version 4.4.3
## Warning: package 'tibble' was built under R version 4.4.3
## Warning: package 'purrr' was built under R version 4.4.3
## Warning: package 'stringr' was built under R version 4.4.3
## Warning: package 'lubridate' was built under R version 4.4.3
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.2
## ✔ ggplot2   4.0.0     ✔ tibble    3.3.0
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.1.0     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
## Warning: package 'knitr' was built under R version 4.4.3
## Warning: package 'tinytex' was built under R version 4.4.3
## Warning: package 'nlme' was built under R version 4.4.3
## 
## Attaching package: 'nlme'
## 
## The following object is masked from 'package:dplyr':
## 
##     collapse
## Warning: package 'quantmod' was built under R version 4.4.3
## Loading required package: xts
## Warning: package 'xts' was built under R version 4.4.2
## Loading required package: zoo
## Warning: package 'zoo' was built under R version 4.4.3
## 
## Attaching package: 'zoo'
## 
## The following objects are masked from 'package:base':
## 
##     as.Date, as.Date.numeric
## 
## 
## ######################### Warning from 'xts' package ##########################
## #                                                                             #
## # The dplyr lag() function breaks how base R's lag() function is supposed to  #
## # work, which breaks lag(my_xts). Calls to lag(my_xts) that you type or       #
## # source() into this session won't work correctly.                            #
## #                                                                             #
## # Use stats::lag() to make sure you're not using dplyr::lag(), or you can add #
## # conflictRules('dplyr', exclude = 'lag') to your .Rprofile to stop           #
## # dplyr from breaking base R's lag() function.                                #
## #                                                                             #
## # Code in packages is not affected. It's protected by R's namespace mechanism #
## # Set `options(xts.warn_dplyr_breaks_lag = FALSE)` to suppress this warning.  #
## #                                                                             #
## ###############################################################################
## 
## Attaching package: 'xts'
## 
## The following objects are masked from 'package:dplyr':
## 
##     first, last
## 
## Loading required package: TTR
## Warning: package 'TTR' was built under R version 4.4.2
## Registered S3 method overwritten by 'quantmod':
##   method            from
##   as.zoo.data.frame zoo
## Warning: package 'rugarch' was built under R version 4.4.3
## Loading required package: parallel
## 
## Attaching package: 'rugarch'
## 
## The following object is masked from 'package:purrr':
## 
##     reduce
## Warning: package 'forecast' was built under R version 4.4.3
## 
## Attaching package: 'forecast'
## 
## The following object is masked from 'package:nlme':
## 
##     getResponse
# Fetch data
df <- NULL

# Define date range
end <- Sys.time()
start <- end - days(365*5)

# Define tickers
tickers <- c("VAR.OL", "OSEBX.OL", "CL=F")

# Fetch stock data
for(ticker in tickers) {
  df <- cbind(df, getSymbols(ticker, 
                             src = "yahoo",
                             from = start,
                             end = end, 
                             periodicity = 'daily', 
                             auto.assign = FALSE)[,6])
}
## Warning: OSEBX.OL 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.
# We use log returns
returns <- (log(df / lag(df))) %>% na.omit() %>% as.data.frame()
colnames(returns) <- c("VAR", "OSEBX", "Oil")
attach(returns)

# Compute covariance matrix
factor_returns <- returns[c("OSEBX", "Oil")]
factor_returns_mtx <- factor_returns |> as.matrix()
factor_cov <- cov(factor_returns)
VAR.fit <- lm(VAR ~ OSEBX + Oil)

summary(VAR.fit)
## 
## Call:
## lm(formula = VAR ~ OSEBX + Oil)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.082700 -0.009833  0.000127  0.009593  0.110900 
## 
## Coefficients:
##              Estimate Std. Error t value Pr(>|t|)    
## (Intercept) 0.0002490  0.0005909   0.421    0.674    
## OSEBX       1.1165684  0.0680994  16.396   <2e-16 ***
## Oil         0.2739399  0.0266712  10.271   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01715 on 841 degrees of freedom
## Multiple R-squared:  0.3969, Adjusted R-squared:  0.3955 
## F-statistic: 276.8 on 2 and 841 DF,  p-value: < 2.2e-16
# Perform an eigen rotation

# Covariance matrix
Sigma_X <- cov(factor_returns_mtx)

# Eigen rotation
eig <- eigen(Sigma_X)

# Rotation matrix (orthonormal)
Q <- eig$vectors

# Rotated orthogonal components
Z <- factor_returns_mtx %*% Q

OSEBX <- Z[, 1]
Oil   <- Z[, 2]

cov(Z)  
##               [,1]          [,2]
## [1,]  5.665794e-04 -1.005921e-20
## [2,] -1.005921e-20  7.373646e-05
VAR.fit <- lm(VAR ~ OSEBX + Oil)

summary(VAR.fit)
## 
## Call:
## lm(formula = VAR ~ OSEBX + Oil)
## 
## Residuals:
##       Min        1Q    Median        3Q       Max 
## -0.082700 -0.009833  0.000127  0.009593  0.110900 
## 
## Coefficients:
##               Estimate Std. Error t value Pr(>|t|)    
## (Intercept)  0.0002490  0.0005909   0.421    0.674    
## OSEBX        0.4407117  0.0248185  17.757   <2e-16 ***
## Oil         -1.0618574  0.0687962 -15.435   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 0.01715 on 841 degrees of freedom
## Multiple R-squared:  0.3969, Adjusted R-squared:  0.3955 
## F-statistic: 276.8 on 2 and 841 DF,  p-value: < 2.2e-16
oil_variance_prop <- (var(Oil) * VAR.fit$coefficients[3]**2) / var(VAR)
OSEBX_variance_prop <- (var(OSEBX) * VAR.fit$coefficients[2]**2) / var(VAR)
idio_variance_prop <- (var(VAR.fit$residuals)) / var(VAR)

idio_variance_prop
## [1] 0.6030581
oil_variance_prop
##       Oil 
## 0.1708309
OSEBX_variance_prop
##    OSEBX 
## 0.226111